From 7d98284b84178dd11f0cc29c8ffaec84d78a6a4e Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 27 Oct 2023 10:53:00 +0800 Subject: [PATCH 001/113] Extract binary dependencies into external repositories. --- .gitignore | 18 +- Makefile | 911 +++++++++------------------------------ README.rst | 4 + patch/libffi-3.4.4.patch | 174 -------- patch/xz-5.4.4.patch | 30 -- 5 files changed, 207 insertions(+), 930 deletions(-) delete mode 100644 patch/libffi-3.4.4.patch delete mode 100644 patch/xz-5.4.4.patch diff --git a/.gitignore b/.gitignore index 22c9b3e7..f8053935 100644 --- a/.gitignore +++ b/.gitignore @@ -2,18 +2,12 @@ *.swp .envrc .vscode/ -alias/* -archive/* -build/* -diff/* -dist/* -downloads/* -install/* -local/* -merge/* -src/* -support/* -wheels/* +build +dist +downloads +install +local +support *.dist-info __pycache__ tests/testbed/macOS diff --git a/Makefile b/Makefile index add83aaa..d4f27698 100644 --- a/Makefile +++ b/Makefile @@ -5,30 +5,6 @@ # - iOS - build everything for iOS # - tvOS - build everything for tvOS # - watchOS - build everything for watchOS -# - BZip2 - build BZip2 for all platforms -# - BZip2-macOS - build BZip2 for macOS -# - BZip2-iOS - build BZip2 for iOS -# - BZip2-tvOS - build BZip2 for tvOS -# - BZip2-watchOS - build BZip2 for watchOS -# - XZ - build XZ for all platforms -# - XZ-macOS - build XZ for macOS -# - XZ-iOS - build XZ for iOS -# - XZ-tvOS - build XZ for tvOS -# - XZ-watchOS - build XZ for watchOS -# - OpenSSL - build OpenSSL for all platforms -# - OpenSSL-macOS - build OpenSSL for macOS -# - OpenSSL-iOS - build OpenSSL for iOS -# - OpenSSL-tvOS - build OpenSSL for tvOS -# - OpenSSL-watchOS - build OpenSSL for watchOS -# - libFFI - build libFFI for all platforms (except macOS) -# - libFFI-iOS - build libFFI for iOS -# - libFFI-tvOS - build libFFI for tvOS -# - libFFI-watchOS - build libFFI for watchOS -# - Python - build Python for all platforms -# - Python-macOS - build Python for macOS -# - Python-iOS - build Python for iOS -# - Python-tvOS - build Python for tvOS -# - Python-watchOS - build Python for watchOS # Current directory PROJECT_DIR=$(shell pwd) @@ -43,22 +19,17 @@ PYTHON_VERSION=3.13.0a1 PYTHON_MICRO_VERSION=$(shell echo $(PYTHON_VERSION) | grep -Eo "\d+\.\d+\.\d+") PYTHON_VER=$(basename $(PYTHON_VERSION)) -BZIP2_VERSION=1.0.8 - -XZ_VERSION=5.4.4 - -# Preference is to use OpenSSL 3; however, Cryptography 3.4.8 (and -# probably some other packages as well) only works with 1.1.1, so -# we need to preserve the ability to build the older OpenSSL (for now...) -OPENSSL_VERSION=3.1.2 -# OPENSSL_VERSION_NUMBER=1.1.1 -# OPENSSL_REVISION=v -# OPENSSL_VERSION=$(OPENSSL_VERSION_NUMBER)$(OPENSSL_REVISION) - -LIBFFI_VERSION=3.4.4 - -# Supported OS and dependencies -DEPENDENCIES=BZip2 XZ OpenSSL libFFI +# The binary releases of dependencies, published at: +# macOS: +# https://github.com/beeware/cpython-macOS-source-deps/releases +# iOS, tvOS, watchOS: +# https://github.com/beeware/cpython-apple-source-deps/releases +BZIP2_VERSION=1.0.8-1 +XZ_VERSION=5.4.4-1 +OPENSSL_VERSION=3.0.12-1 +LIBFFI_VERSION=3.4.4-1 + +# Supported OS OS_LIST=macOS iOS tvOS watchOS CURL_FLAGS=--disable --fail --location --create-dirs --progress-bar @@ -87,9 +58,7 @@ PYTHON_CONFIGURE-watchOS=ac_cv_func_sigaltstack=no # The architecture of the machine doing the build HOST_ARCH=$(shell uname -m) -HOST_PYTHON=install/macOS/macosx/python-$(PYTHON_VERSION) -HOST_PYTHON_EXE=$(HOST_PYTHON)/bin/python3 -BDIST_WHEEL=$(HOST_PYTHON)/lib/python$(PYTHON_VER)/site-packages/wheel/bdist_wheel.py +HOST_PYTHON=$(shell which python$(PYTHON_VER)) # Force the path to be minimal. This ensures that anything in the user environment # (in particular, homebrew and user-provided Python installs) aren't inadvertently @@ -97,29 +66,22 @@ BDIST_WHEEL=$(HOST_PYTHON)/lib/python$(PYTHON_VER)/site-packages/wheel/bdist_whe PATH=/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin # Build for all operating systems -all: $(OS_LIST) wheels +all: $(OS_LIST) .PHONY: \ - all clean distclean update-patch vars wheels \ - $(foreach product,$(PRODUCTS),$(product) $(foreach os,$(OS_LIST),$(product)-$(os) clean-$(product) clean-$(product)-$(os))) \ - $(foreach product,$(PRODUCTS),$(product)-wheels $(foreach os,$(OS_LIST),$(product)-$(os)-wheels clean-$(product)-$(os)-wheels)) \ - $(foreach os,$(OS_LIST),$(os) $(os)-wheels clean-$(os) clean-$(os)-wheels vars-$(os)) + all clean distclean update-patch vars \ + $(foreach os,$(OS_LIST),$(os) clean-$(os) dev-clean-$(os) vars-$(os)) \ + $(foreach os,$(OS_LIST),$(foreach sdk,$$(sort $$(basename $$(TARGETS-$(os)))),$(sdk) vars-$(sdk))) + $(foreach os,$(OS_LIST),$(foreach target,$$(TARGETS-$(os)),$(target) vars-$(target))) # Clean all builds clean: - rm -rf build install merge dist support wheels + rm -rf build dist install support # Full clean - includes all downloaded products distclean: clean rm -rf downloads -downloads: \ - downloads/bzip2-$(BZIP2_VERSION).tar.gz \ - downloads/xz-$(XZ_VERSION).tar.gz \ - downloads/openssl-$(OPENSSL_VERSION).tar.gz \ - downloads/libffi-$(LIBFFI_VERSION).tar.gz \ - downloads/Python-$(PYTHON_VERSION).tar.gz - update-patch: # Generate a diff from the clone of the python/cpython Github repository, # comparing between the current state of the 3.X branch against the v3.X.Y @@ -136,59 +98,15 @@ update-patch: -X $(PROJECT_DIR)/patch/Python/diff.exclude -p 1 --clean \ > $(PROJECT_DIR)/patch/Python/Python.patch -########################################################################### -# Setup: BZip2 -########################################################################### - -# Download original BZip2 source code archive. -downloads/bzip2-$(BZIP2_VERSION).tar.gz: - @echo ">>> Download BZip2 sources" - curl $(CURL_FLAGS) -o $@ \ - https://sourceware.org/pub/bzip2/$(notdir $@) - -########################################################################### -# Setup: XZ (LZMA) -########################################################################### - -# Download original XZ source code archive. -downloads/xz-$(XZ_VERSION).tar.gz: - @echo ">>> Download XZ sources" - curl $(CURL_FLAGS) -o $@ \ - https://tukaani.org/xz/$(notdir $@) - -########################################################################### -# Setup: OpenSSL -# These build instructions adapted from the scripts developed by -# Felix Shchulze (@x2on) https://github.com/x2on/OpenSSL-for-iPhone -########################################################################### - -# Download original OpenSSL source code archive. -downloads/openssl-$(OPENSSL_VERSION).tar.gz: - @echo ">>> Download OpenSSL sources" - curl $(CURL_FLAGS) -o $@ \ - https://openssl.org/source/$(notdir $@) \ - || curl $(CURL_FLAGS) -o $@ \ - https://openssl.org/source/old/$(basename $(OPENSSL_VERSION))/$(notdir $@) - -########################################################################### -# Setup: libFFI -########################################################################### - -# Download original libFFI source code archive. -downloads/libffi-$(LIBFFI_VERSION).tar.gz: - @echo ">>> Download libFFI sources" - curl $(CURL_FLAGS) -o $@ \ - https://github.com/libffi/libffi/releases/download/v$(LIBFFI_VERSION)/$(notdir $@) - ########################################################################### # Setup: Python ########################################################################### -# Download original Python source code archive. downloads/Python-$(PYTHON_VERSION).tar.gz: @echo ">>> Download Python sources" + mkdir -p downloads curl $(CURL_FLAGS) -o $@ \ - https://www.python.org/ftp/python/$(PYTHON_MICRO_VERSION)/$(basename $(basename $(notdir $@))).tgz + https://www.python.org/ftp/python/$(PYTHON_MICRO_VERSION)/Python-$(PYTHON_VERSION).tgz ########################################################################### # Build for specified target (from $(TARGETS-*)) @@ -229,260 +147,71 @@ LDFLAGS-$(target)=$$(CFLAGS-$(os)) # Target: Aliases ########################################################################### -install/$(os)/bin/$$(TARGET_TRIPLE-$(target))-gcc: +support/$(PYTHON_VER)/$(os)/bin/$$(TARGET_TRIPLE-$(target))-clang: patch/make-xcrun-alias $$@ "--sdk $$(SDK-$(target)) clang -target $$(TARGET_TRIPLE-$(target))" -install/$(os)/bin/$$(TARGET_TRIPLE-$(target))-cpp: +support/$(PYTHON_VER)/$(os)/bin/$$(TARGET_TRIPLE-$(target))-cpp: patch/make-xcrun-alias $$@ "--sdk $$(SDK-$(target)) clang -target $$(TARGET_TRIPLE-$(target)) -E" ########################################################################### # Target: BZip2 ########################################################################### -BZIP2_SRCDIR-$(target)=build/$(os)/$(target)/bzip2-$(BZIP2_VERSION) BZIP2_INSTALL-$(target)=$(PROJECT_DIR)/install/$(os)/$(target)/bzip2-$(BZIP2_VERSION) BZIP2_LIB-$(target)=$$(BZIP2_INSTALL-$(target))/lib/libbz2.a -BZIP2_WHEEL-$(target)=wheels/$(os)/bzip2-$(BZIP2_VERSION)-1-$$(WHEEL_TAG-$(target)).whl -BZIP2_WHEEL_DISTINFO-$(target)=$$(BZIP2_INSTALL-$(target))/wheel/bzip2-$(BZIP2_VERSION).dist-info - -$$(BZIP2_SRCDIR-$(target))/Makefile: downloads/bzip2-$(BZIP2_VERSION).tar.gz - @echo ">>> Unpack BZip2 sources for $(target)" - mkdir -p $$(BZIP2_SRCDIR-$(target)) - tar zxf $$< --strip-components 1 -C $$(BZIP2_SRCDIR-$(target)) - # Touch the makefile to ensure that Make identifies it as up to date. - touch $$(BZIP2_SRCDIR-$(target))/Makefile - -$$(BZIP2_LIB-$(target)): install/$(os)/bin/$$(TARGET_TRIPLE-$(target))-gcc $$(BZIP2_SRCDIR-$(target))/Makefile - @echo ">>> Build BZip2 for $(target)" - cd $$(BZIP2_SRCDIR-$(target)) && \ - PATH="$(PROJECT_DIR)/install/$(os)/bin:$(PATH)" \ - make install \ - PREFIX="$$(BZIP2_INSTALL-$(target))" \ - CC=$$(TARGET_TRIPLE-$(target))-gcc \ - CFLAGS="$$(CFLAGS-$(target))" \ - LDFLAGS="$$(LDFLAGS-$(target))" \ - 2>&1 | tee -a ../bzip2-$(BZIP2_VERSION).build.log - -$$(BZIP2_WHEEL-$(target)): $$(BZIP2_LIB-$(target)) $$(BDIST_WHEEL) - @echo ">>> Build BZip2 wheel for $(target)" - mkdir -p $$(BZIP2_WHEEL_DISTINFO-$(target)) - mkdir -p $$(BZIP2_INSTALL-$(target))/wheel/opt - - # Copy distributable content - cp -r $$(BZIP2_INSTALL-$(target))/include $$(BZIP2_INSTALL-$(target))/wheel/opt/include - cp -r $$(BZIP2_INSTALL-$(target))/lib $$(BZIP2_INSTALL-$(target))/wheel/opt/lib - - # Copy LICENSE file - cp $$(BZIP2_SRCDIR-$(target))/LICENSE $$(BZIP2_WHEEL_DISTINFO-$(target)) - - # Write package metadata - echo "Metadata-Version: 1.2" > $$(BZIP2_WHEEL_DISTINFO-$(target))/METADATA - echo "Name: bzip2" >> $$(BZIP2_WHEEL_DISTINFO-$(target))/METADATA - echo "Version: $(BZIP2_VERSION)" >> $$(BZIP2_WHEEL_DISTINFO-$(target))/METADATA - echo "Summary: " >> $$(BZIP2_WHEEL_DISTINFO-$(target))/METADATA - echo "Download-URL: " >> $$(BZIP2_WHEEL_DISTINFO-$(target))/METADATA - - # Write wheel metadata - echo "Wheel-Version: 1.0" > $$(BZIP2_WHEEL_DISTINFO-$(target))/WHEEL - echo "Root-Is-Purelib: false" >> $$(BZIP2_WHEEL_DISTINFO-$(target))/WHEEL - echo "Generator: Python-Apple-support.BeeWare" >> $$(BZIP2_WHEEL_DISTINFO-$(target))/WHEEL - echo "Build: 1" >> $$(BZIP2_WHEEL_DISTINFO-$(target))/WHEEL - echo "Tag: $$(WHEEL_TAG-$(target))" >> $$(BZIP2_WHEEL_DISTINFO-$(target))/WHEEL - - # Pack the wheel - mkdir -p wheels/$(os) - $(HOST_PYTHON_EXE) -m wheel pack $$(BZIP2_INSTALL-$(target))/wheel --dest-dir wheels/$(os) --build-number 1 + +downloads/bzip2-$(BZIP2_VERSION)-$(target).tar.gz: + @echo ">>> Download BZip2 for $(target)" + mkdir -p downloads + curl $(CURL_FLAGS) -o $$@ \ + https://github.com/beeware/cpython-apple-source-deps/releases/download/BZip2-$(BZIP2_VERSION)/bzip2-$(BZIP2_VERSION)-$(target).tar.gz + +$$(BZIP2_LIB-$(target)): downloads/bzip2-$(BZIP2_VERSION)-$(target).tar.gz + @echo ">>> Install BZip2 for $(target)" + mkdir -p $$(BZIP2_INSTALL-$(target)) + cd $$(BZIP2_INSTALL-$(target)) && tar zxvf $(PROJECT_DIR)/downloads/bzip2-$(BZIP2_VERSION)-$(target).tar.gz + # Ensure the target is marked as clean. + touch $$(BZIP2_LIB-$(target)) ########################################################################### # Target: XZ (LZMA) ########################################################################### -XZ_SRCDIR-$(target)=build/$(os)/$(target)/xz-$(XZ_VERSION) XZ_INSTALL-$(target)=$(PROJECT_DIR)/install/$(os)/$(target)/xz-$(XZ_VERSION) XZ_LIB-$(target)=$$(XZ_INSTALL-$(target))/lib/liblzma.a -XZ_WHEEL-$(target)=wheels/$(os)/xz-$(XZ_VERSION)-1-$$(WHEEL_TAG-$(target)).whl -XZ_WHEEL_DISTINFO-$(target)=$$(XZ_INSTALL-$(target))/wheel/xz-$(XZ_VERSION).dist-info - -$$(XZ_SRCDIR-$(target))/configure: downloads/xz-$(XZ_VERSION).tar.gz - @echo ">>> Unpack XZ sources for $(target)" - mkdir -p $$(XZ_SRCDIR-$(target)) - tar zxf $$< --strip-components 1 -C $$(XZ_SRCDIR-$(target)) - # Patch the source to add support for new platforms - cd $$(XZ_SRCDIR-$(target)) && patch -p1 < $(PROJECT_DIR)/patch/xz-$(XZ_VERSION).patch - # Touch the configure script to ensure that Make identifies it as up to date. - touch $$(XZ_SRCDIR-$(target))/configure -$$(XZ_SRCDIR-$(target))/Makefile: install/$(os)/bin/$$(TARGET_TRIPLE-$(target))-gcc $$(XZ_SRCDIR-$(target))/configure - # Configure the build - cd $$(XZ_SRCDIR-$(target)) && \ - PATH="$(PROJECT_DIR)/install/$(os)/bin:$(PATH)" \ - ./configure \ - CC=$$(TARGET_TRIPLE-$(target))-gcc \ - CFLAGS="$$(CFLAGS-$(target))" \ - LDFLAGS="$$(LDFLAGS-$(target))" \ - --disable-shared \ - --enable-static \ - --host=$$(TARGET_TRIPLE-$(target)) \ - --build=$(HOST_ARCH)-apple-darwin \ - --prefix="$$(XZ_INSTALL-$(target))" \ - 2>&1 | tee -a ../xz-$(XZ_VERSION).config.log +downloads/xz-$(XZ_VERSION)-$(target).tar.gz: + @echo ">>> Download XZ for $(target)" + mkdir -p downloads + curl $(CURL_FLAGS) -o $$@ \ + https://github.com/beeware/cpython-apple-source-deps/releases/download/XZ-$(XZ_VERSION)/xz-$(XZ_VERSION)-$(target).tar.gz -$$(XZ_LIB-$(target)): $$(XZ_SRCDIR-$(target))/Makefile - @echo ">>> Build and install XZ for $(target)" - cd $$(XZ_SRCDIR-$(target)) && \ - PATH="$(PROJECT_DIR)/install/$(os)/bin:$(PATH)" \ - make install \ - 2>&1 | tee -a ../xz-$(XZ_VERSION).build.log - -$$(XZ_WHEEL-$(target)): $$(XZ_LIB-$(target)) $$(BDIST_WHEEL) - @echo ">>> Build XZ wheel for $(target)" - mkdir -p $$(XZ_WHEEL_DISTINFO-$(target)) - mkdir -p $$(XZ_INSTALL-$(target))/wheel/opt - - # Copy distributable content - cp -r $$(XZ_INSTALL-$(target))/include $$(XZ_INSTALL-$(target))/wheel/opt/include - cp -r $$(XZ_INSTALL-$(target))/lib $$(XZ_INSTALL-$(target))/wheel/opt/lib - - # Copy license files - cp $$(XZ_SRCDIR-$(target))/COPYING $$(XZ_WHEEL_DISTINFO-$(target))/LICENSE - cp $$(XZ_SRCDIR-$(target))/COPYING.* $$(XZ_WHEEL_DISTINFO-$(target)) - - # Write package metadata - echo "Metadata-Version: 1.2" > $$(XZ_WHEEL_DISTINFO-$(target))/METADATA - echo "Name: xz" >> $$(XZ_WHEEL_DISTINFO-$(target))/METADATA - echo "Version: $(XZ_VERSION)" >> $$(XZ_WHEEL_DISTINFO-$(target))/METADATA - echo "Summary: " >> $$(XZ_WHEEL_DISTINFO-$(target))/METADATA - echo "Download-URL: " >> $$(XZ_WHEEL_DISTINFO-$(target))/METADATA - - # Write wheel metadata - echo "Wheel-Version: 1.0" > $$(XZ_WHEEL_DISTINFO-$(target))/WHEEL - echo "Root-Is-Purelib: false" >> $$(XZ_WHEEL_DISTINFO-$(target))/WHEEL - echo "Generator: Python-Apple-support.BeeWare" >> $$(XZ_WHEEL_DISTINFO-$(target))/WHEEL - echo "Build: 1" >> $$(XZ_WHEEL_DISTINFO-$(target))/WHEEL - echo "Tag: $$(WHEEL_TAG-$(target))" >> $$(XZ_WHEEL_DISTINFO-$(target))/WHEEL - - # Pack the wheel - mkdir -p wheels/$(os) - $(HOST_PYTHON_EXE) -m wheel pack $$(XZ_INSTALL-$(target))/wheel --dest-dir wheels/$(os) --build-number 1 +$$(XZ_LIB-$(target)): downloads/xz-$(XZ_VERSION)-$(target).tar.gz + @echo ">>> Install XZ for $(target)" + mkdir -p $$(XZ_INSTALL-$(target)) + cd $$(XZ_INSTALL-$(target)) && tar zxvf $(PROJECT_DIR)/downloads/xz-$(XZ_VERSION)-$(target).tar.gz + # Ensure the target is marked as clean. + touch $$(XZ_LIB-$(target)) ########################################################################### # Target: OpenSSL ########################################################################### -OPENSSL_SRCDIR-$(target)=build/$(os)/$(target)/openssl-$(OPENSSL_VERSION) OPENSSL_INSTALL-$(target)=$(PROJECT_DIR)/install/$(os)/$(target)/openssl-$(OPENSSL_VERSION) OPENSSL_SSL_LIB-$(target)=$$(OPENSSL_INSTALL-$(target))/lib/libssl.a -OPENSSL_CRYPTO_LIB-$(target)=$$(OPENSSL_INSTALL-$(target))/lib/libcrypto.a -OPENSSL_WHEEL-$(target)=wheels/$(os)/openssl-$(OPENSSL_VERSION)-1-$$(WHEEL_TAG-$(target)).whl -OPENSSL_WHEEL_DISTINFO-$(target)=$$(OPENSSL_INSTALL-$(target))/wheel/openssl-$(OPENSSL_VERSION).dist-info - -$$(OPENSSL_SRCDIR-$(target))/Configure: downloads/openssl-$(OPENSSL_VERSION).tar.gz - @echo ">>> Unpack and configure OpenSSL sources for $(target)" - mkdir -p $$(OPENSSL_SRCDIR-$(target)) - tar zxf $$< --strip-components 1 -C $$(OPENSSL_SRCDIR-$(target)) - -ifneq ($(os),macOS) - # Patch code to disable the use of fork as it's not available on $(os) -ifeq ($(OPENSSL_VERSION_NUMBER),1.1.1) - sed -ie 's/define HAVE_FORK 1/define HAVE_FORK 0/' $$(OPENSSL_SRCDIR-$(target))/apps/speed.c - sed -ie 's/define HAVE_FORK 1/define HAVE_FORK 0/' $$(OPENSSL_SRCDIR-$(target))/apps/ocsp.c -else - sed -ie 's/define HAVE_FORK 1/define HAVE_FORK 0/' $$(OPENSSL_SRCDIR-$(target))/apps/include/http_server.h - sed -ie 's/define HAVE_FORK 1/define HAVE_FORK 0/' $$(OPENSSL_SRCDIR-$(target))/apps/speed.c -endif -endif - # Touch the Configure script to ensure that Make identifies it as up to date. - touch $$(OPENSSL_SRCDIR-$(target))/Configure +downloads/openssl-$(OPENSSL_VERSION)-$(target).tar.gz: + @echo ">>> Download OpenSSL for $(target)" + mkdir -p downloads + curl $(CURL_FLAGS) -o $$@ \ + https://github.com/beeware/cpython-apple-source-deps/releases/download/OpenSSL-$(OPENSSL_VERSION)/openssl-$(OPENSSL_VERSION)-$(target).tar.gz -$$(OPENSSL_SRCDIR-$(target))/is_configured: install/$(os)/bin/$$(TARGET_TRIPLE-$(target))-gcc $$(OPENSSL_SRCDIR-$(target))/Configure - # Configure the OpenSSL build -ifeq ($(os),macOS) - cd $$(OPENSSL_SRCDIR-$(target)) && \ - PATH="$(PROJECT_DIR)/install/$(os)/bin:$(PATH)" \ - CC="$$(TARGET_TRIPLE-$(target))-gcc $$(CFLAGS-$(target))" \ - ./Configure darwin64-$$(ARCH-$(target))-cc no-tests \ - --prefix="$$(OPENSSL_INSTALL-$(target))" \ - --openssldir=/etc/ssl \ - 2>&1 | tee -a ../openssl-$(OPENSSL_VERSION).config.log -else - cd $$(OPENSSL_SRCDIR-$(target)) && \ - PATH="$(PROJECT_DIR)/install/$(os)/bin:$(PATH)" \ - CC="$$(TARGET_TRIPLE-$(target))-gcc $$(CFLAGS-$(target))" \ - CROSS_TOP="$$(dir $$(SDK_ROOT-$(target))).." \ - CROSS_SDK="$$(notdir $$(SDK_ROOT-$(target)))" \ - ./Configure iphoneos-cross no-asm no-tests \ - --prefix="$$(OPENSSL_INSTALL-$(target))" \ - --openssldir=/etc/ssl \ - 2>&1 | tee -a ../openssl-$(OPENSSL_VERSION).config.log -endif - # The OpenSSL Makefile is... interesting. Invoking `make all` or `make - # install` *modifies the Makefile*. Therefore, we can't use the Makefile as - # a build dependency, because building/installing dirties the target that - # was used as a dependency. To compensate, create a dummy file as a marker - # for whether OpenSSL has been configured, and use *that* as a reference. - date > $$(OPENSSL_SRCDIR-$(target))/is_configured - -$$(OPENSSL_SRCDIR-$(target))/libssl.a: $$(OPENSSL_SRCDIR-$(target))/is_configured - @echo ">>> Build OpenSSL for $(target)" - # OpenSSL's `all` target modifies the Makefile; - # use the raw targets that make up all and it's dependencies - cd $$(OPENSSL_SRCDIR-$(target)) && \ - PATH="$(PROJECT_DIR)/install/$(os)/bin:$(PATH)" \ - CC="$$(TARGET_TRIPLE-$(target))-gcc $$(CFLAGS-$(target))" \ - CROSS_TOP="$$(dir $$(SDK_ROOT-$(target))).." \ - CROSS_SDK="$$(notdir $$(SDK_ROOT-$(target)))" \ - make build_sw \ - 2>&1 | tee -a ../openssl-$(OPENSSL_VERSION).build.log - -$$(OPENSSL_SSL_LIB-$(target)): $$(OPENSSL_SRCDIR-$(target))/libssl.a +$$(OPENSSL_SSL_LIB-$(target)): downloads/openssl-$(OPENSSL_VERSION)-$(target).tar.gz @echo ">>> Install OpenSSL for $(target)" - # Install just the software (not the docs) - cd $$(OPENSSL_SRCDIR-$(target)) && \ - PATH="$(PROJECT_DIR)/install/$(os)/bin:$(PATH)" \ - CC="$$(TARGET_TRIPLE-$(target))-gcc $$(CFLAGS-$(target))" \ - CROSS_TOP="$$(dir $$(SDK_ROOT-$(target))).." \ - CROSS_SDK="$$(notdir $$(SDK_ROOT-$(target)))" \ - make install_sw \ - 2>&1 | tee -a ../openssl-$(OPENSSL_VERSION).install.log - -$$(OPENSSL_WHEEL-$(target)): $$(OPENSSL_LIB-$(target)) $$(BDIST_WHEEL) - @echo ">>> Build OpenSSL wheel for $(target)" - mkdir -p $$(OPENSSL_WHEEL_DISTINFO-$(target)) - mkdir -p $$(OPENSSL_INSTALL-$(target))/wheel/opt - - # Copy distributable content - cp -r $$(OPENSSL_INSTALL-$(target))/include $$(OPENSSL_INSTALL-$(target))/wheel/opt/include - cp -r $$(OPENSSL_INSTALL-$(target))/lib $$(OPENSSL_INSTALL-$(target))/wheel/opt/lib - - # Remove dynamic library content - rm -f $$(OPENSSL_INSTALL-$(target))/wheel/opt/lib/*.dylib - - # Copy LICENSE file - # OpenSSL 1.1.1 uses LICENSE; OpenSSL 3 uses LICENSE.txt - if [ -f "$$(OPENSSL_SRCDIR-$(target))/LICENSE.txt" ]; then \ - cp $$(OPENSSL_SRCDIR-$(target))/LICENSE.txt $$(OPENSSL_WHEEL_DISTINFO-$(target))/LICENSE; \ - else \ - cp $$(OPENSSL_SRCDIR-$(target))/LICENSE $$(OPENSSL_WHEEL_DISTINFO-$(target))/LICENSE; \ - fi - - # Write package metadata - echo "Metadata-Version: 1.2" > $$(OPENSSL_WHEEL_DISTINFO-$(target))/METADATA - echo "Name: openssl" >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/METADATA - echo "Version: $(OPENSSL_VERSION)" >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/METADATA - echo "Summary: " >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/METADATA - echo "Download-URL: " >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/METADATA - - # Write wheel metadata - echo "Wheel-Version: 1.0" > $$(OPENSSL_WHEEL_DISTINFO-$(target))/WHEEL - echo "Root-Is-Purelib: false" >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/WHEEL - echo "Generator: Python-Apple-support.BeeWare" >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/WHEEL - echo "Build: 1" >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/WHEEL - echo "Tag: $$(WHEEL_TAG-$(target))" >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/WHEEL - - # Pack the wheel - mkdir -p wheels/$(os) - $(HOST_PYTHON_EXE) -m wheel pack $$(OPENSSL_INSTALL-$(target))/wheel --dest-dir wheels/$(os) --build-number 1 + mkdir -p $$(OPENSSL_INSTALL-$(target)) + cd $$(OPENSSL_INSTALL-$(target)) && tar zxvf $(PROJECT_DIR)/downloads/openssl-$(OPENSSL_VERSION)-$(target).tar.gz + # Ensure the target is marked as clean. + touch $$(OPENSSL_SSL_LIB-$(target)) ########################################################################### # Target: libFFI @@ -493,50 +222,21 @@ $$(OPENSSL_WHEEL-$(target)): $$(OPENSSL_LIB-$(target)) $$(BDIST_WHEEL) # The configure step is performed as part of the OS-level build. ifneq ($(os),macOS) -LIBFFI_SRCDIR-$(os)=build/$(os)/libffi-$(LIBFFI_VERSION) -LIBFFI_SRCDIR-$(target)=$$(LIBFFI_SRCDIR-$(os))/build_$$(SDK-$(target))-$$(ARCH-$(target)) -LIBFFI_LIB-$(target)=$$(LIBFFI_SRCDIR-$(target))/.libs/libffi.a -LIBFFI_WHEEL-$(target)=wheels/$(os)/libffi-$(LIBFFI_VERSION)-1-$$(WHEEL_TAG-$(target)).whl -LIBFFI_WHEEL_DISTINFO-$(target)=$$(LIBFFI_SRCDIR-$(target))/wheel/libffi-$(LIBFFI_VERSION).dist-info - -$$(LIBFFI_LIB-$(target)): $$(LIBFFI_SRCDIR-$(os))/darwin_common/include/ffi.h - @echo ">>> Build libFFI for $(target)" - cd $$(LIBFFI_SRCDIR-$(target)) && \ - make \ - 2>&1 | tee -a ../../libffi-$(LIBFFI_VERSION).build.log - -$$(LIBFFI_WHEEL-$(target)): $$(LIBFFI_LIB-$(target)) $$(BDIST_WHEEL) - @echo ">>> Build libFFI wheel for $(target)" - mkdir -p $$(LIBFFI_WHEEL_DISTINFO-$(target)) - mkdir -p $$(LIBFFI_SRCDIR-$(target))/wheel/opt - - # Copy distributable content - cp -r $$(LIBFFI_SRCDIR-$(target))/include $$(LIBFFI_SRCDIR-$(target))/wheel/opt/include - cp -r $$(LIBFFI_SRCDIR-$(target))/.libs $$(LIBFFI_SRCDIR-$(target))/wheel/opt/lib - - # Remove dynamic library content - rm -f $$(LIBFFI_SRCDIR-$(target))/wheel/opt/lib/*.dylib - - # Copy LICENSE file - cp $$(LIBFFI_SRCDIR-$(os))/LICENSE $$(LIBFFI_WHEEL_DISTINFO-$(target)) - - # Write package metadata - echo "Metadata-Version: 1.2" > $$(LIBFFI_WHEEL_DISTINFO-$(target))/METADATA - echo "Name: libffi" >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/METADATA - echo "Version: $(LIBFFI_VERSION)" >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/METADATA - echo "Summary: " >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/METADATA - echo "Download-URL: " >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/METADATA - - # Write wheel metadata - echo "Wheel-Version: 1.0" > $$(LIBFFI_WHEEL_DISTINFO-$(target))/WHEEL - echo "Root-Is-Purelib: false" >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/WHEEL - echo "Generator: Python-Apple-support.BeeWare" >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/WHEEL - echo "Build: 1" >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/WHEEL - echo "Tag: $$(WHEEL_TAG-$(target))" >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/WHEEL - - # Pack the wheel - mkdir -p wheels/$(os) - $(HOST_PYTHON_EXE) -m wheel pack $$(LIBFFI_SRCDIR-$(target))/wheel --dest-dir wheels/$(os) --build-number 1 +LIBFFI_INSTALL-$(target)=$(PROJECT_DIR)/install/$(os)/$(target)/libffi-$(LIBFFI_VERSION) +LIBFFI_LIB-$(target)=$$(LIBFFI_INSTALL-$(target))/lib/libffi.a + +downloads/libffi-$(LIBFFI_VERSION)-$(target).tar.gz: + @echo ">>> Download libFFI for $(target)" + mkdir -p downloads + curl $(CURL_FLAGS) -o $$@ \ + https://github.com/beeware/cpython-apple-source-deps/releases/download/libFFI-$(LIBFFI_VERSION)/libffi-$(LIBFFI_VERSION)-$(target).tar.gz + +$$(LIBFFI_LIB-$(target)): downloads/libffi-$(LIBFFI_VERSION)-$(target).tar.gz + @echo ">>> Install libFFI for $(target)" + mkdir -p $$(LIBFFI_INSTALL-$(target)) + cd $$(LIBFFI_INSTALL-$(target)) && tar zxvf $(PROJECT_DIR)/downloads/libffi-$(LIBFFI_VERSION)-$(target).tar.gz + # Ensure the target is marked as clean. + touch $$(LIBFFI_LIB-$(target)) endif @@ -555,11 +255,10 @@ PYTHON_LIB-$(target)=$$(PYTHON_INSTALL-$(target))/lib/libpython$(PYTHON_VER).a $$(PYTHON_SRCDIR-$(target))/configure: \ downloads/Python-$(PYTHON_VERSION).tar.gz \ - $$(BZIP2_FATLIB-$$(SDK-$(target))) \ - $$(XZ_FATLIB-$$(SDK-$(target))) \ - $$(OPENSSL_FATINCLUDE-$$(SDK-$(target))) $$(OPENSSL_SSL_FATLIB-$$(SDK-$(target))) $$(OPENSSL_CRYPTO_FATLIB-$$(SDK-$(target))) \ - $$(LIBFFI_FATLIB-$$(SDK-$(target))) \ - $$(PYTHON_LIB-macOS) + $$(BZIP2_LIB-$(target)) \ + $$(XZ_LIB-$(target)) \ + $$(OPENSSL_SSL_LIB-$(target)) \ + $$(LIBFFI_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)) @@ -569,32 +268,32 @@ $$(PYTHON_SRCDIR-$(target))/configure: \ touch $$(PYTHON_SRCDIR-$(target))/configure $$(PYTHON_SRCDIR-$(target))/Makefile: \ - install/$(os)/bin/$$(TARGET_TRIPLE-$$(SDK-$(target)))-ar \ - install/$(os)/bin/$$(TARGET_TRIPLE-$(target))-gcc \ - install/$(os)/bin/$$(TARGET_TRIPLE-$(target))-cpp \ + support/$(PYTHON_VER)/$(os)/bin/$$(TARGET_TRIPLE-$$(SDK-$(target)))-ar \ + support/$(PYTHON_VER)/$(os)/bin/$$(TARGET_TRIPLE-$(target))-clang \ + support/$(PYTHON_VER)/$(os)/bin/$$(TARGET_TRIPLE-$(target))-cpp \ $$(PYTHON_SRCDIR-$(target))/configure # Configure target Python cd $$(PYTHON_SRCDIR-$(target)) && \ - PATH="$(PROJECT_DIR)/install/$(os)/bin:$(PATH)" \ + PATH="$(PROJECT_DIR)/support/$(PYTHON_VER)/$(os)/bin:$(PATH)" \ ./configure \ AR=$$(TARGET_TRIPLE-$$(SDK-$(target)))-ar \ - CC=$$(TARGET_TRIPLE-$(target))-gcc \ + CC=$$(TARGET_TRIPLE-$(target))-clang \ CPP=$$(TARGET_TRIPLE-$(target))-cpp \ - CXX=$$(TARGET_TRIPLE-$(target))-gcc \ + CXX=$$(TARGET_TRIPLE-$(target))-clang \ CFLAGS="$$(CFLAGS-$(target))" \ LDFLAGS="$$(LDFLAGS-$(target))" \ - LIBLZMA_CFLAGS="-I$$(XZ_MERGE-$$(SDK-$(target)))/include" \ - LIBLZMA_LIBS="-L$$(XZ_MERGE-$$(SDK-$(target)))/lib -llzma" \ - BZIP2_CFLAGS="-I$$(BZIP2_MERGE-$$(SDK-$(target)))/include" \ - BZIP2_LIBS="-L$$(BZIP2_MERGE-$$(SDK-$(target)))/lib -lbz2" \ - LIBFFI_CFLAGS="-I$$(LIBFFI_MERGE-$$(SDK-$(target)))/include" \ - LIBFFI_LIBS="-L$$(LIBFFI_MERGE-$$(SDK-$(target)))/lib -lffi" \ + LIBLZMA_CFLAGS="-I$$(XZ_INSTALL-$(target))/include" \ + LIBLZMA_LIBS="-L$$(XZ_INSTALL-$(target))/lib -llzma" \ + BZIP2_CFLAGS="-I$$(BZIP2_INSTALL-$(target))/include" \ + BZIP2_LIBS="-L$$(BZIP2_INSTALL-$(target))/lib -lbz2" \ + LIBFFI_CFLAGS="-I$$(LIBFFI_INSTALL-$(target))/include" \ + LIBFFI_LIBS="-L$$(LIBFFI_INSTALL-$(target))/lib -lffi" \ --host=$$(TARGET_TRIPLE-$(target)) \ --build=$(HOST_ARCH)-apple-darwin \ - --with-build-python=$$(PYTHON_INSTALL-macosx)/bin/python$(PYTHON_VER) \ + --with-build-python=$(HOST_PYTHON) \ --prefix="$$(PYTHON_INSTALL-$(target))" \ --enable-ipv6 \ - --with-openssl="$$(OPENSSL_MERGE-$$(SDK-$(target)))" \ + --with-openssl="$$(OPENSSL_INSTALL-$(target))" \ --without-ensurepip \ ac_cv_file__dev_ptmx=no \ ac_cv_file__dev_ptc=no \ @@ -604,14 +303,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)/install/$(os)/bin:$(PATH)" \ + PATH="$(PROJECT_DIR)/support/$(PYTHON_VER)/$(os)/bin:$(PATH)" \ make 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)/install/$(os)/bin:$(PATH)" \ + PATH="$(PROJECT_DIR)/support/$(PYTHON_VER)/$(os)/bin:$(PATH)" \ make install \ 2>&1 | tee -a ../python-$(PYTHON_VERSION).install.log @@ -628,6 +327,7 @@ $$(PYTHON_SITECUSTOMIZE-$(target)): | sed -e "s/{{tag}}/$$(OS_LOWER-$(target))-$$(VERSION_MIN-$(os))-$$(SDK-$(target))-$$(ARCH-$(target))/g" \ > $$(PYTHON_SITECUSTOMIZE-$(target)) +$(target): $$(PYTHON_SITECUSTOMIZE-$(target)) $$(PYTHON_LIB-$(target)) ########################################################################### # Target: Debug @@ -641,26 +341,14 @@ vars-$(target): @echo "SDK_ROOT-$(target): $$(SDK_ROOT-$(target))" @echo "CFLAGS-$(target): $$(CFLAGS-$(target))" @echo "LDFLAGS-$(target): $$(LDFLAGS-$(target))" - @echo "BZIP2_SRCDIR-$(target): $$(BZIP2_SRCDIR-$(target))" @echo "BZIP2_INSTALL-$(target): $$(BZIP2_INSTALL-$(target))" @echo "BZIP2_LIB-$(target): $$(BZIP2_LIB-$(target))" - @echo "BZIP2_WHEEL-$(target): $$(BZIP2_WHEEL-$(target))" - @echo "BZIP2_WHEEL_DISTINFO-$(target): $$(BZIP2_WHEEL_DISTINFO-$(target))" - @echo "XZ_SRCDIR-$(target): $$(XZ_SRCDIR-$(target))" @echo "XZ_INSTALL-$(target): $$(XZ_INSTALL-$(target))" @echo "XZ_LIB-$(target): $$(XZ_LIB-$(target))" - @echo "XZ_WHEEL-$(target): $$(XZ_WHEEL-$(target))" - @echo "XZ_WHEEL_DISTINFO-$(target): $$(XZ_WHEEL_DISTINFO-$(target))" - @echo "OPENSSL_SRCDIR-$(target): $$(OPENSSL_SRCDIR-$(target))" @echo "OPENSSL_INSTALL-$(target): $$(OPENSSL_INSTALL-$(target))" @echo "OPENSSL_SSL_LIB-$(target): $$(OPENSSL_SSL_LIB-$(target))" - @echo "OPENSSL_CRYPTO_LIB-$(target): $$(OPENSSL_CRYPTO_LIB-$(target))" - @echo "OPENSSL_WHEEL-$(target): $$(OPENSSL_WHEEL-$(target))" - @echo "OPENSSL_WHEEL_DISTINFO-$(target): $$(OPENSSL_WHEEL_DISTINFO-$(target))" - @echo "LIBFFI_SRCDIR-$(target): $$(LIBFFI_SRCDIR-$(target))" + @echo "LIBFFI_INSTALL-$(target): $$(LIBFFI_INSTALL-$(target))" @echo "LIBFFI_LIB-$(target): $$(LIBFFI_LIB-$(target))" - @echo "LIBFFI_WHEEL-$(target): $$(LIBFFI_WHEEL-$(target))" - @echo "LIBFFI_WHEEL_DISTINFO-$(target): $$(LIBFFI_WHEEL_DISTINFO-$(target))" @echo "PYTHON_SRCDIR-$(target): $$(PYTHON_SRCDIR-$(target))" @echo "PYTHON_INSTALL-$(target): $$(PYTHON_INSTALL-$(target))" @echo "PYTHON_LIB-$(target): $$(PYTHON_LIB-$(target))" @@ -696,25 +384,10 @@ CFLAGS-$(sdk)=$$(CFLAGS-$(os)) LDFLAGS-$(sdk)=$$(CFLAGS-$(os)) # Predeclare SDK constants that are used by the build-target macro - -BZIP2_MERGE-$(sdk)=$(PROJECT_DIR)/merge/$(os)/$(sdk)/bzip2-$(BZIP2_VERSION) -BZIP2_FATLIB-$(sdk)=$$(BZIP2_MERGE-$(sdk))/lib/libbz2.a - -XZ_MERGE-$(sdk)=$(PROJECT_DIR)/merge/$(os)/$(sdk)/xz-$(XZ_VERSION) -XZ_FATLIB-$(sdk)=$$(XZ_MERGE-$(sdk))/lib/liblzma.a - -OPENSSL_MERGE-$(sdk)=$(PROJECT_DIR)/merge/$(os)/$(sdk)/openssl-$(OPENSSL_VERSION) -OPENSSL_FATINCLUDE-$(sdk)=$$(OPENSSL_MERGE-$(sdk))/include -OPENSSL_SSL_FATLIB-$(sdk)=$$(OPENSSL_MERGE-$(sdk))/lib/libssl.a -OPENSSL_CRYPTO_FATLIB-$(sdk)=$$(OPENSSL_MERGE-$(sdk))/lib/libcrypto.a - -LIBFFI_MERGE-$(sdk)=$(PROJECT_DIR)/merge/$(os)/$(sdk)/libffi-$(LIBFFI_VERSION) -LIBFFI_FATLIB-$(sdk)=$$(LIBFFI_MERGE-$(sdk))/lib/libffi.a - -PYTHON_MERGE-$(sdk)=$(PROJECT_DIR)/merge/$(os)/$(sdk)/python-$(PYTHON_VERSION) -PYTHON_FATLIB-$(sdk)=$$(PYTHON_MERGE-$(sdk))/libPython$(PYTHON_VER).a -PYTHON_FATINCLUDE-$(sdk)=$$(PYTHON_MERGE-$(sdk))/Headers -PYTHON_FATSTDLIB-$(sdk)=$$(PYTHON_MERGE-$(sdk))/python-stdlib +PYTHON_INSTALL-$(sdk)=$(PROJECT_DIR)/install/$(os)/$(sdk)/python-$(PYTHON_VERSION) +PYTHON_LIB-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/lib/libpython$(PYTHON_VER).a +PYTHON_INCLUDE-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/include/python$(PYTHON_VER) +PYTHON_STDLIB-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/lib/python$(PYTHON_VER) ifeq ($(os),macOS) TARGET_TRIPLE-$(sdk)=apple-darwin @@ -733,76 +406,77 @@ $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(eval $$(call build-target,$$(target) # SDK: Aliases ########################################################################### -install/$(os)/bin/$$(TARGET_TRIPLE-$(sdk))-gcc: +support/$(PYTHON_VER)/$(os)/bin/$$(TARGET_TRIPLE-$(sdk))-clang: patch/make-xcrun-alias $$@ "--sdk $(sdk) clang" -install/$(os)/bin/$$(TARGET_TRIPLE-$(sdk))-cpp: +support/$(PYTHON_VER)/$(os)/bin/$$(TARGET_TRIPLE-$(sdk))-cpp: patch/make-xcrun-alias $$@ "--sdk $(sdk) clang -E" -install/$(os)/bin/$$(TARGET_TRIPLE-$(sdk))-ar: +support/$(PYTHON_VER)/$(os)/bin/$$(TARGET_TRIPLE-$(sdk))-ar: patch/make-xcrun-alias $$@ "--sdk $(sdk) ar" ########################################################################### # SDK: BZip2 ########################################################################### -$$(BZIP2_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(BZIP2_LIB-$$(target))) - @echo ">>> Build BZip2 fat library for $(sdk)" - mkdir -p $$(BZIP2_MERGE-$(sdk))/lib - lipo -create -output $$@ $$^ \ - 2>&1 | tee -a merge/$(os)/$(sdk)/bzip2-$(BZIP2_VERSION).lipo.log - # Copy headers from the first target associated with the $(sdk) SDK - cp -r $$(BZIP2_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/include $$(BZIP2_MERGE-$(sdk)) +BZIP2_INSTALL-$(sdk)=$(PROJECT_DIR)/install/$(os)/$(sdk)/bzip2-$(BZIP2_VERSION) +BZIP2_LIB-$(sdk)=$$(BZIP2_INSTALL-$(sdk))/lib/libbz2.a + +# This is only used on macOS. +downloads/bzip2-$(BZIP2_VERSION)-$(sdk).tar.gz: + @echo ">>> Download BZip2 for $(sdk)" + mkdir -p downloads + curl $(CURL_FLAGS) -o $$@ \ + https://github.com/beeware/cpython-macOS-source-deps/releases/download/BZip2-$(BZIP2_VERSION)/bzip2-$(BZIP2_VERSION)-$(sdk).tar.gz + +$$(BZIP2_LIB-$(sdk)): downloads/bzip2-$(BZIP2_VERSION)-$(sdk).tar.gz + @echo ">>> Install BZip2 for $(sdk)" + mkdir -p $$(BZIP2_INSTALL-$(sdk)) + cd $$(BZIP2_INSTALL-$(sdk)) && tar zxvf $(PROJECT_DIR)/downloads/bzip2-$(BZIP2_VERSION)-$(sdk).tar.gz + # Ensure the target is marked as clean. + touch $$(BZIP2_LIB-$(sdk)) ########################################################################### # SDK: XZ (LZMA) ########################################################################### -$$(XZ_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(XZ_LIB-$$(target))) - @echo ">>> Build XZ fat library for $(sdk)" - mkdir -p $$(XZ_MERGE-$(sdk))/lib - lipo -create -output $$@ $$^ \ - 2>&1 | tee -a merge/$(os)/$(sdk)/xz-$(XZ_VERSION).lipo.log - # Copy headers from the first target associated with the $(sdk) SDK - cp -r $$(XZ_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/include $$(XZ_MERGE-$(sdk)) +XZ_INSTALL-$(sdk)=$(PROJECT_DIR)/install/$(os)/$(sdk)/xz-$(XZ_VERSION) +XZ_LIB-$(sdk)=$$(XZ_INSTALL-$(sdk))/lib/liblzma.a + +# This is only used on macOS. +downloads/xz-$(XZ_VERSION)-$(sdk).tar.gz: + @echo ">>> Download XZ for $(sdk)" + mkdir -p downloads + curl $(CURL_FLAGS) -o $$@ \ + https://github.com/beeware/cpython-macOS-source-deps/releases/download/XZ-$(XZ_VERSION)/xz-$(XZ_VERSION)-$(sdk).tar.gz + +$$(XZ_LIB-$(sdk)): downloads/xz-$(XZ_VERSION)-$(sdk).tar.gz + @echo ">>> Install XZ for $(sdk)" + mkdir -p $$(XZ_INSTALL-$(sdk)) + cd $$(XZ_INSTALL-$(sdk)) && tar zxvf $(PROJECT_DIR)/downloads/xz-$(XZ_VERSION)-$(sdk).tar.gz + # Ensure the target is marked as clean. + touch $$(XZ_LIB-$(sdk)) ########################################################################### # SDK: OpenSSL ########################################################################### -$$(OPENSSL_FATINCLUDE-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_SSL_LIB-$$(target))) - @echo ">>> Copy OpenSSL headers from the first target associated with the SDK" - mkdir -p $$(OPENSSL_MERGE-$(sdk)) - cp -r $$(OPENSSL_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/include $$(OPENSSL_MERGE-$(sdk)) - -$$(OPENSSL_SSL_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_SSL_LIB-$$(target))) - @echo ">>> Build OpenSSL ssl fat library for $(sdk)" - mkdir -p $$(OPENSSL_MERGE-$(sdk))/lib - lipo -create -output $$@ \ - $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_SSL_LIB-$$(target))) \ - 2>&1 | tee -a merge/$(os)/$(sdk)/openssl-$(OPENSSL_VERSION).ssl.lipo.log - -$$(OPENSSL_CRYPTO_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_CRYPTO_LIB-$$(target))) - @echo ">>> Build OpenSSL crypto fat library for $(sdk)" - mkdir -p $$(OPENSSL_MERGE-$(sdk))/lib - lipo -create -output $$@ \ - $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_CRYPTO_LIB-$$(target))) \ - 2>&1 | tee -a merge/$(os)/$(sdk)/openssl-$(OPENSSL_VERSION).crypto.lipo.log +OPENSSL_INSTALL-$(sdk)=$(PROJECT_DIR)/install/$(os)/$(sdk)/openssl-$(OPENSSL_VERSION) +OPENSSL_SSL_LIB-$(sdk)=$$(OPENSSL_INSTALL-$(sdk))/lib/libssl.a -########################################################################### -# SDK: libFFI -########################################################################### +# This is only used on macOS. +downloads/openssl-$(OPENSSL_VERSION)-$(sdk).tar.gz: + @echo ">>> Download OpenSSL for $(sdk)" + mkdir -p downloads + curl $(CURL_FLAGS) -o $$@ \ + https://github.com/beeware/cpython-macOS-source-deps/releases/download/OpenSSL-$(OPENSSL_VERSION)/openssl-$(OPENSSL_VERSION)-$(sdk).tar.gz -$$(LIBFFI_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(LIBFFI_LIB-$$(target))) - @echo ">>> Build libFFI fat library for $(sdk)" - mkdir -p $$(LIBFFI_MERGE-$(sdk))/lib - lipo -create -output $$@ $$^ \ - 2>&1 | tee -a merge/$(os)/$(sdk)/libffi-$(LIBFFI_VERSION).lipo.log - # Copy headers from the first target associated with the $(sdk) SDK - cp -f -r $$(LIBFFI_SRCDIR-$(os))/darwin_common/include \ - $$(LIBFFI_MERGE-$(sdk)) - cp -f -r $$(LIBFFI_SRCDIR-$(os))/darwin_$$(OS_LOWER-$(sdk))/include/* \ - $$(LIBFFI_MERGE-$(sdk))/include +$$(OPENSSL_SSL_LIB-$(sdk)): downloads/openssl-$(OPENSSL_VERSION)-$(sdk).tar.gz + @echo ">>> Install OpenSSL for $(sdk)" + mkdir -p $$(OPENSSL_INSTALL-$(sdk)) + cd $$(OPENSSL_INSTALL-$(sdk)) && tar zxvf $(PROJECT_DIR)/downloads/openssl-$(OPENSSL_VERSION)-$(sdk).tar.gz + # Ensure the target is marked as clean. + touch $$(OPENSSL_SSL_LIB-$(sdk)) ########################################################################### # SDK: Python @@ -814,13 +488,11 @@ $$(LIBFFI_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(LIBFFI_LIB ifeq ($(os),macOS) PYTHON_SRCDIR-$(sdk)=build/$(os)/$(sdk)/python-$(PYTHON_VERSION) -PYTHON_INSTALL-$(sdk)=$(PROJECT_DIR)/install/$(os)/$(sdk)/python-$(PYTHON_VERSION) -PYTHON_LIB-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/lib/libpython$(PYTHON_VER).a $$(PYTHON_SRCDIR-$(sdk))/configure: \ - $$(BZIP2_FATLIB-$$(sdk)) \ - $$(XZ_FATLIB-$$(sdk)) \ - $$(OPENSSL_FATINCLUDE-$$(sdk)) $$(OPENSSL_SSL_FATLIB-$$(sdk)) $$(OPENSSL_CRYPTO_FATLIB-$$(sdk)) \ + $$(BZIP2_LIB-$$(sdk)) \ + $$(XZ_LIB-$$(sdk)) \ + $$(OPENSSL_SSL_LIB-$$(sdk)) \ downloads/Python-$(PYTHON_VERSION).tar.gz @echo ">>> Unpack and configure Python for $(sdk)" mkdir -p $$(PYTHON_SRCDIR-$(sdk)) @@ -831,60 +503,43 @@ $$(PYTHON_SRCDIR-$(sdk))/configure: \ touch $$(PYTHON_SRCDIR-$(sdk))/configure $$(PYTHON_SRCDIR-$(sdk))/Makefile: \ - install/$(os)/bin/$$(TARGET_TRIPLE-$(sdk))-gcc \ - install/$(os)/bin/$$(TARGET_TRIPLE-$(sdk))-cpp \ + support/$(PYTHON_VER)/$(os)/bin/$$(TARGET_TRIPLE-$(sdk))-clang \ + support/$(PYTHON_VER)/$(os)/bin/$$(TARGET_TRIPLE-$(sdk))-cpp \ $$(PYTHON_SRCDIR-$(sdk))/configure # Configure target Python cd $$(PYTHON_SRCDIR-$(sdk)) && \ - PATH="$(PROJECT_DIR)/install/$(os)/bin:$(PATH)" \ + PATH="$(PROJECT_DIR)/support/$(PYTHON_VER)/$(os)/bin:$(PATH)" \ ./configure \ - CC=$$(TARGET_TRIPLE-$(sdk))-gcc \ + CC=$$(TARGET_TRIPLE-$(sdk))-clang \ CPP=$$(TARGET_TRIPLE-$(sdk))-cpp \ CFLAGS="$$(CFLAGS-$(sdk))" \ LDFLAGS="$$(LDFLAGS-$(sdk))" \ - LIBLZMA_CFLAGS="-I$$(XZ_MERGE-$(sdk))/include" \ - LIBLZMA_LIBS="-L$$(XZ_MERGE-$(sdk))/lib -llzma" \ - BZIP2_CFLAGS="-I$$(BZIP2_MERGE-$(sdk))/include" \ - BZIP2_LIBS="-L$$(BZIP2_MERGE-$(sdk))/lib -lbz2" \ + LIBLZMA_CFLAGS="-I$$(XZ_INSTALL-$(sdk))/include" \ + LIBLZMA_LIBS="-L$$(XZ_INSTALL-$(sdk))/lib -llzma" \ + BZIP2_CFLAGS="-I$$(BZIP2_INSTALL-$(sdk))/include" \ + BZIP2_LIBS="-L$$(BZIP2_INSTALL-$(sdk))/lib -lbz2" \ MACOSX_DEPLOYMENT_TARGET="$$(VERSION_MIN-$(os))" \ --prefix="$$(PYTHON_INSTALL-$(sdk))" \ --enable-ipv6 \ --enable-universalsdk \ - --with-openssl="$$(OPENSSL_MERGE-$(sdk))" \ + --with-openssl="$$(OPENSSL_INSTALL-$(sdk))" \ --with-universal-archs=universal2 \ --without-ensurepip \ 2>&1 | tee -a ../python-$(PYTHON_VERSION).config.log -$$(PYTHON_SRCDIR-$(sdk))/python.exe: \ - $$(PYTHON_SRCDIR-$(sdk))/Makefile +$$(PYTHON_SRCDIR-$(sdk))/python.exe: $$(PYTHON_SRCDIR-$(sdk))/Makefile @echo ">>> Build Python for $(sdk)" cd $$(PYTHON_SRCDIR-$(sdk)) && \ - PATH="$(PROJECT_DIR)/install/$(os)/bin:$(PATH)" \ + PATH="$(PROJECT_DIR)/support/$(PYTHON_VER)/$(os)/bin:$(PATH)" \ make all \ 2>&1 | tee -a ../python-$(PYTHON_VERSION).build.log -$(HOST_PYTHON_EXE) $$(PYTHON_LIB-$(sdk)): $$(PYTHON_SRCDIR-$(sdk))/python.exe +$$(PYTHON_LIB-$(sdk)) $$(PYTHON_INCLUDE-$$(sdk))/Python.h $$(PYTHON_STDLIB-$(sdk))/LICENSE.TXT: $$(PYTHON_SRCDIR-$(sdk))/python.exe @echo ">>> Install Python for $(sdk)" cd $$(PYTHON_SRCDIR-$(sdk)) && \ make install \ 2>&1 | tee -a ../python-$(PYTHON_VERSION).install.log -$$(PYTHON_FATLIB-$(sdk)): $$(PYTHON_LIB-$(sdk)) - @echo ">>> Build Python fat library for $(sdk)" - mkdir -p $$(dir $$(PYTHON_FATLIB-$(sdk))) - # The macosx static library is already fat; copy it as-is - cp $$(PYTHON_LIB-$(sdk)) $$(PYTHON_FATLIB-$(sdk)) - -$$(PYTHON_FATINCLUDE-$(sdk)): $$(PYTHON_LIB-$(sdk)) - @echo ">>> Build Python fat library for $(sdk)" - # The macosx headers are already fat; copy as-is - cp -r $$(PYTHON_INSTALL-$(sdk))/include/python$(PYTHON_VER) $$(PYTHON_FATINCLUDE-$(sdk)) - -$$(PYTHON_FATSTDLIB-$(sdk)): $$(PYTHON_LIB-$(sdk)) - @echo ">>> Build Python stdlib library for $(sdk)" - # The macosx stdlib is already fat; copy it as-is - cp -r $$(PYTHON_INSTALL-$(sdk))/lib/python$(PYTHON_VER) $$(PYTHON_FATSTDLIB-$(sdk)) - else # Non-macOS builds need to be merged on a per-SDK basis. The merge covers: @@ -892,44 +547,46 @@ else # * Installing an architecture-sensitive pyconfig.h # * Merging fat versions of the standard library lib-dynload folder -$$(PYTHON_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_LIB-$$(target))) +$$(PYTHON_LIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_LIB-$$(target))) @echo ">>> Build Python fat library for the $(sdk) SDK" - mkdir -p $$(dir $$(PYTHON_FATLIB-$(sdk))) + mkdir -p $$(dir $$(PYTHON_LIB-$(sdk))) lipo -create -output $$@ $$^ \ - 2>&1 | tee -a merge/$(os)/$(sdk)/python-$(PYTHON_VERSION).lipo.log + 2>&1 | tee -a install/$(os)/$(sdk)/python-$(PYTHON_VERSION).lipo.log -$$(PYTHON_FATINCLUDE-$(sdk)): $$(PYTHON_LIB-$(sdk)) +$$(PYTHON_INCLUDE-$(sdk))/Python.h: $$(PYTHON_LIB-$(sdk)) @echo ">>> Build Python fat headers for the $(sdk) SDK" # Copy headers as-is from the first target in the $(sdk) SDK - cp -r $$(PYTHON_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/include/python$(PYTHON_VER) $$(PYTHON_FATINCLUDE-$(sdk)) + mkdir -p $$(shell dirname $$(PYTHON_INCLUDE-$(sdk))) + cp -r $$(PYTHON_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/include/python$(PYTHON_VER) $$(PYTHON_INCLUDE-$(sdk)) # Copy the cross-target header from the patch folder - cp $(PROJECT_DIR)/patch/Python/pyconfig-$(os).h $$(PYTHON_FATINCLUDE-$(sdk))/pyconfig.h + cp $(PROJECT_DIR)/patch/Python/pyconfig-$(os).h $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h # Add the individual headers from each target in an arch-specific name - $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_INSTALL-$$(target))/include/python$(PYTHON_VER)/pyconfig.h $$(PYTHON_FATINCLUDE-$(sdk))/pyconfig-$$(ARCH-$$(target)).h; ) + $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_INSTALL-$$(target))/include/python$(PYTHON_VER)/pyconfig.h $$(PYTHON_INCLUDE-$(sdk))/pyconfig-$$(ARCH-$$(target)).h; ) -$$(PYTHON_FATSTDLIB-$(sdk)): $$(PYTHON_FATLIB-$(sdk)) +$$(PYTHON_STDLIB-$(sdk))/LICENSE.TXT: $$(PYTHON_LIB-$(sdk)) @echo ">>> Build Python stdlib for the $(sdk) SDK" - mkdir -p $$(PYTHON_FATSTDLIB-$(sdk))/lib-dynload + mkdir -p $$(PYTHON_STDLIB-$(sdk))/lib-dynload # Copy stdlib from the first target associated with the $(sdk) SDK - cp -r $$(PYTHON_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/lib/python$(PYTHON_VER)/ $$(PYTHON_FATSTDLIB-$(sdk)) + cp -r $$(PYTHON_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/lib/python$(PYTHON_VER)/ $$(PYTHON_STDLIB-$(sdk)) # Delete the single-SDK parts of the standard library rm -rf \ - $$(PYTHON_FATSTDLIB-$(sdk))/_sysconfigdata__*.py \ - $$(PYTHON_FATSTDLIB-$(sdk))/config-* \ - $$(PYTHON_FATSTDLIB-$(sdk))/lib-dynload/* + $$(PYTHON_STDLIB-$(sdk))/_sysconfigdata__*.py \ + $$(PYTHON_STDLIB-$(sdk))/config-* \ + $$(PYTHON_STDLIB-$(sdk))/lib-dynload/* # Copy the individual _sysconfigdata modules into names that include the architecture - $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_INSTALL-$$(target))/lib/python$(PYTHON_VER)/_sysconfigdata_* $$(PYTHON_FATSTDLIB-$(sdk))/; ) + $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_INSTALL-$$(target))/lib/python$(PYTHON_VER)/_sysconfigdata_* $$(PYTHON_STDLIB-$(sdk))/; ) # Copy the individual config modules directories into names that include the architecture - $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp -r $$(PYTHON_INSTALL-$$(target))/lib/python$(PYTHON_VER)/config-$(PYTHON_VER)-$(sdk)-$$(ARCH-$$(target)) $$(PYTHON_FATSTDLIB-$(sdk))/; ) + $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp -r $$(PYTHON_INSTALL-$$(target))/lib/python$(PYTHON_VER)/config-$(PYTHON_VER)-$(sdk)-$$(ARCH-$$(target)) $$(PYTHON_STDLIB-$(sdk))/; ) # Merge the binary modules from each target in the $(sdk) SDK into a single binary - $$(foreach module,$$(wildcard $$(PYTHON_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/lib/python$(PYTHON_VER)/lib-dynload/*),lipo -create -output $$(PYTHON_FATSTDLIB-$(sdk))/lib-dynload/$$(notdir $$(module)) $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_INSTALL-$$(target))/lib/python$(PYTHON_VER)/lib-dynload/$$(notdir $$(module))); ) + $$(foreach module,$$(wildcard $$(PYTHON_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/lib/python$(PYTHON_VER)/lib-dynload/*),lipo -create -output $$(PYTHON_STDLIB-$(sdk))/lib-dynload/$$(notdir $$(module)) $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_INSTALL-$$(target))/lib/python$(PYTHON_VER)/lib-dynload/$$(notdir $$(module))); ) endif +$(sdk): $$(PYTHON_STDLIB-$(sdk))/LICENSE.TXT ########################################################################### # SDK: Debug @@ -942,23 +599,11 @@ vars-$(sdk): @echo "SDK_SLICE-$(sdk): $$(SDK_SLICE-$(sdk))" @echo "CFLAGS-$(sdk): $$(CFLAGS-$(sdk))" @echo "LDFLAGS-$(sdk): $$(LDFLAGS-$(sdk))" - @echo "BZIP2_MERGE-$(sdk): $$(BZIP2_MERGE-$(sdk))" - @echo "BZIP2_FATLIB-$(sdk): $$(BZIP2_FATLIB-$(sdk))" - @echo "XZ_MERGE-$(sdk): $$(XZ_MERGE-$(sdk))" - @echo "XZ_FATLIB-$(sdk): $$(XZ_FATLIB-$(sdk))" - @echo "OPENSSL_MERGE-$(sdk): $$(OPENSSL_MERGE-$(sdk))" - @echo "OPENSSL_FATINCLUDE-$(sdk): $$(OPENSSL_FATINCLUDE-$(sdk))" - @echo "OPENSSL_SSL_FATLIB-$(sdk): $$(OPENSSL_SSL_FATLIB-$(sdk))" - @echo "OPENSSL_CRYPTO_FATLIB-$(sdk): $$(OPENSSL_CRYPTO_FATLIB-$(sdk))" - @echo "LIBFFI_MERGE-$(sdk): $$(LIBFFI_MERGE-$(sdk))" - @echo "LIBFFI_FATLIB-$(sdk): $$(LIBFFI_FATLIB-$(sdk))" - @echo "PYTHON_MERGE-$(sdk): $$(PYTHON_MERGE-$(sdk))" - @echo "PYTHON_FATLIB-$(sdk): $$(PYTHON_FATLIB-$(sdk))" - @echo "PYTHON_FATINCLUDE-$(sdk): $$(PYTHON_FATINCLUDE-$(sdk))" - @echo "PYTHON_FATSTDLIB-$(sdk): $$(PYTHON_FATSTDLIB-$(sdk))" @echo "PYTHON_SRCDIR-$(sdk): $$(PYTHON_SRCDIR-$(sdk))" @echo "PYTHON_INSTALL-$(sdk): $$(PYTHON_INSTALL-$(sdk))" @echo "PYTHON_LIB-$(sdk): $$(PYTHON_LIB-$(sdk))" + @echo "PYTHON_INCLUDE-$(sdk): $$(PYTHON_INCLUDE-$(sdk))" + @echo "PYTHON_STDLIB-$(sdk): $$(PYTHON_STDLIB-$(sdk))" @echo endef # build-sdk @@ -987,130 +632,23 @@ PYTHON_STDLIB-$(os)=support/$(PYTHON_VER)/$(os)/python-stdlib # Expand the build-sdk macro for all the sdks on this OS (e.g., iphoneos, iphonesimulator) $$(foreach sdk,$$(SDKS-$(os)),$$(eval $$(call build-sdk,$$(sdk),$(os)))) -########################################################################### -# Build: BZip2 -########################################################################### - -BZip2-$(os): $$(foreach sdk,$$(SDKS-$(os)),$$(BZIP2_FATLIB-$$(sdk))) -BZip2-$(os)-wheels: $$(foreach target,$$(TARGETS-$(os)),$$(BZIP2_WHEEL-$$(target))) - -clean-BZip2-$(os): - @echo ">>> Clean BZip2 build products on $(os)" - rm -rf \ - build/$(os)/*/bzip2-$(BZIP2_VERSION) \ - build/$(os)/*/bzip2-$(BZIP2_VERSION).*.log \ - install/$(os)/*/bzip2-$(BZIP2_VERSION) \ - install/$(os)/*/bzip2-$(BZIP2_VERSION).*.log \ - merge/$(os)/*/bzip2-$(BZIP2_VERSION) \ - merge/$(os)/*/bzip2-$(BZIP2_VERSION).*.log \ - wheels/$(os)/bzip2-$(BZIP2_VERSION)-* - -clean-BZip2-$(os)-wheels: - rm -rf \ - install/$(os)/*/bzip2-$(BZIP2_VERSION)/wheel \ - wheels/$(os)/bzip2-$(BZIP2_VERSION)-* - -########################################################################### -# Build: XZ (LZMA) -########################################################################### - -XZ-$(os): $$(foreach sdk,$$(SDKS-$(os)),$$(XZ_FATLIB-$$(sdk))) -XZ-$(os)-wheels: $$(foreach target,$$(TARGETS-$(os)),$$(XZ_WHEEL-$$(target))) - -clean-XZ-$(os): - @echo ">>> Clean XZ build products on $(os)" - rm -rf \ - build/$(os)/*/xz-$(XZ_VERSION) \ - build/$(os)/*/xz-$(XZ_VERSION).*.log \ - install/$(os)/*/xz-$(XZ_VERSION) \ - install/$(os)/*/xz-$(XZ_VERSION).*.log \ - merge/$(os)/*/xz-$(XZ_VERSION) \ - merge/$(os)/*/xz-$(XZ_VERSION).*.log \ - wheels/$(os)/xz-$(XZ_VERSION)-* - -clean-XZ-$(os)-wheels: - rm -rf \ - install/$(os)/*/xz-$(XZ_VERSION)/wheel \ - wheels/$(os)/xz-$(XZ_VERSION)-* - -########################################################################### -# Build: OpenSSL -########################################################################### - -OpenSSL-$(os): $$(foreach sdk,$$(SDKS-$(os)),$$(OPENSSL_FATINCLUDE-$$(sdk)) $$(OPENSSL_SSL_FATLIB-$$(sdk)) $$(OPENSSL_CRYPTO_FATLIB-$$(sdk))) -OpenSSL-$(os)-wheels: $$(foreach target,$$(TARGETS-$(os)),$$(OPENSSL_WHEEL-$$(target))) - -clean-OpenSSL-$(os): - @echo ">>> Clean OpenSSL build products on $(os)" - rm -rf \ - build/$(os)/*/openssl-$(OPENSSL_VERSION) \ - build/$(os)/*/openssl-$(OPENSSL_VERSION).*.log \ - install/$(os)/*/openssl-$(OPENSSL_VERSION) \ - install/$(os)/*/openssl-$(OPENSSL_VERSION).*.log \ - merge/$(os)/*/openssl-$(OPENSSL_VERSION) \ - merge/$(os)/*/openssl-$(OPENSSL_VERSION).*.log \ - wheels/$(os)/openssl-$(OPENSSL_VERSION)-* - -clean-OpenSSL-$(os)-wheels: - rm -rf \ - install/$(os)/*/openssl-$(OPENSSL_VERSION)/wheel \ - wheels/$(os)/openssl-$(OPENSSL_VERSION)-* - -########################################################################### -# Build: libFFI -########################################################################### - -# macOS uses the system-provided libFFI, so there's no need to package -# a libFFI framework for macOS. -ifneq ($(os),macOS) - -$$(LIBFFI_SRCDIR-$(os))/darwin_common/include/ffi.h: downloads/libffi-$(LIBFFI_VERSION).tar.gz $(HOST_PYTHON_EXE) - @echo ">>> Unpack and configure libFFI sources on $(os)" - mkdir -p $$(LIBFFI_SRCDIR-$(os)) - tar zxf $$< --strip-components 1 -C $$(LIBFFI_SRCDIR-$(os)) - # Patch the build to add support for new platforms - cd $$(LIBFFI_SRCDIR-$(os)) && patch -p1 < $(PROJECT_DIR)/patch/libffi-$(LIBFFI_VERSION).patch - # Configure the build - cd $$(LIBFFI_SRCDIR-$(os)) && \ - $(PROJECT_DIR)/$(HOST_PYTHON_EXE) generate-darwin-source-and-headers.py --only-$(shell echo $(os) | tr '[:upper:]' '[:lower:]') \ - 2>&1 | tee -a ../libffi-$(LIBFFI_VERSION).config.log - -endif - -libFFI-$(os): $$(foreach sdk,$$(SDKS-$(os)),$$(LIBFFI_FATLIB-$$(sdk))) -libFFI-$(os)-wheels: $$(foreach target,$$(TARGETS-$(os)),$$(LIBFFI_WHEEL-$$(target))) - -clean-libFFI-$(os): - @echo ">>> Clean libFFI build products on $(os)" - rm -rf \ - build/$(os)/libffi-$(LIBFFI_VERSION) \ - build/$(os)/libffi-$(LIBFFI_VERSION).*.log \ - merge/$(os)/libffi-$(LIBFFI_VERSION) \ - merge/$(os)/libffi-$(LIBFFI_VERSION).*.log \ - wheels/$(os)/libffi-$(LIBFFI_VERSION)-* - -clean-libFFI-$(os)-wheels: - rm -rf \ - build/$(os)/libffi-$(LIBFFI_VERSION)/build_*/wheel \ - wheels/$(os)/libffi-$(LIBFFI_VERSION)-* - ########################################################################### # Build: Python ########################################################################### $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ - $$(foreach sdk,$$(SDKS-$(os)),$$(PYTHON_FATLIB-$$(sdk)) $$(PYTHON_FATINCLUDE-$$(sdk))) + $$(foreach sdk,$$(SDKS-$(os)),$$(PYTHON_LIB-$$(sdk)) $$(PYTHON_INCLUDE-$$(sdk))/Python.h) @echo ">>> Create Python.XCFramework on $(os)" mkdir -p $$(dir $$(PYTHON_XCFRAMEWORK-$(os))) xcodebuild -create-xcframework \ - -output $$(PYTHON_XCFRAMEWORK-$(os)) $$(foreach sdk,$$(SDKS-$(os)),-library $$(PYTHON_FATLIB-$$(sdk)) -headers $$(PYTHON_FATINCLUDE-$$(sdk))) \ + -output $$(PYTHON_XCFRAMEWORK-$(os)) $$(foreach sdk,$$(SDKS-$(os)),-library $$(PYTHON_LIB-$$(sdk)) -headers $$(PYTHON_INCLUDE-$$(sdk))) \ 2>&1 | tee -a support/$(PYTHON_VER)/python-$(os).xcframework.log $$(PYTHON_STDLIB-$(os))/VERSIONS: \ - $$(foreach sdk,$$(SDKS-$(os)),$$(PYTHON_FATSTDLIB-$$(sdk))) + $$(foreach sdk,$$(SDKS-$(os)),$$(PYTHON_STDLIB-$$(sdk))/LICENSE.TXT) @echo ">>> Create Python stdlib on $(os)" # Copy stdlib from first SDK in $(os) - cp -r $$(PYTHON_FATSTDLIB-$$(firstword $$(SDKS-$(os)))) $$(PYTHON_STDLIB-$(os)) + cp -r $$(PYTHON_STDLIB-$$(firstword $$(SDKS-$(os)))) $$(PYTHON_STDLIB-$(os)) # Delete the single-SDK stdlib artefacts from $(os) rm -rf \ @@ -1119,13 +657,13 @@ $$(PYTHON_STDLIB-$(os))/VERSIONS: \ $$(PYTHON_STDLIB-$(os))/lib-dynload/* # Copy the config-* contents from every SDK in $(os) into the support folder. - $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_FATSTDLIB-$$(sdk))/config-$(PYTHON_VER)-* $$(PYTHON_STDLIB-$(os)); ) + $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_STDLIB-$$(sdk))/config-$(PYTHON_VER)-* $$(PYTHON_STDLIB-$(os)); ) # Copy the _sysconfigdata modules from every SDK in $(os) into the support folder. - $$(foreach sdk,$$(SDKS-$(os)),cp $$(PYTHON_FATSTDLIB-$$(sdk))/_sysconfigdata__*.py $$(PYTHON_STDLIB-$(os)); ) + $$(foreach sdk,$$(SDKS-$(os)),cp $$(PYTHON_STDLIB-$$(sdk))/_sysconfigdata__*.py $$(PYTHON_STDLIB-$(os)); ) # Copy the lib-dynload contents from every SDK in $(os) into the support folder. - $$(foreach sdk,$$(SDKS-$(os)),cp $$(PYTHON_FATSTDLIB-$$(sdk))/lib-dynload/* $$(PYTHON_STDLIB-$(os))/lib-dynload; ) + $$(foreach sdk,$$(SDKS-$(os)),cp $$(PYTHON_STDLIB-$$(sdk))/lib-dynload/* $$(PYTHON_STDLIB-$(os))/lib-dynload; ) @echo ">>> Create VERSIONS file for $(os)" echo "Python version: $(PYTHON_VERSION) " > support/$(PYTHON_VER)/$(os)/VERSIONS @@ -1153,37 +691,23 @@ dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz: \ # Build a distributable tarball tar zcvf $$@ -X patch/Python/release.common.exclude -X patch/Python/release.$(os).exclude -C support/$(PYTHON_VER)/$(os) `ls -A support/$(PYTHON_VER)/$(os)/` -Python-$(os): dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz - -clean-Python-$(os): +clean-$(os): @echo ">>> Clean Python build products on $(os)" rm -rf \ build/$(os)/*/python-$(PYTHON_VER)* \ build/$(os)/*/python-$(PYTHON_VER)*.*.log \ install/$(os)/*/python-$(PYTHON_VER)* \ install/$(os)/*/python-$(PYTHON_VER)*.*.log \ - merge/$(os)/*/python-$(PYTHON_VER)* \ - merge/$(os)/*/python-$(PYTHON_VER)*.*.log \ support/$(PYTHON_VER)/$(os) \ dist/Python-$(PYTHON_VER)-$(os)-* -dev-clean-Python-$(os): +dev-clean-$(os): @echo ">>> Partially clean Python build products on $(os) so that local code modifications can be made" rm -rf \ build/$(os)/*/Python-$(PYTHON_VERSION)/python.exe \ build/$(os)/*/python-$(PYTHON_VERSION).*.log \ install/$(os)/*/python-$(PYTHON_VERSION) \ install/$(os)/*/python-$(PYTHON_VERSION).*.log \ - merge/$(os)/*/python-$(PYTHON_VERSION) \ - merge/$(os)/*/python-$(PYTHON_VERSION).*.log \ - support/$(PYTHON_VER)/$(os) \ - dist/Python-$(PYTHON_VER)-$(os)-* - -merge-clean-Python-$(os): - @echo ">>> Partially clean Python build products on $(os) so that merge modifications can be made" - rm -rf \ - merge/$(os)/*/python-$(PYTHON_VERSION) \ - merge/$(os)/*/python-$(PYTHON_VERSION).*.log \ support/$(PYTHON_VER)/$(os) \ dist/Python-$(PYTHON_VER)-$(os)-* @@ -1191,21 +715,7 @@ merge-clean-Python-$(os): # Build ########################################################################### -$(os)-wheels: $(foreach dep,$(DEPENDENCIES),$(dep)-$(os)-wheels) -$(os): dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz $(os)-wheels - -clean-$(os)-wheels: $(foreach dep,$(DEPENDENCIES),clean-$(dep)-$(os)-wheels) - @echo ">>> Clean $(os) wheels" - rm -rf wheel/$(os) - -clean-$(os): clean-$(os)-wheels - @echo ">>> Clean $(os) build products" - rm -rf \ - build/$(os) \ - install/$(os) \ - merge/$(os) \ - dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz \ - dist/Python-$(PYTHON_VER)-$(os)-support.test-$(BUILD_NUMBER).tar.gz +$(os): dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz ########################################################################### # Build: Debug @@ -1214,49 +724,22 @@ clean-$(os): clean-$(os)-wheels vars-$(os): $$(foreach target,$$(TARGETS-$(os)),vars-$$(target)) $$(foreach sdk,$$(SDKS-$(os)),vars-$$(sdk)) @echo ">>> Environment variables for $(os)" @echo "SDKS-$(os): $$(SDKS-$(os))" - @echo "LIBFFI_SRCDIR-$(os): $$(LIBFFI_SRCDIR-$(os))" @echo "LIBPYTHON_XCFRAMEWORK-$(os): $$(LIBPYTHON_XCFRAMEWORK-$(os))" @echo "PYTHON_XCFRAMEWORK-$(os): $$(PYTHON_XCFRAMEWORK-$(os))" @echo endef # build -$(BDIST_WHEEL): - @echo ">>> Ensure the macOS python install has pip and wheel" - $(HOST_PYTHON_EXE) -m ensurepip - PIP_REQUIRE_VIRTUALENV=false $(HOST_PYTHON_EXE) -m pip install wheel - -# Binary support wheels -wheels: $(foreach dep,$(DEPENDENCIES),$(dep)-wheels) -clean-wheels: $(foreach dep,$(DEPENDENCIES),clean-$(dep)-wheels) - # Dump environment variables (for debugging purposes) vars: $(foreach os,$(OS_LIST),vars-$(os)) + @echo ">>> Environment variables for $(os)" + @echo "HOST_ARCH: $(HOST_ARCH)" + @echo "HOST_PYTHON: $(HOST_PYTHON)" + @echo # Expand cross-platform build and clean targets for each output product -BZip2: $(foreach os,$(OS_LIST),BZip2-$(os)) -BZip2-wheels: $(foreach os,$(OS_LIST),BZip2-$(os)-wheels) -clean-BZip2: $(foreach os,$(OS_LIST),clean-BZip2-$(os)) -clean-BZip2-wheels: $(foreach os,$(OS_LIST),clean-BZip2-$(os)-wheels) - -XZ: $(foreach os,$(OS_LIST),XZ-$(os)) -XZ-wheels: $(foreach os,$(OS_LIST),XZ-$(os)-wheels) -clean-XZ: $(foreach os,$(OS_LIST),clean-XZ-$(os)) -clean-XZ-wheels: $(foreach os,$(OS_LIST),clean-XZ-$(os)-wheels) - -OpenSSL: $(foreach os,$(OS_LIST),OpenSSL-$(os)) -OpenSSL-wheels: $(foreach os,$(OS_LIST),OpenSSL-$(os)-wheels) -clean-OpenSSL: $(foreach os,$(OS_LIST),clean-OpenSSL-$(os)) -clean-OpenSSL-wheels: $(foreach os,$(OS_LIST),clean-OpenSSL-$(os)-wheels) - -libFFI: $(foreach os,$(OS_LIST),libFFI-$(os)) -libFFI-wheels: $(foreach os,$(OS_LIST),libFFI-$(os)-wheels) -clean-libFFI: $(foreach os,$(OS_LIST),clean-libFFI-$(os)) -clean-libFFI-wheels: $(foreach os,$(OS_LIST),clean-libFFI-$(os)-wheels) - -Python: $(foreach os,$(OS_LIST),Python-$(os)) -clean-Python: $(foreach os,$(OS_LIST),clean-Python-$(os)) -dev-clean-Python: $(foreach os,$(OS_LIST),dev-clean-Python-$(os)) +clean: $(foreach os,$(OS_LIST),clean-$(os)) +dev-clean: $(foreach os,$(OS_LIST),dev-clean-$(os)) # Expand the build macro for every OS $(foreach os,$(OS_LIST),$(eval $(call build,$(os)))) diff --git a/README.rst b/README.rst index 7972ca55..dff2a8aa 100644 --- a/README.rst +++ b/README.rst @@ -93,6 +93,10 @@ Each support package contains: * ``VERSIONS``, a text file describing the specific versions of code used to build the support package; +* ``bin``, a folder containing shell aliases for the compilers that are needed + to build packages. This is required because Xcode uses the ``xcrun`` alias to + dynamically generate the name of binaries, but a lot of C tooling expects that ``CC`` + will not contain spaces. * ``platform-site``, a folder that contains site customization scripts that can be used to make your local Python install look like it is an on-device install for each of the underlying target architectures supported by the platform. This is needed because when diff --git a/patch/libffi-3.4.4.patch b/patch/libffi-3.4.4.patch deleted file mode 100644 index 6e143449..00000000 --- a/patch/libffi-3.4.4.patch +++ /dev/null @@ -1,174 +0,0 @@ -diff --git a/config.sub b/config.sub -old mode 100644 -new mode 100755 -index dba16e8..630b5d9 ---- a/config.sub -+++ b/config.sub -@@ -1126,7 +1126,7 @@ case $cpu-$vendor in - xscale-* | xscalee[bl]-*) - cpu=`echo "$cpu" | sed 's/^xscale/arm/'` - ;; -- arm64-* | aarch64le-*) -+ arm64-* | arm64_32-* | aarch64le-*) - cpu=aarch64 - ;; - -@@ -1728,7 +1728,7 @@ case $os in - | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ - | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ - | hiux* | abug | nacl* | netware* | windows* \ -- | os9* | macos* | osx* | ios* \ -+ | os9* | macos* | osx* | ios* | watchos* | tvos* \ - | mpw* | magic* | mmixware* | mon960* | lnews* \ - | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ - | aos* | aros* | cloudabi* | sortix* | twizzler* \ -@@ -1790,6 +1790,8 @@ case $kernel-$os in - ;; - os2-emx) - ;; -+ ios*-simulator | tvos*-simulator | watchos*-simulator) -+ ;; - *-eabi* | *-gnueabi*) - ;; - -*) -diff --git a/generate-darwin-source-and-headers.py b/generate-darwin-source-and-headers.py -index 5b60ccc..c801dc0 100755 ---- a/generate-darwin-source-and-headers.py -+++ b/generate-darwin-source-and-headers.py -@@ -48,7 +48,6 @@ class armv7_platform(Platform): - - - class ios_simulator_i386_platform(i386_platform): -- triple = 'i386-apple-darwin11' - target = 'i386-apple-ios-simulator' - directory = 'darwin_ios' - sdk = 'iphonesimulator' -@@ -56,7 +55,6 @@ class ios_simulator_i386_platform(i386_platform): - - - class ios_simulator_x86_64_platform(x86_64_platform): -- triple = 'x86_64-apple-darwin13' - target = 'x86_64-apple-ios-simulator' - directory = 'darwin_ios' - sdk = 'iphonesimulator' -@@ -64,7 +62,6 @@ class ios_simulator_x86_64_platform(x86_64_platform): - - - class ios_simulator_arm64_platform(arm64_platform): -- triple = 'aarch64-apple-darwin20' - target = 'arm64-apple-ios-simulator' - directory = 'darwin_ios' - sdk = 'iphonesimulator' -@@ -72,7 +69,6 @@ class ios_simulator_arm64_platform(arm64_platform): - - - class ios_device_armv7_platform(armv7_platform): -- triple = 'arm-apple-darwin11' - target = 'armv7-apple-ios' - directory = 'darwin_ios' - sdk = 'iphoneos' -@@ -80,7 +76,6 @@ class ios_device_armv7_platform(armv7_platform): - - - class ios_device_arm64_platform(arm64_platform): -- triple = 'aarch64-apple-darwin13' - target = 'arm64-apple-ios' - directory = 'darwin_ios' - sdk = 'iphoneos' -@@ -88,7 +83,6 @@ class ios_device_arm64_platform(arm64_platform): - - - class desktop_x86_64_platform(x86_64_platform): -- triple = 'x86_64-apple-darwin10' - target = 'x86_64-apple-macos' - directory = 'darwin_osx' - sdk = 'macosx' -@@ -96,7 +90,6 @@ class desktop_x86_64_platform(x86_64_platform): - - - class desktop_arm64_platform(arm64_platform): -- triple = 'aarch64-apple-darwin20' - target = 'arm64-apple-macos' - directory = 'darwin_osx' - sdk = 'macosx' -@@ -104,7 +97,6 @@ class desktop_arm64_platform(arm64_platform): - - - class tvos_simulator_x86_64_platform(x86_64_platform): -- triple = 'x86_64-apple-darwin13' - target = 'x86_64-apple-tvos-simulator' - directory = 'darwin_tvos' - sdk = 'appletvsimulator' -@@ -112,7 +104,6 @@ class tvos_simulator_x86_64_platform(x86_64_platform): - - - class tvos_simulator_arm64_platform(arm64_platform): -- triple = 'aarch64-apple-darwin20' - target = 'arm64-apple-tvos-simulator' - directory = 'darwin_tvos' - sdk = 'appletvsimulator' -@@ -120,7 +111,6 @@ class tvos_simulator_arm64_platform(arm64_platform): - - - class tvos_device_arm64_platform(arm64_platform): -- triple = 'aarch64-apple-darwin13' - target = 'arm64-apple-tvos' - directory = 'darwin_tvos' - sdk = 'appletvos' -@@ -128,7 +118,6 @@ class tvos_device_arm64_platform(arm64_platform): - - - class watchos_simulator_i386_platform(i386_platform): -- triple = 'i386-apple-darwin11' - target = 'i386-apple-watchos-simulator' - directory = 'darwin_watchos' - sdk = 'watchsimulator' -@@ -136,7 +125,6 @@ class watchos_simulator_i386_platform(i386_platform): - - - class watchos_simulator_x86_64_platform(x86_64_platform): -- triple = 'x86_64-apple-darwin13' - target = 'x86_64-apple-watchos-simulator' - directory = 'darwin_watchos' - sdk = 'watchsimulator' -@@ -144,7 +132,6 @@ class watchos_simulator_x86_64_platform(x86_64_platform): - - - class watchos_simulator_arm64_platform(arm64_platform): -- triple = 'aarch64-apple-darwin20' - target = 'arm64-apple-watchos-simulator' - directory = 'darwin_watchos' - sdk = 'watchsimulator' -@@ -152,7 +139,6 @@ class watchos_simulator_arm64_platform(arm64_platform): - - - class watchos_device_armv7k_platform(armv7_platform): -- triple = 'arm-apple-darwin11' - target = 'armv7k-apple-watchos' - directory = 'darwin_watchos' - sdk = 'watchos' -@@ -161,7 +147,6 @@ class watchos_device_armv7k_platform(armv7_platform): - - - class watchos_device_arm64_32_platform(arm64_platform): -- triple = 'aarch64-apple-darwin13' - target = 'arm64_32-apple-watchos' - directory = 'darwin_watchos' - sdk = 'watchos' -@@ -229,7 +214,15 @@ def build_target(platform, platform_headers): - working_dir = os.getcwd() - try: - os.chdir(build_dir) -- subprocess.check_call(['../configure', '-host', platform.triple], env=env) -+ subprocess.check_call( -+ [ -+ "../configure", -+ f"--host={platform.target}", -+ ] + ( -+ [] if platform.sdk == "macosx" else [f"--build={os.uname().machine}-apple-darwin"] -+ ), -+ env=env -+ ) - finally: - os.chdir(working_dir) - diff --git a/patch/xz-5.4.4.patch b/patch/xz-5.4.4.patch deleted file mode 100644 index a964e51c..00000000 --- a/patch/xz-5.4.4.patch +++ /dev/null @@ -1,30 +0,0 @@ -diff -ur xz-5.4.2-orig/build-aux/config.sub xz-5.4.2/build-aux/config.sub ---- xz-5.4.2-orig/build-aux/config.sub 2023-03-19 00:08:55 -+++ xz-5.4.2/build-aux/config.sub 2023-04-13 12:54:51 -@@ -1126,7 +1126,7 @@ - xscale-* | xscalee[bl]-*) - cpu=`echo "$cpu" | sed 's/^xscale/arm/'` - ;; -- arm64-* | aarch64le-*) -+ arm64-* | arm64_32-* | aarch64le-*) - cpu=aarch64 - ;; - -@@ -1728,7 +1728,7 @@ - | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ - | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ - | hiux* | abug | nacl* | netware* | windows* \ -- | os9* | macos* | osx* | ios* \ -+ | os9* | macos* | osx* | ios* | tvos* | watchos* \ - | mpw* | magic* | mmixware* | mon960* | lnews* \ - | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ - | aos* | aros* | cloudabi* | sortix* | twizzler* \ -@@ -1791,6 +1791,8 @@ - os2-emx) - ;; - *-eabi* | *-gnueabi*) -+ ;; -+ ios*-simulator | tvos*-simulator | watchos*-simulator) - ;; - -*) - # Blank kernel with real OS is always fine. From 4b7189e184893155b865c843a0de5394a5d7e835 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 27 Oct 2023 11:17:53 +0800 Subject: [PATCH 002/113] Updated Actions workflows to use modern APIs. --- .github/dependabot.yml | 10 +++ .github/workflows/publish.yaml | 35 ++++---- .github/workflows/release.yaml | 143 ++++++++++++++++++--------------- Makefile | 2 +- 4 files changed, 106 insertions(+), 84 deletions(-) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..ecbb02f7 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + # Check for updates on Sunday, 8PM UTC + interval: "weekly" + day: "sunday" + time: "20:00" diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index fd2c9623..7fb8ab0e 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -8,24 +8,27 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python environment uses: actions/setup-python@v1 with: python-version: "3.X" - name: Set build variables + id: build-vars env: TAG_NAME: ${{ github.ref }} run: | export TAG=$(basename $TAG_NAME) echo "TAG=${TAG}" - export TAG_VERSION="${TAG%-*}" - export TAG_BUILD="${TAG#*-}" - echo "PY_VERSION=${TAG_VERSION}" - echo "BUILD_NUMBER=${TAG_BUILD}" - echo "TAG=${TAG}" >> $GITHUB_ENV - echo "PY_VERSION=${TAG_VERSION}" >> $GITHUB_ENV - echo "BUILD_NUMBER=${TAG_BUILD}" >> $GITHUB_ENV + export PYTHON_VER="${TAG%-*}" + export BUILD_NUMBER="${TAG#*-}" + + echo "PYTHON_VER=${PYTHON_VER}" + echo "BUILD_NUMBER=${BUILD_NUMBER}" + + echo "TAG=${TAG}" >> ${GITHUB_OUTPUT} + echo "PYTHON_VER=${PYTHON_VER}" >> ${GITHUB_OUTPUT} + echo "BUILD_NUMBER=${BUILD_NUMBER}" >> ${GITHUB_OUTPUT} - name: Update Release Asset to S3 env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} @@ -35,14 +38,14 @@ jobs: python -m pip install -U setuptools python -m pip install awscli # macOS build - curl -o macOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ env.TAG }}/Python-${{ env.PY_VERSION }}-macOS-support.${{ env.BUILD_NUMBER }}.tar.gz - aws s3 cp macOS-artefact.tar.gz s3://briefcase-support/python/${{ env.PY_VERSION }}/macOS/Python-${{ env.PY_VERSION }}-macOS-support.${{ env.BUILD_NUMBER }}.tar.gz + curl -o macOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.output.TAG }}/Python-${{ steps.build-vars.output.PY_VERSION }}-macOS-support.${{ steps.build-vars.output.BUILD_NUMBER }}.tar.gz + aws s3 cp macOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.output.PY_VERSION }}/macOS/Python-${{ steps.build-vars.output.PY_VERSION }}-macOS-support.${{ steps.build-vars.output.BUILD_NUMBER }}.tar.gz # iOS build - curl -o iOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ env.TAG }}/Python-${{ env.PY_VERSION }}-iOS-support.${{ env.BUILD_NUMBER }}.tar.gz - aws s3 cp iOS-artefact.tar.gz s3://briefcase-support/python/${{ env.PY_VERSION }}/iOS/Python-${{ env.PY_VERSION }}-iOS-support.${{ env.BUILD_NUMBER }}.tar.gz + curl -o iOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.output.TAG }}/Python-${{ steps.build-vars.output.PY_VERSION }}-iOS-support.${{ steps.build-vars.output.BUILD_NUMBER }}.tar.gz + aws s3 cp iOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.output.PY_VERSION }}/iOS/Python-${{ steps.build-vars.output.PY_VERSION }}-iOS-support.${{ steps.build-vars.output.BUILD_NUMBER }}.tar.gz # tvOS build - curl -o tvOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ env.TAG }}/Python-${{ env.PY_VERSION }}-tvOS-support.${{ env.BUILD_NUMBER }}.tar.gz - aws s3 cp tvOS-artefact.tar.gz s3://briefcase-support/python/${{ env.PY_VERSION }}/tvOS/Python-${{ env.PY_VERSION }}-tvOS-support.${{ env.BUILD_NUMBER }}.tar.gz + curl -o tvOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.output.TAG }}/Python-${{ steps.build-vars.output.PY_VERSION }}-tvOS-support.${{ steps.build-vars.output.BUILD_NUMBER }}.tar.gz + aws s3 cp tvOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.output.PY_VERSION }}/tvOS/Python-${{ steps.build-vars.output.PY_VERSION }}-tvOS-support.${{ steps.build-vars.output.BUILD_NUMBER }}.tar.gz # watchOS build - curl -o watchOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ env.TAG }}/Python-${{ env.PY_VERSION }}-watchOS-support.${{ env.BUILD_NUMBER }}.tar.gz - aws s3 cp watchOS-artefact.tar.gz s3://briefcase-support/python/${{ env.PY_VERSION }}/watchOS/Python-${{ env.PY_VERSION }}-watchOS-support.${{ env.BUILD_NUMBER }}.tar.gz + curl -o watchOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.output.TAG }}/Python-${{ steps.build-vars.output.PY_VERSION }}-watchOS-support.${{ steps.build-vars.output.BUILD_NUMBER }}.tar.gz + aws s3 cp watchOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.output.PY_VERSION }}/watchOS/Python-${{ steps.build-vars.output.PY_VERSION }}-watchOS-support.${{ steps.build-vars.output.BUILD_NUMBER }}.tar.gz diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 1dd93032..ea58ebe9 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -8,89 +8,98 @@ on: - '*-b*' jobs: - make-release: - runs-on: ubuntu-latest - steps: - - name: Set build variables - env: - TAG_NAME: ${{ github.ref }} - run: | - export TAG=$(basename $TAG_NAME) - echo "TAG=${TAG}" - export TAG_VERSION="${TAG%-*}" - export TAG_BUILD="${TAG#*-}" - echo "PY_VERSION=${TAG_VERSION}" - echo "BUILD_NUMBER=${TAG_BUILD}" - echo "TAG=${TAG}" >> $GITHUB_ENV - echo "PY_VERSION=${TAG_VERSION}" >> $GITHUB_ENV - echo "BUILD_NUMBER=${TAG_BUILD}" >> $GITHUB_ENV - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref }} - release_name: ${{ github.ref }} - draft: true - prerelease: false - body: | - Build ${{ env.BUILD_NUMBER }} of the BeeWare support package for Python ${{ env.PY_VERSION }}. - - Includes: - * Python ${{ env.PY_VERSION }}.? - * OpenSSL 3.1.2 - * BZip2 1.0.8 - * XZ 5.4.4 - * LibFFI 3.4.4 - outputs: - upload_url: ${{ steps.create_release.outputs.upload_url }} build: runs-on: macOS-latest - needs: make-release + outputs: + TAG: ${{ steps.build-vars.outputs.TAG }} + PYTHON_VER: ${{ steps.build-vars.outputs.PYTHON_VER }} + BUILD_NUMBER: ${{ steps.build-vars.outputs.BUILD_NUMBER }} + PYTHON_VERSION: ${{ steps.version-details.outputs.PYTHON_VERSION }} + BZIP2_VERSION: ${{ steps.version-details.outputs.BZIP2_VERSION }} + XZ_VERSION: ${{ steps.version-details.outputs.XZ_VERSION }} + LIBFFI_VERSION: ${{ steps.version-details.outputs.LIBFFI_VERSION }} + OPENSSL_VERSION: ${{ steps.version-details.outputs.OPENSSL_VERSION }} strategy: - max-parallel: 4 matrix: target: ['macOS', 'iOS', 'tvOS', 'watchOS'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set build variables + id: build-vars env: TAG_NAME: ${{ github.ref }} run: | export TAG=$(basename $TAG_NAME) echo "TAG=${TAG}" - export TAG_VERSION="${TAG%-*}" - export TAG_BUILD="${TAG#*-}" - echo "PY_VERSION=${TAG_VERSION}" - echo "BUILD_NUMBER=${TAG_BUILD}" - echo "TAG=${TAG}" >> $GITHUB_ENV - echo "PY_VERSION=${TAG_VERSION}" >> $GITHUB_ENV - echo "BUILD_NUMBER=${TAG_BUILD}" >> $GITHUB_ENV + export PYTHON_VER="${TAG%-*}" + export BUILD_NUMBER="${TAG#*-}" + + echo "PYTHON_VER=${PYTHON_VER}" + echo "BUILD_NUMBER=${BUILD_NUMBER}" + + echo "TAG=${TAG}" >> ${GITHUB_OUTPUT} + echo "PYTHON_VER=${PYTHON_VER}" >> ${GITHUB_OUTPUT} + echo "BUILD_NUMBER=${BUILD_NUMBER}" >> ${GITHUB_OUTPUT} - name: Set up Python - uses: actions/setup-python@v3.1.2 + uses: actions/setup-python@v4.7.1 with: - python-version: ${{ env.TAG_VERSION }} + python-version: "${{ steps.build-vars.output.PYTHON_VER }}" - name: Build ${{ matrix.target }} - env: - BUILD_NUMBER: ${{ env.BUILD_NUMBER }} run: | - # 2020-06-24: The Homebrew copy of gettext leaks into the macOS build - # if it is present. Uninstall gettext to make sure that doesn't happen. - # 2021-01-02: Uninstall curl as well. We need curl to download the - # source bundles, but Homebrew's curl has a runtime dependency on - # gettext. However macOS provides a version of curl that works fine. - brew uninstall --ignore-dependencies gettext curl - # Do the build for the requested target. - make -e ${{ matrix.target }} - - name: Upload ${{ matrix.target }} release asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + make ${{ matrix.target }} BUILD_NUMBER=${{ steps.build-vars.output.BUILD_NUMBER }} + + - name: Extract version details + id: version-details + run: | + export PYTHON_VERSION=$(grep "Python version:" support/${{ steps.build-vars.output.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 3) + export BZIP2_VERSION=$(grep "BZip2:" support/${{ steps.build-vars.output.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) + export XZ_VERSION=$(grep "XZ:" support/${{ steps.build-vars.output.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) + export OPENSSL_VERSION=$(grep "OpenSSL:" support/${{ steps.build-vars.output.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) + export LIBFFI_VERSION=$(grep "libFFI:" support/${{ steps.build-vars.output.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) + + echo "PYTHON_VERSION=${PYTHON_VERSION}" + echo "BZIP2_VERSION=${BZIP2_VERSION}" + echo "XZ_VERSION=${XZ_VERSION}" + echo "OPENSSL_VERSION=${OPENSSL_VERSION}" + echo "LIBFFI_VERSION=${LIBFFI_VERSION}" + + echo "PYTHON_VERSION=${PYTHON_VERSION}" >> ${GITHUB_OUTPUT} + echo "BZIP2_VERSION=${BZIP2_VERSION}" >> ${GITHUB_OUTPUT} + echo "XZ_VERSION=${XZ_VERSION}" >> ${GITHUB_OUTPUT} + echo "OPENSSL_VERSION=${OPENSSL_VERSION}" >> ${GITHUB_OUTPUT} + echo "LIBFFI_VERSION=${LIBFFI_VERSION}" >> ${GITHUB_OUTPUT} + - name: Upload build artifact + uses: actions/upload-artifact@v3.1.3 with: - upload_url: ${{ needs.make-release.outputs.upload_url }} - asset_path: ./dist/Python-${{ env.PY_VERSION }}-${{ matrix.target }}-support.${{ env.BUILD_NUMBER }}.tar.gz - asset_name: Python-${{ env.PY_VERSION }}-${{ matrix.target }}-support.${{ env.BUILD_NUMBER }}.tar.gz - asset_content_type: application/gzip + name: dist + path: "dist" + if-no-files-found: error + + make-release: + runs-on: ubuntu-latest + needs: build + steps: + - name: Get build artifacts + uses: actions/download-artifact@v3.0.2 + with: + name: dist + path: dist + + - name: Create Release + uses: ncipollo/release-action@v1.13.0 + with: + name: ${{ needs.build.outputs.PYTHON_VER }}-${{ needs.build.outputs.BUILD_NUMBER }} + tag: ${{ needs.build.outputs.PYTHON_VER }}-${{ needs.build.outputs.BUILD_NUMBER }} + draft: true + body: | + Build ${{ needs.build.outputs.BUILD_NUMBER }} of the BeeWare support package for Python ${{ needs.build.outputs.PYTHON_VER }}. + + Includes: + * Python ${{ needs.build.outputs.PYTHON_VERSION }} + * OpenSSL ${{ needs.build.outputs.OPENSSL_VERSION }} + * BZip2 ${{ needs.build.outputs.BZIP2_VERSION }} + * XZ ${{ needs.build.outputs.XZ_VERSION }} + * LibFFI ${{ needs.build.outputs.LIBFFI_VERSION }} + artifacts: "dist/*" diff --git a/Makefile b/Makefile index d4f27698..cbd4f9fe 100644 --- a/Makefile +++ b/Makefile @@ -671,7 +671,7 @@ $$(PYTHON_STDLIB-$(os))/VERSIONS: \ echo "Min $(os) version: $$(VERSION_MIN-$(os))" >> support/$(PYTHON_VER)/$(os)/VERSIONS echo "---------------------" >> support/$(PYTHON_VER)/$(os)/VERSIONS ifeq ($(os),macOS) - echo "libFFI: macOS native" >> support/$(PYTHON_VER)/$(os)/VERSIONS + echo "libFFI: built-in" >> support/$(PYTHON_VER)/$(os)/VERSIONS else echo "libFFI: $(LIBFFI_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS endif From c3cefc94047f37e2b4d43a0052dcb1b734db26eb Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 27 Oct 2023 11:25:33 +0800 Subject: [PATCH 003/113] Use -dev python releases in dev branch. --- .github/workflows/publish.yaml | 3 +++ .github/workflows/release.yaml | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 7fb8ab0e..3bbd1793 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -9,10 +9,12 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Set up Python environment uses: actions/setup-python@v1 with: python-version: "3.X" + - name: Set build variables id: build-vars env: @@ -29,6 +31,7 @@ jobs: echo "TAG=${TAG}" >> ${GITHUB_OUTPUT} echo "PYTHON_VER=${PYTHON_VER}" >> ${GITHUB_OUTPUT} echo "BUILD_NUMBER=${BUILD_NUMBER}" >> ${GITHUB_OUTPUT} + - name: Update Release Asset to S3 env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index ea58ebe9..61aa8e35 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -25,6 +25,7 @@ jobs: target: ['macOS', 'iOS', 'tvOS', 'watchOS'] steps: - uses: actions/checkout@v4 + - name: Set build variables id: build-vars env: @@ -41,10 +42,12 @@ jobs: echo "TAG=${TAG}" >> ${GITHUB_OUTPUT} echo "PYTHON_VER=${PYTHON_VER}" >> ${GITHUB_OUTPUT} echo "BUILD_NUMBER=${BUILD_NUMBER}" >> ${GITHUB_OUTPUT} + - name: Set up Python uses: actions/setup-python@v4.7.1 with: - python-version: "${{ steps.build-vars.output.PYTHON_VER }}" + python-version: "${{ steps.build-vars.output.PYTHON_VER }}-dev" + - name: Build ${{ matrix.target }} run: | # Do the build for the requested target. @@ -70,6 +73,7 @@ jobs: echo "XZ_VERSION=${XZ_VERSION}" >> ${GITHUB_OUTPUT} echo "OPENSSL_VERSION=${OPENSSL_VERSION}" >> ${GITHUB_OUTPUT} echo "LIBFFI_VERSION=${LIBFFI_VERSION}" >> ${GITHUB_OUTPUT} + - name: Upload build artifact uses: actions/upload-artifact@v3.1.3 with: From 184ca2c1a8c1c8cd19dfdf6a79477e50a5f9e4f1 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 27 Oct 2023 11:39:52 +0800 Subject: [PATCH 004/113] Correct the naming of outputs. --- .github/workflows/publish.yaml | 16 ++++++++-------- .github/workflows/release.yaml | 15 +++++++-------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 3bbd1793..da23aa00 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -41,14 +41,14 @@ jobs: python -m pip install -U setuptools python -m pip install awscli # macOS build - curl -o macOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.output.TAG }}/Python-${{ steps.build-vars.output.PY_VERSION }}-macOS-support.${{ steps.build-vars.output.BUILD_NUMBER }}.tar.gz - aws s3 cp macOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.output.PY_VERSION }}/macOS/Python-${{ steps.build-vars.output.PY_VERSION }}-macOS-support.${{ steps.build-vars.output.BUILD_NUMBER }}.tar.gz + curl -o macOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.outputs.TAG }}/Python-${{ steps.build-vars.outputs.PY_VERSION }}-macOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + aws s3 cp macOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.outputs.PY_VERSION }}/macOS/Python-${{ steps.build-vars.outputs.PY_VERSION }}-macOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz # iOS build - curl -o iOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.output.TAG }}/Python-${{ steps.build-vars.output.PY_VERSION }}-iOS-support.${{ steps.build-vars.output.BUILD_NUMBER }}.tar.gz - aws s3 cp iOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.output.PY_VERSION }}/iOS/Python-${{ steps.build-vars.output.PY_VERSION }}-iOS-support.${{ steps.build-vars.output.BUILD_NUMBER }}.tar.gz + curl -o iOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.outputs.TAG }}/Python-${{ steps.build-vars.outputs.PY_VERSION }}-iOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + aws s3 cp iOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.outputs.PY_VERSION }}/iOS/Python-${{ steps.build-vars.outputs.PY_VERSION }}-iOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz # tvOS build - curl -o tvOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.output.TAG }}/Python-${{ steps.build-vars.output.PY_VERSION }}-tvOS-support.${{ steps.build-vars.output.BUILD_NUMBER }}.tar.gz - aws s3 cp tvOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.output.PY_VERSION }}/tvOS/Python-${{ steps.build-vars.output.PY_VERSION }}-tvOS-support.${{ steps.build-vars.output.BUILD_NUMBER }}.tar.gz + curl -o tvOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.outputs.TAG }}/Python-${{ steps.build-vars.outputs.PY_VERSION }}-tvOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + aws s3 cp tvOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.outputs.PY_VERSION }}/tvOS/Python-${{ steps.build-vars.outputs.PY_VERSION }}-tvOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz # watchOS build - curl -o watchOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.output.TAG }}/Python-${{ steps.build-vars.output.PY_VERSION }}-watchOS-support.${{ steps.build-vars.output.BUILD_NUMBER }}.tar.gz - aws s3 cp watchOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.output.PY_VERSION }}/watchOS/Python-${{ steps.build-vars.output.PY_VERSION }}-watchOS-support.${{ steps.build-vars.output.BUILD_NUMBER }}.tar.gz + curl -o watchOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.outputs.TAG }}/Python-${{ steps.build-vars.outputs.PY_VERSION }}-watchOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + aws s3 cp watchOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.outputs.PY_VERSION }}/watchOS/Python-${{ steps.build-vars.outputs.PY_VERSION }}-watchOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 61aa8e35..34448867 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -8,7 +8,6 @@ on: - '*-b*' jobs: - build: runs-on: macOS-latest outputs: @@ -46,21 +45,21 @@ jobs: - name: Set up Python uses: actions/setup-python@v4.7.1 with: - python-version: "${{ steps.build-vars.output.PYTHON_VER }}-dev" + python-version: "${{ steps.build-vars.outputs.PYTHON_VER }}-dev" - name: Build ${{ matrix.target }} run: | # Do the build for the requested target. - make ${{ matrix.target }} BUILD_NUMBER=${{ steps.build-vars.output.BUILD_NUMBER }} + make ${{ matrix.target }} BUILD_NUMBER=${{ steps.build-vars.outputs.BUILD_NUMBER }} - name: Extract version details id: version-details run: | - export PYTHON_VERSION=$(grep "Python version:" support/${{ steps.build-vars.output.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 3) - export BZIP2_VERSION=$(grep "BZip2:" support/${{ steps.build-vars.output.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) - export XZ_VERSION=$(grep "XZ:" support/${{ steps.build-vars.output.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) - export OPENSSL_VERSION=$(grep "OpenSSL:" support/${{ steps.build-vars.output.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) - export LIBFFI_VERSION=$(grep "libFFI:" support/${{ steps.build-vars.output.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) + export PYTHON_VERSION=$(grep "Python version:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 3) + export BZIP2_VERSION=$(grep "BZip2:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) + export XZ_VERSION=$(grep "XZ:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) + export OPENSSL_VERSION=$(grep "OpenSSL:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) + export LIBFFI_VERSION=$(grep "libFFI:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) echo "PYTHON_VERSION=${PYTHON_VERSION}" echo "BZIP2_VERSION=${BZIP2_VERSION}" From 7431428f58baea6f4bdea08424b0c4b9502ae390 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 27 Oct 2023 11:52:48 +0800 Subject: [PATCH 005/113] Bump setup-python action version. --- .github/workflows/publish.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index da23aa00..6cb84f82 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python environment - uses: actions/setup-python@v1 + uses: actions/setup-python@v4.7.1 with: python-version: "3.X" From ba8baa729dcb765fc5eb011a0395885c2754befc Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 27 Oct 2023 14:53:11 +0800 Subject: [PATCH 006/113] Make clean cross-version safe. --- Makefile | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Makefile b/Makefile index cbd4f9fe..76689c34 100644 --- a/Makefile +++ b/Makefile @@ -74,13 +74,9 @@ all: $(OS_LIST) $(foreach os,$(OS_LIST),$(foreach sdk,$$(sort $$(basename $$(TARGETS-$(os)))),$(sdk) vars-$(sdk))) $(foreach os,$(OS_LIST),$(foreach target,$$(TARGETS-$(os)),$(target) vars-$(target))) -# Clean all builds -clean: - rm -rf build dist install support - # Full clean - includes all downloaded products distclean: clean - rm -rf downloads + rm -rf downloads build dist install support update-patch: # Generate a diff from the clone of the python/cpython Github repository, From f0c1acdfeaa592e8010fcfad246b7e02677c730a Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 1 Nov 2023 09:30:58 +0800 Subject: [PATCH 007/113] Extract macOS framework from official binary release. --- Makefile | 192 ++++++++--------------------- patch/Python/release.macOS.exclude | 12 +- 2 files changed, 59 insertions(+), 145 deletions(-) diff --git a/Makefile b/Makefile index 76689c34..32019c98 100644 --- a/Makefile +++ b/Makefile @@ -104,6 +104,12 @@ downloads/Python-$(PYTHON_VERSION).tar.gz: curl $(CURL_FLAGS) -o $@ \ https://www.python.org/ftp/python/$(PYTHON_MICRO_VERSION)/Python-$(PYTHON_VERSION).tgz +downloads/python-$(PYTHON_VERSION)-macos11.pkg: + @echo ">>> Download macOS Python package" + mkdir -p downloads + curl $(CURL_FLAGS) -o $@ \ + https://www.python.org/ftp/python/$(PYTHON_MICRO_VERSION)/python-$(PYTHON_VERSION)-macos11.pkg + ########################################################################### # Build for specified target (from $(TARGETS-*)) ########################################################################### @@ -398,146 +404,15 @@ endif # Expand the build-target macro for target on this OS $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(eval $$(call build-target,$$(target),$(os)))) -########################################################################### -# SDK: Aliases -########################################################################### - -support/$(PYTHON_VER)/$(os)/bin/$$(TARGET_TRIPLE-$(sdk))-clang: - patch/make-xcrun-alias $$@ "--sdk $(sdk) clang" - -support/$(PYTHON_VER)/$(os)/bin/$$(TARGET_TRIPLE-$(sdk))-cpp: - patch/make-xcrun-alias $$@ "--sdk $(sdk) clang -E" - -support/$(PYTHON_VER)/$(os)/bin/$$(TARGET_TRIPLE-$(sdk))-ar: - patch/make-xcrun-alias $$@ "--sdk $(sdk) ar" - -########################################################################### -# SDK: BZip2 -########################################################################### - -BZIP2_INSTALL-$(sdk)=$(PROJECT_DIR)/install/$(os)/$(sdk)/bzip2-$(BZIP2_VERSION) -BZIP2_LIB-$(sdk)=$$(BZIP2_INSTALL-$(sdk))/lib/libbz2.a - -# This is only used on macOS. -downloads/bzip2-$(BZIP2_VERSION)-$(sdk).tar.gz: - @echo ">>> Download BZip2 for $(sdk)" - mkdir -p downloads - curl $(CURL_FLAGS) -o $$@ \ - https://github.com/beeware/cpython-macOS-source-deps/releases/download/BZip2-$(BZIP2_VERSION)/bzip2-$(BZIP2_VERSION)-$(sdk).tar.gz - -$$(BZIP2_LIB-$(sdk)): downloads/bzip2-$(BZIP2_VERSION)-$(sdk).tar.gz - @echo ">>> Install BZip2 for $(sdk)" - mkdir -p $$(BZIP2_INSTALL-$(sdk)) - cd $$(BZIP2_INSTALL-$(sdk)) && tar zxvf $(PROJECT_DIR)/downloads/bzip2-$(BZIP2_VERSION)-$(sdk).tar.gz - # Ensure the target is marked as clean. - touch $$(BZIP2_LIB-$(sdk)) - -########################################################################### -# SDK: XZ (LZMA) -########################################################################### - -XZ_INSTALL-$(sdk)=$(PROJECT_DIR)/install/$(os)/$(sdk)/xz-$(XZ_VERSION) -XZ_LIB-$(sdk)=$$(XZ_INSTALL-$(sdk))/lib/liblzma.a - -# This is only used on macOS. -downloads/xz-$(XZ_VERSION)-$(sdk).tar.gz: - @echo ">>> Download XZ for $(sdk)" - mkdir -p downloads - curl $(CURL_FLAGS) -o $$@ \ - https://github.com/beeware/cpython-macOS-source-deps/releases/download/XZ-$(XZ_VERSION)/xz-$(XZ_VERSION)-$(sdk).tar.gz - -$$(XZ_LIB-$(sdk)): downloads/xz-$(XZ_VERSION)-$(sdk).tar.gz - @echo ">>> Install XZ for $(sdk)" - mkdir -p $$(XZ_INSTALL-$(sdk)) - cd $$(XZ_INSTALL-$(sdk)) && tar zxvf $(PROJECT_DIR)/downloads/xz-$(XZ_VERSION)-$(sdk).tar.gz - # Ensure the target is marked as clean. - touch $$(XZ_LIB-$(sdk)) - -########################################################################### -# SDK: OpenSSL -########################################################################### - -OPENSSL_INSTALL-$(sdk)=$(PROJECT_DIR)/install/$(os)/$(sdk)/openssl-$(OPENSSL_VERSION) -OPENSSL_SSL_LIB-$(sdk)=$$(OPENSSL_INSTALL-$(sdk))/lib/libssl.a - -# This is only used on macOS. -downloads/openssl-$(OPENSSL_VERSION)-$(sdk).tar.gz: - @echo ">>> Download OpenSSL for $(sdk)" - mkdir -p downloads - curl $(CURL_FLAGS) -o $$@ \ - https://github.com/beeware/cpython-macOS-source-deps/releases/download/OpenSSL-$(OPENSSL_VERSION)/openssl-$(OPENSSL_VERSION)-$(sdk).tar.gz - -$$(OPENSSL_SSL_LIB-$(sdk)): downloads/openssl-$(OPENSSL_VERSION)-$(sdk).tar.gz - @echo ">>> Install OpenSSL for $(sdk)" - mkdir -p $$(OPENSSL_INSTALL-$(sdk)) - cd $$(OPENSSL_INSTALL-$(sdk)) && tar zxvf $(PROJECT_DIR)/downloads/openssl-$(OPENSSL_VERSION)-$(sdk).tar.gz - # Ensure the target is marked as clean. - touch $$(OPENSSL_SSL_LIB-$(sdk)) - ########################################################################### # SDK: Python ########################################################################### -# macOS builds are compiled as a single universal2 build. The fat library is a -# direct copy of OS build, and the headers and standard library are unmodified -# from the versions produced by the OS build. -ifeq ($(os),macOS) - -PYTHON_SRCDIR-$(sdk)=build/$(os)/$(sdk)/python-$(PYTHON_VERSION) - -$$(PYTHON_SRCDIR-$(sdk))/configure: \ - $$(BZIP2_LIB-$$(sdk)) \ - $$(XZ_LIB-$$(sdk)) \ - $$(OPENSSL_SSL_LIB-$$(sdk)) \ - downloads/Python-$(PYTHON_VERSION).tar.gz - @echo ">>> Unpack and configure Python for $(sdk)" - mkdir -p $$(PYTHON_SRCDIR-$(sdk)) - tar zxf downloads/Python-$(PYTHON_VERSION).tar.gz --strip-components 1 -C $$(PYTHON_SRCDIR-$(sdk)) - # Apply target Python patches - cd $$(PYTHON_SRCDIR-$(sdk)) && patch -p1 < $(PROJECT_DIR)/patch/Python/Python.patch - # Touch the configure script to ensure that Make identifies it as up to date. - touch $$(PYTHON_SRCDIR-$(sdk))/configure - -$$(PYTHON_SRCDIR-$(sdk))/Makefile: \ - support/$(PYTHON_VER)/$(os)/bin/$$(TARGET_TRIPLE-$(sdk))-clang \ - support/$(PYTHON_VER)/$(os)/bin/$$(TARGET_TRIPLE-$(sdk))-cpp \ - $$(PYTHON_SRCDIR-$(sdk))/configure - # Configure target Python - cd $$(PYTHON_SRCDIR-$(sdk)) && \ - PATH="$(PROJECT_DIR)/support/$(PYTHON_VER)/$(os)/bin:$(PATH)" \ - ./configure \ - CC=$$(TARGET_TRIPLE-$(sdk))-clang \ - CPP=$$(TARGET_TRIPLE-$(sdk))-cpp \ - CFLAGS="$$(CFLAGS-$(sdk))" \ - LDFLAGS="$$(LDFLAGS-$(sdk))" \ - LIBLZMA_CFLAGS="-I$$(XZ_INSTALL-$(sdk))/include" \ - LIBLZMA_LIBS="-L$$(XZ_INSTALL-$(sdk))/lib -llzma" \ - BZIP2_CFLAGS="-I$$(BZIP2_INSTALL-$(sdk))/include" \ - BZIP2_LIBS="-L$$(BZIP2_INSTALL-$(sdk))/lib -lbz2" \ - MACOSX_DEPLOYMENT_TARGET="$$(VERSION_MIN-$(os))" \ - --prefix="$$(PYTHON_INSTALL-$(sdk))" \ - --enable-ipv6 \ - --enable-universalsdk \ - --with-openssl="$$(OPENSSL_INSTALL-$(sdk))" \ - --with-universal-archs=universal2 \ - --without-ensurepip \ - 2>&1 | tee -a ../python-$(PYTHON_VERSION).config.log - -$$(PYTHON_SRCDIR-$(sdk))/python.exe: $$(PYTHON_SRCDIR-$(sdk))/Makefile - @echo ">>> Build Python for $(sdk)" - cd $$(PYTHON_SRCDIR-$(sdk)) && \ - PATH="$(PROJECT_DIR)/support/$(PYTHON_VER)/$(os)/bin:$(PATH)" \ - make all \ - 2>&1 | tee -a ../python-$(PYTHON_VERSION).build.log - -$$(PYTHON_LIB-$(sdk)) $$(PYTHON_INCLUDE-$$(sdk))/Python.h $$(PYTHON_STDLIB-$(sdk))/LICENSE.TXT: $$(PYTHON_SRCDIR-$(sdk))/python.exe - @echo ">>> Install Python for $(sdk)" - cd $$(PYTHON_SRCDIR-$(sdk)) && \ - make install \ - 2>&1 | tee -a ../python-$(PYTHON_VERSION).install.log - -else +ifneq ($(os),macOS) +# macOS builds are extracted from the official installer package, then +# reprocessed into an XCFramework. +# # Non-macOS builds need to be merged on a per-SDK basis. The merge covers: # * Merging a fat libPython.a # * Installing an architecture-sensitive pyconfig.h @@ -632,6 +507,47 @@ $$(foreach sdk,$$(SDKS-$(os)),$$(eval $$(call build-sdk,$$(sdk),$(os)))) # Build: Python ########################################################################### +ifeq ($(os),macOS) + +$$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ + downloads/python-$(PYTHON_VERSION)-macos11.pkg + @echo ">>> Repackage macOS package as XCFramework" + + # Unpack .pkg file. It turns out .pkg files are readable by tar... although + # their internal format is a bit of a mess. From tar's perspective, the .pkg + # is a tarball that contains additional tarballs; the inner tarball has the + # "payload" that is the framework. + mkdir -p build/macOS/macosx/python-$(PYTHON_VERSION) + tar zxf downloads/python-$(PYTHON_VERSION)-macos11.pkg -C build/macOS/macosx/python-$(PYTHON_VERSION) + + # Unpack payload inside .pkg file + mkdir -p install/macOS/macosx/python-$(PYTHON_VERSION)/Python.framework + tar zxf build/macOS/macosx/python-$(PYTHON_VERSION)/Python_Framework.pkgPython_Framework.pkg/PayloadPython_Framework.pkgPython_Framework.pkg/PayloadPython_Framework.pkgPython_Framework.pkg/Payload -C install/macOS/macosx/python-$(PYTHON_VERSION)/Python.framework -X patch/Python/release.macOS.exclude + + # Remove the signature from the extracted framework + codesign --remove-signature install/macOS/macosx/python-$(PYTHON_VERSION)/Python.framework + + # Create XCFramework out of the extracted framework + xcodebuild -create-xcframework -output $$(PYTHON_XCFRAMEWORK-$(os)) -framework install/macOS/macosx/python-$(PYTHON_VERSION)/Python.framework + +support/$(PYTHON_VER)/macOS/VERSIONS: + @echo ">>> Create VERSIONS file for macOS" + echo "Python version: $(PYTHON_VERSION) " > support/$(PYTHON_VER)/macOS/VERSIONS + echo "Build: $(BUILD_NUMBER)" >> support/$(PYTHON_VER)/macOS/VERSIONS + echo "Min macOS version: $$(VERSION_MIN-macOS)" >> support/$(PYTHON_VER)/macOS/VERSIONS + +dist/Python-$(PYTHON_VER)-macOS-support.$(BUILD_NUMBER).tar.gz: \ + $$(PYTHON_XCFRAMEWORK-macOS)/Info.plist \ + support/$(PYTHON_VER)/macOS/VERSIONS \ + $$(foreach target,$$(TARGETS-macOS), $$(PYTHON_SITECUSTOMIZE-$$(target))) + + @echo ">>> Create final distribution artefact for macOS" + mkdir -p dist + # Build a distributable tarball + tar zcvf $$@ -C support/$(PYTHON_VER)/macOS `ls -A support/$(PYTHON_VER)/macOS/` + +else + $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ $$(foreach sdk,$$(SDKS-$(os)),$$(PYTHON_LIB-$$(sdk)) $$(PYTHON_INCLUDE-$$(sdk))/Python.h) @echo ">>> Create Python.XCFramework on $(os)" @@ -666,11 +582,7 @@ $$(PYTHON_STDLIB-$(os))/VERSIONS: \ echo "Build: $(BUILD_NUMBER)" >> support/$(PYTHON_VER)/$(os)/VERSIONS echo "Min $(os) version: $$(VERSION_MIN-$(os))" >> support/$(PYTHON_VER)/$(os)/VERSIONS echo "---------------------" >> support/$(PYTHON_VER)/$(os)/VERSIONS -ifeq ($(os),macOS) - echo "libFFI: built-in" >> support/$(PYTHON_VER)/$(os)/VERSIONS -else echo "libFFI: $(LIBFFI_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS -endif echo "BZip2: $(BZIP2_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS echo "OpenSSL: $(OPENSSL_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS echo "XZ: $(XZ_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS @@ -687,6 +599,8 @@ dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz: \ # Build a distributable tarball tar zcvf $$@ -X patch/Python/release.common.exclude -X patch/Python/release.$(os).exclude -C support/$(PYTHON_VER)/$(os) `ls -A support/$(PYTHON_VER)/$(os)/` +endif + clean-$(os): @echo ">>> Clean Python build products on $(os)" rm -rf \ diff --git a/patch/Python/release.macOS.exclude b/patch/Python/release.macOS.exclude index 1428348e..13854545 100644 --- a/patch/Python/release.macOS.exclude +++ b/patch/Python/release.macOS.exclude @@ -1,8 +1,8 @@ -# This is a list of support package path patterns that we exclude -# from macOS Python-Apple-support tarballs. +# This is a list of Framework path patterns that we exclude +# when building macOS Python-Apple-support tarballs from the official Framework # It is used by `tar -X` during the Makefile build. # -# Remove the testing binary modules -python-stdlib/lib-dynload/*_test*.so -python-stdlib/lib-dynload/_xx*.so -python-stdlib/lib-dynload/xx*.so +./Versions/*/Resources/Python.app +./Versions/*/bin +./Versions/*/etc +./Versions/*/share From 9ef6e91805677336f09c42fa3e54aec8e364f6a7 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 14 Nov 2023 13:43:20 +0800 Subject: [PATCH 008/113] Modify build process to generate frameworks for iOS/tvOS/watchOS. --- Makefile | 205 ++- patch/Python/Python.patch | 1835 ++++++++++++++++++++++++-- patch/Python/release.common.exclude | 26 - patch/Python/release.iOS.exclude | 12 +- patch/Python/release.tvOS.exclude | 12 +- patch/Python/release.watchOS.exclude | 12 +- patch/make-macho-standalone.py | 10 + 7 files changed, 1867 insertions(+), 245 deletions(-) delete mode 100644 patch/Python/release.common.exclude create mode 100644 patch/make-macho-standalone.py diff --git a/Makefile b/Makefile index 32019c98..40d637e0 100644 --- a/Makefile +++ b/Makefile @@ -37,23 +37,19 @@ CURL_FLAGS=--disable --fail --location --create-dirs --progress-bar # macOS targets TARGETS-macOS=macosx.x86_64 macosx.arm64 VERSION_MIN-macOS=11.0 -CFLAGS-macOS=-mmacosx-version-min=$(VERSION_MIN-macOS) # iOS targets TARGETS-iOS=iphonesimulator.x86_64 iphonesimulator.arm64 iphoneos.arm64 VERSION_MIN-iOS=12.0 -CFLAGS-iOS=-mios-version-min=$(VERSION_MIN-iOS) # tvOS targets TARGETS-tvOS=appletvsimulator.x86_64 appletvsimulator.arm64 appletvos.arm64 VERSION_MIN-tvOS=9.0 -CFLAGS-tvOS=-mtvos-version-min=$(VERSION_MIN-tvOS) PYTHON_CONFIGURE-tvOS=ac_cv_func_sigaltstack=no # watchOS targets TARGETS-watchOS=watchsimulator.x86_64 watchsimulator.arm64 watchos.arm64_32 VERSION_MIN-watchOS=4.0 -CFLAGS-watchOS=-mwatchos-version-min=$(VERSION_MIN-watchOS) PYTHON_CONFIGURE-watchOS=ac_cv_func_sigaltstack=no # The architecture of the machine doing the build @@ -131,29 +127,17 @@ ARCH-$(target)=$$(subst .,,$$(suffix $(target))) WHEEL_TAG-$(target)=py3-none-$$(shell echo $$(OS_LOWER-$(target))_$$(VERSION_MIN-$(os))_$(target) | sed "s/\./_/g") -ifeq ($(os),macOS) -TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-darwin -else +ifneq ($(os),macOS) ifeq ($$(findstring simulator,$$(SDK-$(target))),) TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(OS_LOWER-$(target))$$(VERSION_MIN-$(os)) +TARGET_TOOL_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(OS_LOWER-$(target)) else TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(OS_LOWER-$(target))$$(VERSION_MIN-$(os))-simulator +TARGET_TOOL_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(OS_LOWER-$(target))-simulator endif endif SDK_ROOT-$(target)=$$(shell xcrun --sdk $$(SDK-$(target)) --show-sdk-path) -CFLAGS-$(target)=$$(CFLAGS-$(os)) -LDFLAGS-$(target)=$$(CFLAGS-$(os)) - -########################################################################### -# Target: Aliases -########################################################################### - -support/$(PYTHON_VER)/$(os)/bin/$$(TARGET_TRIPLE-$(target))-clang: - patch/make-xcrun-alias $$@ "--sdk $$(SDK-$(target)) clang -target $$(TARGET_TRIPLE-$(target))" - -support/$(PYTHON_VER)/$(os)/bin/$$(TARGET_TRIPLE-$(target))-cpp: - patch/make-xcrun-alias $$@ "--sdk $$(SDK-$(target)) clang -target $$(TARGET_TRIPLE-$(target)) -E" ########################################################################### # Target: BZip2 @@ -253,7 +237,11 @@ ifneq ($(os),macOS) PYTHON_SRCDIR-$(target)=build/$(os)/$(target)/python-$(PYTHON_VERSION) PYTHON_INSTALL-$(target)=$(PROJECT_DIR)/install/$(os)/$(target)/python-$(PYTHON_VERSION) -PYTHON_LIB-$(target)=$$(PYTHON_INSTALL-$(target))/lib/libpython$(PYTHON_VER).a +PYTHON_FRAMEWORK-$(target)=$$(PYTHON_INSTALL-$(target))/Python.framework +PYTHON_LIB-$(target)=$$(PYTHON_FRAMEWORK-$(target))/Python +PYTHON_BIN-$(target)=$$(PYTHON_INSTALL-$(target))/bin +PYTHON_INCLUDE-$(target)=$$(PYTHON_FRAMEWORK-$(target))/Headers +PYTHON_STDLIB-$(target)=$$(PYTHON_INSTALL-$(target))/lib/python$(PYTHON_VER) $$(PYTHON_SRCDIR-$(target))/configure: \ downloads/Python-$(PYTHON_VERSION).tar.gz \ @@ -266,24 +254,21 @@ $$(PYTHON_SRCDIR-$(target))/configure: \ 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/* # Touch the configure script to ensure that Make identifies it as up to date. touch $$(PYTHON_SRCDIR-$(target))/configure $$(PYTHON_SRCDIR-$(target))/Makefile: \ - support/$(PYTHON_VER)/$(os)/bin/$$(TARGET_TRIPLE-$$(SDK-$(target)))-ar \ - support/$(PYTHON_VER)/$(os)/bin/$$(TARGET_TRIPLE-$(target))-clang \ - support/$(PYTHON_VER)/$(os)/bin/$$(TARGET_TRIPLE-$(target))-cpp \ $$(PYTHON_SRCDIR-$(target))/configure # Configure target Python cd $$(PYTHON_SRCDIR-$(target)) && \ - PATH="$(PROJECT_DIR)/support/$(PYTHON_VER)/$(os)/bin:$(PATH)" \ + PATH="$(PROJECT_DIR)/$$(PYTHON_SRCDIR-$(target))/$(os)/Resources/bin:$(PATH)" \ ./configure \ - AR=$$(TARGET_TRIPLE-$$(SDK-$(target)))-ar \ - CC=$$(TARGET_TRIPLE-$(target))-clang \ - CPP=$$(TARGET_TRIPLE-$(target))-cpp \ - CXX=$$(TARGET_TRIPLE-$(target))-clang \ - CFLAGS="$$(CFLAGS-$(target))" \ - LDFLAGS="$$(LDFLAGS-$(target))" \ + AR=$$(TARGET_TOOL_TRIPLE-$(target))-ar \ + CC=$$(TARGET_TOOL_TRIPLE-$(target))-clang \ + CPP=$$(TARGET_TOOL_TRIPLE-$(target))-cpp \ + CXX=$$(TARGET_TOOL_TRIPLE-$(target))-clang \ LIBLZMA_CFLAGS="-I$$(XZ_INSTALL-$(target))/include" \ LIBLZMA_LIBS="-L$$(XZ_INSTALL-$(target))/lib -llzma" \ BZIP2_CFLAGS="-I$$(BZIP2_INSTALL-$(target))/include" \ @@ -293,10 +278,10 @@ $$(PYTHON_SRCDIR-$(target))/Makefile: \ --host=$$(TARGET_TRIPLE-$(target)) \ --build=$(HOST_ARCH)-apple-darwin \ --with-build-python=$(HOST_PYTHON) \ - --prefix="$$(PYTHON_INSTALL-$(target))" \ --enable-ipv6 \ --with-openssl="$$(OPENSSL_INSTALL-$(target))" \ --without-ensurepip \ + --enable-framework="$$(PYTHON_INSTALL-$(target))" \ ac_cv_file__dev_ptmx=no \ ac_cv_file__dev_ptc=no \ $$(PYTHON_CONFIGURE-$(os)) \ @@ -305,14 +290,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)/support/$(PYTHON_VER)/$(os)/bin:$(PATH)" \ + PATH="$(PROJECT_DIR)/$$(PYTHON_SRCDIR-$(target))/$(os)/Resources/bin:$(PATH)" \ make 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)/support/$(PYTHON_VER)/$(os)/bin:$(PATH)" \ + PATH="$(PROJECT_DIR)/$$(PYTHON_SRCDIR-$(target))/$(os)/Resources/bin:$(PATH)" \ make install \ 2>&1 | tee -a ../python-$(PYTHON_VERSION).install.log @@ -341,8 +326,6 @@ vars-$(target): @echo "ARCH-$(target): $$(ARCH-$(target))" @echo "TARGET_TRIPLE-$(target): $$(TARGET_TRIPLE-$(target))" @echo "SDK_ROOT-$(target): $$(SDK_ROOT-$(target))" - @echo "CFLAGS-$(target): $$(CFLAGS-$(target))" - @echo "LDFLAGS-$(target): $$(LDFLAGS-$(target))" @echo "BZIP2_INSTALL-$(target): $$(BZIP2_INSTALL-$(target))" @echo "BZIP2_LIB-$(target): $$(BZIP2_LIB-$(target))" @echo "XZ_INSTALL-$(target): $$(XZ_INSTALL-$(target))" @@ -353,7 +336,11 @@ vars-$(target): @echo "LIBFFI_LIB-$(target): $$(LIBFFI_LIB-$(target))" @echo "PYTHON_SRCDIR-$(target): $$(PYTHON_SRCDIR-$(target))" @echo "PYTHON_INSTALL-$(target): $$(PYTHON_INSTALL-$(target))" + @echo "PYTHON_FRAMEWORK-$(target): $$(PYTHON_FRAMEWORK-$(target))" @echo "PYTHON_LIB-$(target): $$(PYTHON_LIB-$(target))" + @echo "PYTHON_BIN-$(target): $$(PYTHON_BIN-$(target))" + @echo "PYTHON_INCLUDE-$(target): $$(PYTHON_INCLUDE-$(target))" + @echo "PYTHON_STDLIB-$(target): $$(PYTHON_STDLIB-$(target))" @echo endef # build-target @@ -382,25 +369,6 @@ else SDK_SLICE-$(sdk)=$$(OS_LOWER-$(sdk))-$$(shell echo $$(SDK_ARCHES-$(sdk)) | sed "s/ /_/g")-simulator endif -CFLAGS-$(sdk)=$$(CFLAGS-$(os)) -LDFLAGS-$(sdk)=$$(CFLAGS-$(os)) - -# Predeclare SDK constants that are used by the build-target macro -PYTHON_INSTALL-$(sdk)=$(PROJECT_DIR)/install/$(os)/$(sdk)/python-$(PYTHON_VERSION) -PYTHON_LIB-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/lib/libpython$(PYTHON_VER).a -PYTHON_INCLUDE-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/include/python$(PYTHON_VER) -PYTHON_STDLIB-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/lib/python$(PYTHON_VER) - -ifeq ($(os),macOS) -TARGET_TRIPLE-$(sdk)=apple-darwin -else - ifeq ($$(findstring simulator,$(sdk)),) -TARGET_TRIPLE-$(sdk)=apple-$$(OS_LOWER-$(sdk))$$(VERSION_MIN-$(os)) - else -TARGET_TRIPLE-$(sdk)=apple-$$(OS_LOWER-$(sdk))$$(VERSION_MIN-$(os))-simulator - endif -endif - # Expand the build-target macro for target on this OS $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(eval $$(call build-target,$$(target),$(os)))) @@ -408,15 +376,31 @@ $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(eval $$(call build-target,$$(target) # SDK: Python ########################################################################### -ifneq ($(os),macOS) +ifeq ($(os),macOS) # macOS builds are extracted from the official installer package, then # reprocessed into an XCFramework. -# + +PYTHON_INSTALL-$(sdk)=$(PROJECT_DIR)/install/$(os)/$(sdk)/python-$(PYTHON_VERSION) +PYTHON_FRAMEWORK-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/Python.framework +PYTHON_INSTALL_VERSION-$(sdk)=$$(PYTHON_FRAMEWORK-$(sdk))/Versions/$(PYTHON_VER) +PYTHON_LIB-$(sdk)=$$(PYTHON_INSTALL_VERSION-$(sdk))/Python +PYTHON_INCLUDE-$(sdk)=$$(PYTHON_INSTALL_VERSION-$(sdk))/include/python$(PYTHON_VER) +PYTHON_STDLIB-$(sdk)=$$(PYTHON_INSTALL_VERSION-$(sdk))/lib/python$(PYTHON_VER) + +else # Non-macOS builds need to be merged on a per-SDK basis. The merge covers: -# * Merging a fat libPython.a +# * Merging a fat libPython # * Installing an architecture-sensitive pyconfig.h # * Merging fat versions of the standard library lib-dynload folder +# The non-macOS frameworks don't use the versioning structure. + +PYTHON_INSTALL-$(sdk)=$(PROJECT_DIR)/install/$(os)/$(sdk)/python-$(PYTHON_VERSION) +PYTHON_FRAMEWORK-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/Python.framework +PYTHON_LIB-$(sdk)=$$(PYTHON_FRAMEWORK-$(sdk))/Python +PYTHON_BIN-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/bin +PYTHON_INCLUDE-$(sdk)=$$(PYTHON_FRAMEWORK-$(sdk))/Headers +PYTHON_STDLIB-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/lib/python$(PYTHON_VER) $$(PYTHON_LIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_LIB-$$(target))) @echo ">>> Build Python fat library for the $(sdk) SDK" @@ -424,21 +408,30 @@ $$(PYTHON_LIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_LIB-$$ lipo -create -output $$@ $$^ \ 2>&1 | tee -a install/$(os)/$(sdk)/python-$(PYTHON_VERSION).lipo.log -$$(PYTHON_INCLUDE-$(sdk))/Python.h: $$(PYTHON_LIB-$(sdk)) +$$(PYTHON_FRAMEWORK-$(sdk))/Info.plist: $$(PYTHON_LIB-$(sdk)) + @echo ">>> Install Info.plist for the $(sdk) SDK" + # Copy Info.plist as-is from the first target in the $(sdk) SDK + cp -r $$(PYTHON_FRAMEWORK-$$(firstword $$(SDK_TARGETS-$(sdk))))/Info.plist $$(PYTHON_FRAMEWORK-$(sdk)) + +$$(PYTHON_INCLUDE-$(sdk))/pyconfig.h: $$(PYTHON_LIB-$(sdk)) @echo ">>> Build Python fat headers for the $(sdk) SDK" + # Copy binary helpers from the first target in the $(sdk) SDK + cp -r $$(PYTHON_BIN-$$(firstword $$(SDK_TARGETS-$(sdk)))) $$(PYTHON_BIN-$(sdk)) # Copy headers as-is from the first target in the $(sdk) SDK - mkdir -p $$(shell dirname $$(PYTHON_INCLUDE-$(sdk))) - cp -r $$(PYTHON_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/include/python$(PYTHON_VER) $$(PYTHON_INCLUDE-$(sdk)) + cp -r $$(PYTHON_INCLUDE-$$(firstword $$(SDK_TARGETS-$(sdk)))) $$(PYTHON_INCLUDE-$(sdk)) + # Link the PYTHONHOME version of the headers + mkdir -p $$(PYTHON_INSTALL-$(sdk))/include + ln -si ../Python.framework/Headers $$(PYTHON_INSTALL-$(sdk))/include/python$(PYTHON_VER) + # 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 patch folder cp $(PROJECT_DIR)/patch/Python/pyconfig-$(os).h $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h - # Add the individual headers from each target in an arch-specific name - $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_INSTALL-$$(target))/include/python$(PYTHON_VER)/pyconfig.h $$(PYTHON_INCLUDE-$(sdk))/pyconfig-$$(ARCH-$$(target)).h; ) -$$(PYTHON_STDLIB-$(sdk))/LICENSE.TXT: $$(PYTHON_LIB-$(sdk)) +$$(PYTHON_STDLIB-$(sdk))/LICENSE.TXT: $$(PYTHON_LIB-$(sdk)) $$(PYTHON_FRAMEWORK-$(sdk))/Info.plist $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h @echo ">>> Build Python stdlib for the $(sdk) SDK" mkdir -p $$(PYTHON_STDLIB-$(sdk))/lib-dynload # Copy stdlib from the first target associated with the $(sdk) SDK - cp -r $$(PYTHON_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/lib/python$(PYTHON_VER)/ $$(PYTHON_STDLIB-$(sdk)) + cp -r $$(PYTHON_STDLIB-$$(firstword $$(SDK_TARGETS-$(sdk))))/ $$(PYTHON_STDLIB-$(sdk)) # Delete the single-SDK parts of the standard library rm -rf \ @@ -447,13 +440,10 @@ $$(PYTHON_STDLIB-$(sdk))/LICENSE.TXT: $$(PYTHON_LIB-$(sdk)) $$(PYTHON_STDLIB-$(sdk))/lib-dynload/* # Copy the individual _sysconfigdata modules into names that include the architecture - $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_INSTALL-$$(target))/lib/python$(PYTHON_VER)/_sysconfigdata_* $$(PYTHON_STDLIB-$(sdk))/; ) - - # Copy the individual config modules directories into names that include the architecture - $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp -r $$(PYTHON_INSTALL-$$(target))/lib/python$(PYTHON_VER)/config-$(PYTHON_VER)-$(sdk)-$$(ARCH-$$(target)) $$(PYTHON_STDLIB-$(sdk))/; ) + $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_STDLIB-$$(target))/_sysconfigdata_* $$(PYTHON_STDLIB-$(sdk))/; ) # Merge the binary modules from each target in the $(sdk) SDK into a single binary - $$(foreach module,$$(wildcard $$(PYTHON_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/lib/python$(PYTHON_VER)/lib-dynload/*),lipo -create -output $$(PYTHON_STDLIB-$(sdk))/lib-dynload/$$(notdir $$(module)) $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_INSTALL-$$(target))/lib/python$(PYTHON_VER)/lib-dynload/$$(notdir $$(module))); ) + $$(foreach module,$$(wildcard $$(PYTHON_STDLIB-$$(firstword $$(SDK_TARGETS-$(sdk))))/lib-dynload/*),lipo -create -output $$(PYTHON_STDLIB-$(sdk))/lib-dynload/$$(notdir $$(module)) $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_STDLIB-$$(target))/lib-dynload/$$(notdir $$(module))); ) endif @@ -468,13 +458,14 @@ vars-$(sdk): @echo "SDK_TARGETS-$(sdk): $$(SDK_TARGETS-$(sdk))" @echo "SDK_ARCHES-$(sdk): $$(SDK_ARCHES-$(sdk))" @echo "SDK_SLICE-$(sdk): $$(SDK_SLICE-$(sdk))" - @echo "CFLAGS-$(sdk): $$(CFLAGS-$(sdk))" @echo "LDFLAGS-$(sdk): $$(LDFLAGS-$(sdk))" - @echo "PYTHON_SRCDIR-$(sdk): $$(PYTHON_SRCDIR-$(sdk))" @echo "PYTHON_INSTALL-$(sdk): $$(PYTHON_INSTALL-$(sdk))" + @echo "PYTHON_FRAMEWORK-$(sdk): $$(PYTHON_FRAMEWORK-$(sdk))" @echo "PYTHON_LIB-$(sdk): $$(PYTHON_LIB-$(sdk))" + @echo "PYTHON_BIN-$(sdk): $$(PYTHON_BIN-$(sdk))" @echo "PYTHON_INCLUDE-$(sdk): $$(PYTHON_INCLUDE-$(sdk))" @echo "PYTHON_STDLIB-$(sdk): $$(PYTHON_STDLIB-$(sdk))" + @echo endef # build-sdk @@ -496,9 +487,6 @@ os=$1 SDKS-$(os)=$$(sort $$(basename $$(TARGETS-$(os)))) -# Predeclare the Python XCFramework files so they can be referenced in SDK targets -PYTHON_XCFRAMEWORK-$(os)=support/$(PYTHON_VER)/$(os)/Python.xcframework -PYTHON_STDLIB-$(os)=support/$(PYTHON_VER)/$(os)/python-stdlib # Expand the build-sdk macro for all the sdks on this OS (e.g., iphoneos, iphonesimulator) $$(foreach sdk,$$(SDKS-$(os)),$$(eval $$(call build-sdk,$$(sdk),$(os)))) @@ -507,8 +495,13 @@ $$(foreach sdk,$$(SDKS-$(os)),$$(eval $$(call build-sdk,$$(sdk),$(os)))) # Build: Python ########################################################################### + +PYTHON_XCFRAMEWORK-$(os)=support/$(PYTHON_VER)/$(os)/Python.xcframework + ifeq ($(os),macOS) +PYTHON_FRAMEWORK-$(os)=$$(PYTHON_INSTALL-$(sdk))/Python.framework + $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ downloads/python-$(PYTHON_VERSION)-macos11.pkg @echo ">>> Repackage macOS package as XCFramework" @@ -521,14 +514,31 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ tar zxf downloads/python-$(PYTHON_VERSION)-macos11.pkg -C build/macOS/macosx/python-$(PYTHON_VERSION) # Unpack payload inside .pkg file - mkdir -p install/macOS/macosx/python-$(PYTHON_VERSION)/Python.framework - tar zxf build/macOS/macosx/python-$(PYTHON_VERSION)/Python_Framework.pkgPython_Framework.pkg/PayloadPython_Framework.pkgPython_Framework.pkg/PayloadPython_Framework.pkgPython_Framework.pkg/Payload -C install/macOS/macosx/python-$(PYTHON_VERSION)/Python.framework -X patch/Python/release.macOS.exclude - - # Remove the signature from the extracted framework - codesign --remove-signature install/macOS/macosx/python-$(PYTHON_VERSION)/Python.framework + mkdir -p $$(PYTHON_FRAMEWORK-macosx) + tar zxf build/macOS/macosx/python-$(PYTHON_VERSION)/Python_Framework.pkgPython_Framework.pkg/PayloadPython_Framework.pkgPython_Framework.pkg/PayloadPython_Framework.pkgPython_Framework.pkg/Payload -C $$(PYTHON_FRAMEWORK-macosx) -X patch/Python/release.macOS.exclude + + # Rewrite the framework to make it standalone + python3 patch/make-macho-standalone.py $$(PYTHON_FRAMEWORK-macosx) \ + 2>&1 | tee $$(PYTHON_INSTALL-macosx)/python-$(PY_VERSION).make-macho-standalone.log + + # Remove the "development" versions of the libs + rm -f $$(PYTHON_INSTALL_VERSION-macosx)/lib/*.dylib + rm -f $$(PYTHON_INSTALL_VERSION-macosx)/lib/*.a + rm -rf $$(PYTHON_INSTALL_VERSION-macosx)/lib/python$(PY_VERSION)/config-* + + # Re-apply the signature on the binaries. + codesign -s - --preserve-metadata=identifier,entitlements,flags,runtime -f $$(PYTHON_LIB-macosx) \ + 2>&1 | tee $$(PYTHON_INSTALL-macosx)/python-$(os).codesign.log + find install/macOS/macosx/python-3.13.0a1/Python.framework -name "*.dylib" -type f -exec codesign -s - --preserve-metadata=identifier,entitlements,flags,runtime -f {} \; \ + 2>&1 | tee -a $$(PYTHON_INSTALL-macosx)/python-$(os).codesign.log + find install/macOS/macosx/python-3.13.0a1/Python.framework -name "*.so" -type f -exec codesign -s - --preserve-metadata=identifier,entitlements,flags,runtime -f {} \; \ + 2>&1 | tee -a $$(PYTHON_INSTALL-macosx)/python-$(os).codesign.log + codesign -s - --preserve-metadata=identifier,entitlements,flags,runtime -f $$(PYTHON_FRAMEWORK-macosx) \ + 2>&1 | tee -a $$(PYTHON_INSTALL-macosx)/python-$(os).codesign.log # Create XCFramework out of the extracted framework - xcodebuild -create-xcframework -output $$(PYTHON_XCFRAMEWORK-$(os)) -framework install/macOS/macosx/python-$(PYTHON_VERSION)/Python.framework + xcodebuild -create-xcframework -output $$(PYTHON_XCFRAMEWORK-$(os)) -framework $$(PYTHON_FRAMEWORK-macosx) \ + 2>&1 | tee $$(PYTHON_INSTALL-macosx)/python-$(os).xcframework.log support/$(PYTHON_VER)/macOS/VERSIONS: @echo ">>> Create VERSIONS file for macOS" @@ -549,33 +559,20 @@ dist/Python-$(PYTHON_VER)-macOS-support.$(BUILD_NUMBER).tar.gz: \ else $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ - $$(foreach sdk,$$(SDKS-$(os)),$$(PYTHON_LIB-$$(sdk)) $$(PYTHON_INCLUDE-$$(sdk))/Python.h) + $$(foreach sdk,$$(SDKS-$(os)),$$(PYTHON_STDLIB-$$(sdk))/LICENSE.TXT) @echo ">>> Create Python.XCFramework on $(os)" mkdir -p $$(dir $$(PYTHON_XCFRAMEWORK-$(os))) xcodebuild -create-xcframework \ - -output $$(PYTHON_XCFRAMEWORK-$(os)) $$(foreach sdk,$$(SDKS-$(os)),-library $$(PYTHON_LIB-$$(sdk)) -headers $$(PYTHON_INCLUDE-$$(sdk))) \ + -output $$(PYTHON_XCFRAMEWORK-$(os)) $$(foreach sdk,$$(SDKS-$(os)),-framework $$(PYTHON_FRAMEWORK-$$(sdk))) \ 2>&1 | tee -a support/$(PYTHON_VER)/python-$(os).xcframework.log -$$(PYTHON_STDLIB-$(os))/VERSIONS: \ - $$(foreach sdk,$$(SDKS-$(os)),$$(PYTHON_STDLIB-$$(sdk))/LICENSE.TXT) - @echo ">>> Create Python stdlib on $(os)" - # Copy stdlib from first SDK in $(os) - cp -r $$(PYTHON_STDLIB-$$(firstword $$(SDKS-$(os)))) $$(PYTHON_STDLIB-$(os)) - - # Delete the single-SDK stdlib artefacts from $(os) - rm -rf \ - $$(PYTHON_STDLIB-$(os))/_sysconfigdata__*.py \ - $$(PYTHON_STDLIB-$(os))/config-* \ - $$(PYTHON_STDLIB-$(os))/lib-dynload/* - - # Copy the config-* contents from every SDK in $(os) into the support folder. - $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_STDLIB-$$(sdk))/config-$(PYTHON_VER)-* $$(PYTHON_STDLIB-$(os)); ) - - # Copy the _sysconfigdata modules from every SDK in $(os) into the support folder. - $$(foreach sdk,$$(SDKS-$(os)),cp $$(PYTHON_STDLIB-$$(sdk))/_sysconfigdata__*.py $$(PYTHON_STDLIB-$(os)); ) + @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)); ) + $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/lib $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) - # Copy the lib-dynload contents from every SDK in $(os) into the support folder. - $$(foreach sdk,$$(SDKS-$(os)),cp $$(PYTHON_STDLIB-$$(sdk))/lib-dynload/* $$(PYTHON_STDLIB-$(os))/lib-dynload; ) + @echo ">>> Create helper links in XCframework for $(os)" + $$(foreach sdk,$$(SDKS-$(os)),ln -si $$(SDK_SLICE-$$(sdk)) $$(PYTHON_XCFRAMEWORK-$(os))/$$(sdk); ) @echo ">>> Create VERSIONS file for $(os)" echo "Python version: $(PYTHON_VERSION) " > support/$(PYTHON_VER)/$(os)/VERSIONS @@ -589,15 +586,12 @@ $$(PYTHON_STDLIB-$(os))/VERSIONS: \ dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz: \ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist \ - $$(PYTHON_STDLIB-$(os))/VERSIONS \ $$(foreach target,$$(TARGETS-$(os)), $$(PYTHON_SITECUSTOMIZE-$$(target))) @echo ">>> Create final distribution artefact for $(os)" mkdir -p dist - # Build a "full" tarball with all content for test purposes - tar zcvf dist/Python-$(PYTHON_VER)-$(os)-support.test-$(BUILD_NUMBER).tar.gz -X patch/Python/test.exclude -C support/$(PYTHON_VER)/$(os) `ls -A support/$(PYTHON_VER)/$(os)/` # Build a distributable tarball - tar zcvf $$@ -X patch/Python/release.common.exclude -X patch/Python/release.$(os).exclude -C support/$(PYTHON_VER)/$(os) `ls -A support/$(PYTHON_VER)/$(os)/` + tar zcvf $$@ -X patch/Python/release.$(os).exclude -C support/$(PYTHON_VER)/$(os) `ls -A support/$(PYTHON_VER)/$(os)/` endif @@ -609,6 +603,7 @@ clean-$(os): install/$(os)/*/python-$(PYTHON_VER)* \ install/$(os)/*/python-$(PYTHON_VER)*.*.log \ support/$(PYTHON_VER)/$(os) \ + support/$(PYTHON_VER)/python-$(os).*.log \ dist/Python-$(PYTHON_VER)-$(os)-* dev-clean-$(os): diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 72674183..cd30184a 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1152,6 +1152,139 @@ index 8b0628745c..2d8de8aecb 100755 # # Platform support for Windows +diff --git a/Makefile.pre.in b/Makefile.pre.in +index 4996c5c309..24ee442643 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -184,6 +184,8 @@ + PYTHONFRAMEWORKDIR= @PYTHONFRAMEWORKDIR@ + PYTHONFRAMEWORKPREFIX= @PYTHONFRAMEWORKPREFIX@ + PYTHONFRAMEWORKINSTALLDIR= @PYTHONFRAMEWORKINSTALLDIR@ ++PYTHONFRAMEWORKINSTALLNAMEPREFIX= @PYTHONFRAMEWORKINSTALLNAMEPREFIX@ ++RESSRCDIR= @RESSRCDIR@ + # Deployment target selected during configure, to be checked + # by distutils. The export statement is needed to ensure that the + # deployment target is active during build. +@@ -843,7 +845,7 @@ + $(BLDSHARED) $(NO_AS_NEEDED) -o $@ -Wl,-h$@ $^ + + libpython$(LDVERSION).dylib: $(LIBRARY_OBJS) +- $(CC) -dynamiclib -Wl,-single_module $(PY_CORE_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(DTRACE_OBJS) $(SHLIBS) $(LIBC) $(LIBM); \ ++ $(CC) -dynamiclib $(PY_CORE_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(PYTHONFRAMEWORKINSTALLNAMEPREFIX)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(DTRACE_OBJS) $(SHLIBS) $(LIBC) $(LIBM); \ + + + libpython$(VERSION).sl: $(LIBRARY_OBJS) +@@ -868,14 +870,13 @@ + # This rule is here for OPENSTEP/Rhapsody/MacOSX. It builds a temporary + # minimal framework (not including the Lib directory and such) in the current + # directory. +-RESSRCDIR=Mac/Resources/framework + $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK): \ + $(LIBRARY) \ + $(RESSRCDIR)/Info.plist + $(INSTALL) -d -m $(DIRMODE) $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION) + $(CC) -o $(LDLIBRARY) $(PY_CORE_LDFLAGS) -dynamiclib \ +- -all_load $(LIBRARY) -Wl,-single_module \ +- -install_name $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK) \ ++ -all_load $(LIBRARY) \ ++ -install_name $(PYTHONFRAMEWORKINSTALLNAMEPREFIX)/Versions/$(VERSION)/$(PYTHONFRAMEWORK) \ + -compatibility_version $(VERSION) \ + -current_version $(VERSION) \ + -framework CoreFoundation $(LIBS); +@@ -887,6 +888,21 @@ + $(LN) -fsn Versions/Current/$(PYTHONFRAMEWORK) $(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK) + $(LN) -fsn Versions/Current/Resources $(PYTHONFRAMEWORKDIR)/Resources + ++# This rule is for iOS, which requires an annoyingly just slighly different ++# format for frameworks to macOS. It *doesn't* use a versioned framework, and ++# the Info.plist must be in the root of the framework. ++$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK): \ ++ $(LIBRARY) \ ++ $(RESSRCDIR)/Info.plist ++ $(INSTALL) -d -m $(DIRMODE) $(PYTHONFRAMEWORKDIR) ++ $(CC) -o $(LDLIBRARY) $(PY_CORE_LDFLAGS) -dynamiclib \ ++ -all_load $(LIBRARY) \ ++ -install_name $(PYTHONFRAMEWORKINSTALLNAMEPREFIX)/$(PYTHONFRAMEWORK) \ ++ -compatibility_version $(VERSION) \ ++ -current_version $(VERSION) \ ++ -framework CoreFoundation $(LIBS); ++ $(INSTALL_DATA) $(RESSRCDIR)/Info.plist $(PYTHONFRAMEWORKDIR)/Info.plist ++ + # This rule builds the Cygwin Python DLL and import library if configured + # for a shared core library; otherwise, this rule is a noop. + $(DLLLIBRARY) libpython$(LDVERSION).dll.a: $(LIBRARY_OBJS) +@@ -1924,7 +1940,7 @@ + # which can lead to two parallel `./python setup.py build` processes that + # step on each others toes. + .PHONY: install +-install: @FRAMEWORKINSTALLFIRST@ commoninstall bininstall maninstall @FRAMEWORKINSTALLLAST@ ++install: @FRAMEWORKINSTALLFIRST@ @INSTALLTARGETS@ @FRAMEWORKINSTALLLAST@ + if test "x$(ENSUREPIP)" != "xno" ; then \ + case $(ENSUREPIP) in \ + upgrade) ensurepip="--upgrade" ;; \ +@@ -2507,20 +2523,32 @@ + exit 1; \ + else true; \ + fi +- @for i in $(prefix)/Resources/English.lproj $(prefix)/lib; do\ +- if test ! -d $(DESTDIR)$$i; then \ +- echo "Creating directory $(DESTDIR)$$i"; \ +- $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$$i; \ +- else true; \ +- fi; \ +- done +- $(LN) -fsn include/python$(LDVERSION) $(DESTDIR)$(prefix)/Headers +- sed 's/%VERSION%/'"`$(RUNSHARED) ./$(BUILDPYTHON) -c 'import platform; print(platform.python_version())'`"'/g' < $(RESSRCDIR)/Info.plist > $(DESTDIR)$(prefix)/Resources/Info.plist +- $(LN) -fsn $(VERSION) $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Versions/Current +- $(LN) -fsn Versions/Current/$(PYTHONFRAMEWORK) $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/$(PYTHONFRAMEWORK) +- $(LN) -fsn Versions/Current/Headers $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Headers +- $(LN) -fsn Versions/Current/Resources $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Resources +- $(INSTALL_SHARED) $(LDLIBRARY) $(DESTDIR)$(PYTHONFRAMEWORKPREFIX)/$(LDLIBRARY) ++ # iOS/tvOS/watchOS uses a non-versioned framework with Info.plist in the ++ # framework root, no .lproj data, and binaries ++ @if test "$(MACHDEP)" = ios -o "$(MACHDEP)" = tvos -o "$(MACHDEP)" = watchos; then \ ++ $(INSTALL) -d -m $(DIRMODE) $(PYTHONFRAMEWORKINSTALLDIR); \ ++ sed 's/%VERSION%/'"`$(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import platform; print(platform.python_version())'`"'/g' < $(RESSRCDIR)/Info.plist > $(PYTHONFRAMEWORKINSTALLDIR)/Info.plist; \ ++ $(INSTALL_SHARED) $(LDLIBRARY) $(PYTHONFRAMEWORKPREFIX)/$(LDLIBRARY); \ ++ $(INSTALL) -d -m $(DIRMODE) $(BINDIR); \ ++ for file in $(RESSRCDIR)/bin/* ; do \ ++ $(INSTALL) -m $(EXEMODE) $$file $(BINDIR); \ ++ done; \ ++ else \ ++ for i in $(prefix)/Resources/English.lproj $(prefix)/lib; do \ ++ if test ! -d $(DESTDIR)$$i; then \ ++ echo "Creating directory $(DESTDIR)$$i"; \ ++ $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$$i; \ ++ else true; \ ++ fi; \ ++ done; \ ++ $(LN) -fsn include/python$(LDVERSION) $(DESTDIR)$(prefix)/Headers; \ ++ sed 's/%VERSION%/'"`$(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import platform; print(platform.python_version())'`"'/g' < $(RESSRCDIR)/Info.plist > $(DESTDIR)$(prefix)/Resources/Info.plist; \ ++ $(LN) -fsn $(VERSION) $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Versions/Current; \ ++ $(LN) -fsn Versions/Current/$(PYTHONFRAMEWORK) $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/$(PYTHONFRAMEWORK); \ ++ $(LN) -fsn Versions/Current/Headers $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Headers; \ ++ $(LN) -fsn Versions/Current/Resources $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Resources; \ ++ $(INSTALL_SHARED) $(LDLIBRARY) $(DESTDIR)$(PYTHONFRAMEWORKPREFIX)/$(LDLIBRARY); \ ++ fi + + # This installs Mac/Lib into the framework + # Install a number of symlinks to keep software that expects a normal unix +@@ -2562,6 +2590,15 @@ + frameworkinstallextras: + cd Mac && $(MAKE) installextras DESTDIR="$(DESTDIR)" + ++# On iOS, bin/lib can't live inside the framework; include needs to be called ++# "Headers", but *must* be in the framework, and *not* include the `python3.X` ++# subdirectory. The install has put these folders in the same folder as ++# Python.framework; Move the headers to their final framework-compatible home. ++.PHONY: frameworkinstallmobileheaders ++frameworkinstallmobileheaders: ++ mv "$(PYTHONFRAMEWORKPREFIX)/include/python$(VERSION)" "$(PYTHONFRAMEWORKINSTALLDIR)/Headers" ++ $(LN) -fs "$(PYTHONFRAMEWORKDIR)" "$(PYTHONFRAMEWORKPREFIX)/include/python$(VERSION)" ++ + # Build the toplevel Makefile + Makefile.pre: $(srcdir)/Makefile.pre.in config.status + CONFIG_FILES=Makefile.pre CONFIG_HEADERS= ./config.status diff --git a/Misc/platform_triplet.c b/Misc/platform_triplet.c index 3307260544..b5db9e8a80 100644 --- a/Misc/platform_triplet.c @@ -1311,6 +1444,19 @@ index 2898eedc3e..b48a143c34 100644 } /* module level code ********************************************************/ +diff --git a/Modules/getpath.c b/Modules/getpath.c +index 6f76a84e78..e91272f833 100644 +--- a/Modules/getpath.c ++++ b/Modules/getpath.c +@@ -758,7 +758,7 @@ + return winmodule_to_dict(dict, key, PyWin_DLLhModule); + } + #endif +-#elif defined(WITH_NEXT_FRAMEWORK) ++#elif defined(WITH_NEXT_FRAMEWORK) && !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR + static char modPath[MAXPATHLEN + 1]; + static int modPathInitialized = -1; + if (modPathInitialized < 0) { diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index a4d9466559..8f51bef22d 100644 --- a/Modules/mathmodule.c @@ -1696,13 +1842,66 @@ index d74fb6deac..09ebc4287c 100755 # Blank kernel with real OS is always fine. ;; diff --git a/configure b/configure -index c87f518382..69685cd25a 100755 +index c87f518382..45f021d1b1 100755 --- a/configure +++ b/configure -@@ -4247,6 +4247,15 @@ - *-*-cygwin*) - ac_sys_system=Cygwin - ;; +@@ -963,10 +963,13 @@ + CFLAGS + CC + HAS_XCRUN ++WATCHOS_DEPLOYMENT_TARGET ++TVOS_DEPLOYMENT_TARGET ++IOS_DEPLOYMENT_TARGET + EXPORT_MACOSX_DEPLOYMENT_TARGET + CONFIGURE_MACOSX_DEPLOYMENT_TARGET + _PYTHON_HOST_PLATFORM +-MACHDEP ++INSTALLTARGETS + FRAMEWORKINSTALLAPPSPREFIX + FRAMEWORKUNIXTOOLSPREFIX + FRAMEWORKPYTHONW +@@ -974,6 +977,8 @@ + FRAMEWORKALTINSTALLFIRST + FRAMEWORKINSTALLLAST + FRAMEWORKINSTALLFIRST ++RESSRCDIR ++PYTHONFRAMEWORKINSTALLNAMEPREFIX + PYTHONFRAMEWORKINSTALLDIR + PYTHONFRAMEWORKPREFIX + PYTHONFRAMEWORKDIR +@@ -983,6 +988,7 @@ + LIPO_32BIT_FLAGS + ARCH_RUN_32BIT + UNIVERSALSDK ++MACHDEP + PKG_CONFIG_LIBDIR + PKG_CONFIG_PATH + PKG_CONFIG +@@ -3988,6 +3994,86 @@ + as_fn_error $? "pkg-config is required" "$LINENO" 5] + fi + ++# Set name for machine-dependent library files ++ ++{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking MACHDEP" >&5 ++printf %s "checking MACHDEP... " >&6; } ++if test -z "$MACHDEP" ++then ++ # avoid using uname for cross builds ++ if test "$cross_compiling" = yes; then ++ # ac_sys_system and ac_sys_release are used for setting ++ # a lot of different things including 'define_xopen_source' ++ # in the case statement below. ++ case "$host" in ++ *-*-linux-android*) ++ ac_sys_system=Linux-android ++ ;; ++ *-*-linux*) ++ ac_sys_system=Linux ++ ;; ++ *-*-cygwin*) ++ ac_sys_system=Cygwin ++ ;; + *-apple-ios*) + ac_sys_system=iOS + ;; @@ -1712,10 +1911,326 @@ index c87f518382..69685cd25a 100755 + *-apple-watchos*) + ac_sys_system=watchOS + ;; - *-*-vxworks*) - ac_sys_system=VxWorks - ;; -@@ -4303,27 +4312,96 @@ ++ *-*-vxworks*) ++ ac_sys_system=VxWorks ++ ;; ++ *-*-emscripten) ++ ac_sys_system=Emscripten ++ ;; ++ *-*-wasi) ++ ac_sys_system=WASI ++ ;; ++ *) ++ # for now, limit cross builds to known configurations ++ MACHDEP="unknown" ++ as_fn_error $? "cross build not supported for $host" "$LINENO" 5 ++ esac ++ ac_sys_release= ++ else ++ ac_sys_system=`uname -s` ++ if test "$ac_sys_system" = "AIX" \ ++ -o "$ac_sys_system" = "UnixWare" -o "$ac_sys_system" = "OpenUNIX"; then ++ ac_sys_release=`uname -v` ++ else ++ ac_sys_release=`uname -r` ++ fi ++ fi ++ ac_md_system=`echo $ac_sys_system | ++ tr -d '/ ' | tr '[A-Z]' '[a-z]'` ++ ac_md_release=`echo $ac_sys_release | ++ tr -d '/ ' | sed 's/^[A-Z]\.//' | sed 's/\..*//'` ++ MACHDEP="$ac_md_system$ac_md_release" ++ ++ case $MACHDEP in ++ aix*) MACHDEP="aix";; ++ linux*) MACHDEP="linux";; ++ cygwin*) MACHDEP="cygwin";; ++ darwin*) MACHDEP="darwin";; ++ '') MACHDEP="unknown";; ++ esac ++ ++ if test "$ac_sys_system" = "SunOS"; then ++ # For Solaris, there isn't an OS version specific macro defined ++ # in most compilers, so we define one here. ++ SUNOS_VERSION=`echo $ac_sys_release | sed -e 's!\.\(0-9\)$!.0\1!g' | tr -d '.'` ++ ++printf "%s\n" "#define Py_SUNOS_VERSION $SUNOS_VERSION" >>confdefs.h ++ ++ fi ++fi ++{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: \"$MACHDEP\"" >&5 ++printf "%s\n" "\"$MACHDEP\"" >&6; } ++ + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --enable-universalsdk" >&5 + printf %s "checking for --enable-universalsdk... " >&6; } + # Check whether --enable-universalsdk was given. +@@ -4111,11 +4197,15 @@ + PYTHONFRAMEWORKDIR=no-framework + PYTHONFRAMEWORKPREFIX= + PYTHONFRAMEWORKINSTALLDIR= ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX= ++ RESSRCDIR= + FRAMEWORKINSTALLFIRST= + FRAMEWORKINSTALLLAST= + FRAMEWORKALTINSTALLFIRST= + FRAMEWORKALTINSTALLLAST= + FRAMEWORKPYTHONW= ++ INSTALLTARGETS="commoninstall bininstall maninstall" ++ + if test "x${prefix}" = "xNONE"; then + FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}" + else +@@ -4128,65 +4218,112 @@ + PYTHONFRAMEWORKINSTALLDIR=$PYTHONFRAMEWORKPREFIX/$PYTHONFRAMEWORKDIR + FRAMEWORKINSTALLFIRST="frameworkinstallstructure" + FRAMEWORKALTINSTALLFIRST="frameworkinstallstructure " +- FRAMEWORKINSTALLLAST="frameworkinstallmaclib frameworkinstallapps frameworkinstallunixtools" +- FRAMEWORKALTINSTALLLAST="frameworkinstallmaclib frameworkinstallapps frameworkaltinstallunixtools" +- FRAMEWORKPYTHONW="frameworkpythonw" +- FRAMEWORKINSTALLAPPSPREFIX="/Applications" + +- if test "x${prefix}" = "xNONE" ; then +- FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}" ++ case $ac_sys_system in #( ++ iOS) : ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" + +- else +- FRAMEWORKUNIXTOOLSPREFIX="${prefix}" +- fi ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=iOS/Resources + +- case "${enableval}" in +- /System*) +- FRAMEWORKINSTALLAPPSPREFIX="/Applications" +- if test "${prefix}" = "NONE" ; then +- # See below +- FRAMEWORKUNIXTOOLSPREFIX="/usr" +- fi +- ;; ++ ac_config_files="$ac_config_files iOS/Resources/Info.plist" + +- /Library*) +- FRAMEWORKINSTALLAPPSPREFIX="/Applications" +- ;; ++ ;; ++ tvOS) : ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" + +- */Library/Frameworks) +- MDIR="`dirname "${enableval}"`" +- MDIR="`dirname "${MDIR}"`" +- FRAMEWORKINSTALLAPPSPREFIX="${MDIR}/Applications" +- +- if test "${prefix}" = "NONE"; then +- # User hasn't specified the +- # --prefix option, but wants to install +- # the framework in a non-default location, +- # ensure that the compatibility links get +- # installed relative to that prefix as well +- # instead of in /usr/local. +- FRAMEWORKUNIXTOOLSPREFIX="${MDIR}" +- fi +- ;; ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=tvOS/Resources + +- *) +- FRAMEWORKINSTALLAPPSPREFIX="/Applications" +- ;; +- esac ++ ac_config_files="$ac_config_files tvOS/Resources/Info.plist" + +- prefix=$PYTHONFRAMEWORKINSTALLDIR/Versions/$VERSION ++ ;; ++ watchOS) : ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" + +- # Add files for Mac specific code to the list of output +- # files: +- ac_config_files="$ac_config_files Mac/Makefile" ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=watchOS/Resources + +- ac_config_files="$ac_config_files Mac/PythonLauncher/Makefile" ++ ac_config_files="$ac_config_files watchOS/Resources/Info.plist" + +- ac_config_files="$ac_config_files Mac/Resources/framework/Info.plist" ++ ;; ++ *) : ++ FRAMEWORKINSTALLLAST="frameworkinstallmaclib frameworkinstallapps frameworkinstallunixtools" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmaclib frameworkinstallapps frameworkaltinstallunixtools" ++ FRAMEWORKPYTHONW="frameworkpythonw" ++ FRAMEWORKINSTALLAPPSPREFIX="/Applications" ++ INSTALLTARGETS="commoninstall bininstall maninstall" + +- ac_config_files="$ac_config_files Mac/Resources/app/Info.plist" ++ if test "x${prefix}" = "xNONE" ; then ++ FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}" + +- esac ++ else ++ FRAMEWORKUNIXTOOLSPREFIX="${prefix}" ++ fi ++ ++ case "${enableval}" in ++ /System*) ++ FRAMEWORKINSTALLAPPSPREFIX="/Applications" ++ if test "${prefix}" = "NONE" ; then ++ # See below ++ FRAMEWORKUNIXTOOLSPREFIX="/usr" ++ fi ++ ;; ++ ++ /Library*) ++ FRAMEWORKINSTALLAPPSPREFIX="/Applications" ++ ;; ++ ++ */Library/Frameworks) ++ MDIR="`dirname "${enableval}"`" ++ MDIR="`dirname "${MDIR}"`" ++ FRAMEWORKINSTALLAPPSPREFIX="${MDIR}/Applications" ++ ++ if test "${prefix}" = "NONE"; then ++ # User hasn't specified the ++ # --prefix option, but wants to install ++ # the framework in a non-default location, ++ # ensure that the compatibility links get ++ # installed relative to that prefix as well ++ # instead of in /usr/local. ++ FRAMEWORKUNIXTOOLSPREFIX="${MDIR}" ++ fi ++ ;; ++ ++ *) ++ FRAMEWORKINSTALLAPPSPREFIX="/Applications" ++ ;; ++ esac ++ ++ prefix=$PYTHONFRAMEWORKINSTALLDIR/Versions/$VERSION ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX=$(prefix) ++ RESSRCDIR=Mac/Resources/framework ++ ++ # Add files for Mac specific code to the list of output ++ # files: ++ ac_config_files="$ac_config_files Mac/Makefile" ++ ++ ac_config_files="$ac_config_files Mac/PythonLauncher/Makefile" ++ ++ ac_config_files="$ac_config_files Mac/Resources/app/Info.plist" ++ ++ ac_config_files="$ac_config_files Mac/Resources/framework/Info.plist" ++ ++ ;; ++ esac ++ esac + + else $as_nop + +@@ -4194,6 +4331,8 @@ + PYTHONFRAMEWORKDIR=no-framework + PYTHONFRAMEWORKPREFIX= + PYTHONFRAMEWORKINSTALLDIR= ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX= ++ RESSRCDIR= + FRAMEWORKINSTALLFIRST= + FRAMEWORKINSTALLLAST= + FRAMEWORKALTINSTALLFIRST= +@@ -4223,79 +4362,11 @@ + + + +-printf "%s\n" "#define _PYTHONFRAMEWORK \"${PYTHONFRAMEWORK}\"" >>confdefs.h + + +-# Set name for machine-dependent library files + +-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking MACHDEP" >&5 +-printf %s "checking MACHDEP... " >&6; } +-if test -z "$MACHDEP" +-then +- # avoid using uname for cross builds +- if test "$cross_compiling" = yes; then +- # ac_sys_system and ac_sys_release are used for setting +- # a lot of different things including 'define_xopen_source' +- # in the case statement below. +- case "$host" in +- *-*-linux-android*) +- ac_sys_system=Linux-android +- ;; +- *-*-linux*) +- ac_sys_system=Linux +- ;; +- *-*-cygwin*) +- ac_sys_system=Cygwin +- ;; +- *-*-vxworks*) +- ac_sys_system=VxWorks +- ;; +- *-*-emscripten) +- ac_sys_system=Emscripten +- ;; +- *-*-wasi) +- ac_sys_system=WASI +- ;; +- *) +- # for now, limit cross builds to known configurations +- MACHDEP="unknown" +- as_fn_error $? "cross build not supported for $host" "$LINENO" 5 +- esac +- ac_sys_release= +- else +- ac_sys_system=`uname -s` +- if test "$ac_sys_system" = "AIX" \ +- -o "$ac_sys_system" = "UnixWare" -o "$ac_sys_system" = "OpenUNIX"; then +- ac_sys_release=`uname -v` +- else +- ac_sys_release=`uname -r` +- fi +- fi +- ac_md_system=`echo $ac_sys_system | +- tr -d '/ ' | tr '[A-Z]' '[a-z]'` +- ac_md_release=`echo $ac_sys_release | +- tr -d '/ ' | sed 's/^[A-Z]\.//' | sed 's/\..*//'` +- MACHDEP="$ac_md_system$ac_md_release" +- +- case $MACHDEP in +- aix*) MACHDEP="aix";; +- linux*) MACHDEP="linux";; +- cygwin*) MACHDEP="cygwin";; +- darwin*) MACHDEP="darwin";; +- '') MACHDEP="unknown";; +- esac +- +- if test "$ac_sys_system" = "SunOS"; then +- # For Solaris, there isn't an OS version specific macro defined +- # in most compilers, so we define one here. +- SUNOS_VERSION=`echo $ac_sys_release | sed -e 's!\.\(0-9\)$!.0\1!g' | tr -d '.'` +- +-printf "%s\n" "#define Py_SUNOS_VERSION $SUNOS_VERSION" >>confdefs.h ++printf "%s\n" "#define _PYTHONFRAMEWORK \"${PYTHONFRAMEWORK}\"" >>confdefs.h + +- fi +-fi +-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: \"$MACHDEP\"" >&5 +-printf "%s\n" "\"$MACHDEP\"" >&6; } + + + if test "$cross_compiling" = yes; then +@@ -4303,27 +4374,102 @@ *-*-linux*) case "$host_cpu" in arm*) @@ -1732,63 +2247,69 @@ index c87f518382..69685cd25a 100755 + _host_ident= + ;; + *-apple-ios*-simulator) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` ++ _host_os=`echo $host | cut -d '-' -f3` ++ IOS_DEPLOYMENT_TARGET=${_host_os:3} + case "$host_cpu" in + aarch64) -+ _host_ident=${_host_os_min_version:3}-iphonesimulator-arm64 ++ _host_ident=${IOS_DEPLOYMENT_TARGET}-iphonesimulator-arm64 + ;; + *) -+ _host_ident=${_host_os_min_version:3}-iphonesimulator-$host_cpu ++ _host_ident=${IOS_DEPLOYMENT_TARGET}-iphonesimulator-$host_cpu + esac + ;; + *-apple-ios*) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` ++ _host_os=`echo $host | cut -d '-' -f3` ++ IOS_DEPLOYMENT_TARGET=${_host_os:3} + case "$host_cpu" in + aarch64) -+ _host_ident=${_host_os_min_version:3}-iphoneos-arm64 ++ _host_ident=${IOS_DEPLOYMENT_TARGET}-iphoneos-arm64 + ;; + *) -+ _host_ident=${_host_os_min_version:3}-iphoneos-$host_cpu ++ _host_ident=${IOS_DEPLOYMENT_TARGET}-iphoneos-$host_cpu + esac + ;; + *-apple-tvos*-simulator) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` ++ _host_os=`echo $host | cut -d '-' -f3` ++ TVOS_DEPLOYMENT_TARGET=${_host_os:4} + case "$host_cpu" in + aarch64) -+ _host_ident=${_host_os_min_version:3}-appletvsimulator-arm64 ++ _host_ident=${TVOS_DEPLOYMENT_TARGET}-appletvsimulator-arm64 + ;; + *) -+ _host_ident=${_host_os_min_version:3}-appletvsimulator-$host_cpu ++ _host_ident=${TVOS_DEPLOYMENT_TARGET}-appletvsimulator-$host_cpu + esac + ;; + *-apple-tvos*) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` ++ _host_os=`echo $host | cut -d '-' -f3` ++ TVOS_DEPLOYMENT_TARGET=${_host_os:4} + case "$host_cpu" in + aarch64) -+ _host_ident=${_host_os_min_version:3}-appletvos-arm64 ++ _host_ident=${TVOS_DEPLOYMENT_TARGET}-appletvos-arm64 + ;; + *) -+ _host_ident=${_host_os_min_version:3}-appletvos-$host_cpu ++ _host_ident=${TVOS_DEPLOYMENT_TARGET}-appletvos-$host_cpu + esac + ;; + *-apple-watchos*-simulator) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` ++ _host_os=`echo $host | cut -d '-' -f3` ++ WATCHOS_DEPLOYMENT_TARGET=${_host_os:7} + case "$host_cpu" in + aarch64) -+ _host_ident=${_host_os_min_version:3}-watchsimualtor-arm64 ++ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-watchsimulator-arm64 + ;; + *) -+ _host_ident=${_host_os_min_version:3}-watchsimualtor-$host_cpu ++ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-watchsimulator-$host_cpu + esac + ;; + *-apple-watchos*) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` ++ _host_os=`echo $host | cut -d '-' -f3` ++ WATCHOS_DEPLOYMENT_TARGET=${_host_os:7} + case "$host_cpu" in + aarch64) -+ _host_ident=${_host_os_min_version:3}-watchosos-arm64_32 ++ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-watchosos-arm64_32 + ;; + *) -+ _host_ident=${_host_os_min_version:3}-watchosos-$host_cpu ++ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-watchosos-$host_cpu + esac + ;; + *-apple-*) @@ -1818,7 +2339,7 @@ index c87f518382..69685cd25a 100755 fi # Some systems cannot stand _XOPEN_SOURCE being defined at all; they -@@ -4390,6 +4468,13 @@ +@@ -4390,6 +4536,13 @@ define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; @@ -1832,7 +2353,40 @@ index c87f518382..69685cd25a 100755 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -6746,6 +6831,12 @@ +@@ -4484,6 +4637,32 @@ + ;; + esac + ++case $ac_sys_system in #( ++ iOS) : ++ ++ IOS_DEPLOYMENT_TARGET=${IOS_DEPLOYMENT_TARGET:=12.0} ++ as_fn_append CFLAGS " -mios-version-min=${IOS_DEPLOYMENT_TARGET}" ++ as_fn_append LDFLAGS " -mios-version-min=${IOS_DEPLOYMENT_TARGET}" ++ ++ ;; #( ++ tvOS) : ++ ++ TVOS_DEPLOYMENT_TARGET=${TVOS_DEPLOYMENT_TARGET:=9.0} ++ as_fn_append CFLAGS " -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}" ++ as_fn_append LDFLAGS " -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}" ++ ++ ;; #( ++ watchOS) : ++ ++ WATCHOS_DEPLOYMENT_TARGET=${WATCHOS_DEPLOYMENT_TARGET:=4.0} ++ as_fn_append CFLAGS " -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}" ++ as_fn_append LDFLAGS " -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}" ++ ++ ;; #( ++ *) : ++ ;; ++esac ++ + if test "$ac_sys_system" = "Darwin" + then + # Extract the first word of "xcrun", so it can be a program name with args. +@@ -6746,6 +6925,12 @@ case $ac_sys_system in #( Darwin*) : MULTIARCH="" ;; #( @@ -1845,17 +2399,16 @@ index c87f518382..69685cd25a 100755 FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -6753,9 +6844,6 @@ +@@ -6753,8 +6938,6 @@ ;; esac -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MULTIARCH" >&5 -printf "%s\n" "$MULTIARCH" >&6; } -- + if test x$PLATFORM_TRIPLET != x && test x$MULTIARCH != x; then if test x$PLATFORM_TRIPLET != x$MULTIARCH; then - as_fn_error $? "internal configure error for the platform triplet, please file a bug report" "$LINENO" 5 -@@ -6764,6 +6852,16 @@ +@@ -6764,6 +6947,16 @@ MULTIARCH=$PLATFORM_TRIPLET fi @@ -1872,7 +2425,7 @@ index c87f518382..69685cd25a 100755 if test x$MULTIARCH != x; then MULTIARCH_CPPFLAGS="-DMULTIARCH=\\\"$MULTIARCH\\\"" -@@ -6807,8 +6905,14 @@ +@@ -6807,6 +7000,12 @@ PY_SUPPORT_TIER=3 ;; #( x86_64-*-freebsd*/clang) : PY_SUPPORT_TIER=3 ;; #( @@ -1883,12 +2436,58 @@ index c87f518382..69685cd25a 100755 + aarch64-apple-ios*/clang) : + PY_SUPPORT_TIER=3 ;; #( *) : -- PY_SUPPORT_TIER=0 -+ PY_SUPPORT_TIER=0 + PY_SUPPORT_TIER=0 ;; - esac - -@@ -12515,6 +12619,7 @@ +@@ -7257,17 +7456,23 @@ + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking LDLIBRARY" >&5 + printf %s "checking LDLIBRARY... " >&6; } + +-# MacOSX framework builds need more magic. LDLIBRARY is the dynamic ++# iOS/MacOSX framework builds need more magic. LDLIBRARY is the dynamic + # library that we build, but we do not want to link against it (we + # will find it with a -framework option). For this reason there is an + # extra variable BLDLIBRARY against which Python and the extension + # modules are linked, BLDLIBRARY. This is normally the same as +-# LDLIBRARY, but empty for MacOSX framework builds. ++# LDLIBRARY, but empty for MacOSX framework builds. iOS does the same, ++# but uses a non-versioned framework layout. + if test "$enable_framework" + then +- LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' +- RUNSHARED=DYLD_FRAMEWORK_PATH=`pwd`${DYLD_FRAMEWORK_PATH:+:${DYLD_FRAMEWORK_PATH}} ++ case $ac_sys_system in ++ iOS|tvOS|watchOS) ++ LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; ++ *) ++ LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; ++ esac + BLDLIBRARY='' ++ RUNSHARED=DYLD_FRAMEWORK_PATH=`pwd`${DYLD_FRAMEWORK_PATH:+:${DYLD_FRAMEWORK_PATH}} + else + BLDLIBRARY='$(LDLIBRARY)' + fi +@@ -7317,12 +7522,16 @@ + ;; + Darwin*) + LDLIBRARY='libpython$(LDVERSION).dylib' +- BLDLIBRARY='-L. -lpython$(LDVERSION)' +- RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ++ BLDLIBRARY='-L. -lpython$(LDVERSION)' ++ RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ++ ;; ++ iOS|tvOS|watchOS) ++ LDLIBRARY='libpython$(LDVERSION).dylib' ++ BLDLIBRARY='-L. -lpython$(LDVERSION)' + ;; + AIX*) +- LDLIBRARY='libpython$(LDVERSION).so' +- RUNSHARED=LIBPATH=`pwd`${LIBPATH:+:${LIBPATH}} ++ LDLIBRARY='libpython$(LDVERSION).so' ++ RUNSHARED=LIBPATH=`pwd`${LIBPATH:+:${LIBPATH}} + ;; + + esac +@@ -12515,6 +12724,7 @@ esac ;; CYGWIN*) SHLIB_SUFFIX=.dll;; @@ -1896,17 +2495,44 @@ index c87f518382..69685cd25a 100755 *) SHLIB_SUFFIX=.so;; esac fi -@@ -12597,6 +12702,9 @@ +@@ -12597,6 +12807,11 @@ BLDSHARED="$LDSHARED" fi ;; + iOS/*|tvOS/*|watchOS/*) -+ LDSHARED='$(CC) -dynamiclib -undefined dynamic_lookup' -+ LDCXXSHARED='$(CXX) -dynamiclib -undefined dynamic_lookup';; ++ LDSHARED='$(CC) -dynamiclib -F . -framework Python' ++ LDCXXSHARED='$(CXX) -dynamiclib -F . -framework Python' ++ BLDSHARED="$LDSHARED" ++ ;; Emscripten|WASI) LDSHARED='$(CC) -shared' LDCXXSHARED='$(CXX) -shared';; -@@ -14138,6 +14246,10 @@ +@@ -12750,6 +12965,24 @@ + LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' + fi + LINKFORSHARED="$LINKFORSHARED";; ++ iOS/*|tvOS/*|watchOS/*) ++ LINKFORSHARED="$extra_undefs -framework CoreFoundation" ++ ++ # Issue #18075: the default maximum stack size (8MBytes) is too ++ # small for the default recursion limit. Increase the stack size ++ # to ensure that tests don't crash ++ stack_size="1000000" # 16 MB ++ if test "$with_ubsan" = "yes" ++ then ++ # Undefined behavior sanitizer requires an even deeper stack ++ stack_size="4000000" # 64 MB ++ fi ++ ++ ++printf "%s\n" "#define THREAD_STACK_SIZE 0x$stack_size" >>confdefs.h ++ ++ ++ LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; + OpenUNIX*|UnixWare*) LINKFORSHARED="-Wl,-Bexport";; + SCO_SV*) LINKFORSHARED="-Wl,-Bexport";; + ReliantUNIX*) LINKFORSHARED="-W1 -Blargedynsym";; +@@ -14138,6 +14371,10 @@ ctypes_malloc_closure=yes ;; #( @@ -1917,7 +2543,7 @@ index c87f518382..69685cd25a 100755 sunos5) : as_fn_append LIBFFI_LIBS " -mimpure-text" ;; #( -@@ -23651,7 +23763,7 @@ +@@ -23651,7 +23888,7 @@ printf "%s\n" "$ABIFLAGS" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking SOABI" >&5 printf %s "checking SOABI... " >&6; } @@ -1926,7 +2552,7 @@ index c87f518382..69685cd25a 100755 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $SOABI" >&5 printf "%s\n" "$SOABI" >&6; } -@@ -23660,7 +23772,7 @@ +@@ -23660,7 +23897,7 @@ if test "$Py_DEBUG" = 'true'; then # Similar to SOABI but remove "d" flag from ABIFLAGS @@ -1935,7 +2561,7 @@ index c87f518382..69685cd25a 100755 printf "%s\n" "#define ALT_SOABI \"${ALT_SOABI}\"" >>confdefs.h -@@ -27949,6 +28061,28 @@ +@@ -27949,6 +28186,28 @@ ;; #( Darwin) : ;; #( @@ -1964,19 +2590,49 @@ index c87f518382..69685cd25a 100755 CYGWIN*) : -@@ -32186,4 +32320,3 @@ - CPython core team, see https://peps.python.org/pep-0011/ for more information. - " >&2;} - fi -- +@@ -31528,10 +31787,13 @@ + do + case $ac_config_target in + "pyconfig.h") CONFIG_HEADERS="$CONFIG_HEADERS pyconfig.h" ;; ++ "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" ;; + "Mac/Makefile") CONFIG_FILES="$CONFIG_FILES Mac/Makefile" ;; + "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" ;; ++ "Mac/Resources/framework/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/framework/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 cd69f0ede5..135036bf67 100644 +index cd69f0ede5..a96ca3a3c7 100644 --- a/configure.ac +++ b/configure.ac -@@ -553,6 +553,15 @@ - *-*-cygwin*) - ac_sys_system=Cygwin - ;; +@@ -310,6 +310,83 @@ + AC_MSG_ERROR([pkg-config is required])] + fi + ++# Set name for machine-dependent library files ++AC_ARG_VAR([MACHDEP], [name for machine-dependent library files]) ++AC_MSG_CHECKING([MACHDEP]) ++if test -z "$MACHDEP" ++then ++ # avoid using uname for cross builds ++ if test "$cross_compiling" = yes; then ++ # ac_sys_system and ac_sys_release are used for setting ++ # a lot of different things including 'define_xopen_source' ++ # in the case statement below. ++ case "$host" in ++ *-*-linux-android*) ++ ac_sys_system=Linux-android ++ ;; ++ *-*-linux*) ++ ac_sys_system=Linux ++ ;; ++ *-*-cygwin*) ++ ac_sys_system=Cygwin ++ ;; + *-apple-ios*) + ac_sys_system=iOS + ;; @@ -1986,10 +2642,322 @@ index cd69f0ede5..135036bf67 100644 + *-apple-watchos*) + ac_sys_system=watchOS + ;; - *-*-vxworks*) - ac_sys_system=VxWorks - ;; -@@ -607,27 +616,96 @@ ++ *-*-vxworks*) ++ ac_sys_system=VxWorks ++ ;; ++ *-*-emscripten) ++ ac_sys_system=Emscripten ++ ;; ++ *-*-wasi) ++ ac_sys_system=WASI ++ ;; ++ *) ++ # for now, limit cross builds to known configurations ++ MACHDEP="unknown" ++ AC_MSG_ERROR([cross build not supported for $host]) ++ esac ++ ac_sys_release= ++ else ++ ac_sys_system=`uname -s` ++ if test "$ac_sys_system" = "AIX" \ ++ -o "$ac_sys_system" = "UnixWare" -o "$ac_sys_system" = "OpenUNIX"; then ++ ac_sys_release=`uname -v` ++ else ++ ac_sys_release=`uname -r` ++ fi ++ fi ++ ac_md_system=`echo $ac_sys_system | ++ tr -d '[/ ]' | tr '[[A-Z]]' '[[a-z]]'` ++ ac_md_release=`echo $ac_sys_release | ++ tr -d '[/ ]' | sed 's/^[[A-Z]]\.//' | sed 's/\..*//'` ++ MACHDEP="$ac_md_system$ac_md_release" ++ ++ case $MACHDEP in ++ aix*) MACHDEP="aix";; ++ linux*) MACHDEP="linux";; ++ cygwin*) MACHDEP="cygwin";; ++ darwin*) MACHDEP="darwin";; ++ '') MACHDEP="unknown";; ++ esac ++ ++ if test "$ac_sys_system" = "SunOS"; then ++ # For Solaris, there isn't an OS version specific macro defined ++ # in most compilers, so we define one here. ++ SUNOS_VERSION=`echo $ac_sys_release | sed -e 's!\.\([0-9]\)$!.0\1!g' | tr -d '.'` ++ AC_DEFINE_UNQUOTED([Py_SUNOS_VERSION], [$SUNOS_VERSION], ++ [The version of SunOS/Solaris as reported by `uname -r' without the dot.]) ++ fi ++fi ++AC_MSG_RESULT(["$MACHDEP"]) ++ + AC_MSG_CHECKING([for --enable-universalsdk]) + AC_ARG_ENABLE([universalsdk], + AS_HELP_STRING([--enable-universalsdk@<:@=SDKDIR@:>@], +@@ -427,11 +504,15 @@ + PYTHONFRAMEWORKDIR=no-framework + PYTHONFRAMEWORKPREFIX= + PYTHONFRAMEWORKINSTALLDIR= ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX= ++ RESSRCDIR= + FRAMEWORKINSTALLFIRST= + FRAMEWORKINSTALLLAST= + FRAMEWORKALTINSTALLFIRST= + FRAMEWORKALTINSTALLLAST= + FRAMEWORKPYTHONW= ++ INSTALLTARGETS="commoninstall bininstall maninstall" ++ + if test "x${prefix}" = "xNONE"; then + FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}" + else +@@ -444,66 +525,112 @@ + PYTHONFRAMEWORKINSTALLDIR=$PYTHONFRAMEWORKPREFIX/$PYTHONFRAMEWORKDIR + FRAMEWORKINSTALLFIRST="frameworkinstallstructure" + FRAMEWORKALTINSTALLFIRST="frameworkinstallstructure " +- FRAMEWORKINSTALLLAST="frameworkinstallmaclib frameworkinstallapps frameworkinstallunixtools" +- FRAMEWORKALTINSTALLLAST="frameworkinstallmaclib frameworkinstallapps frameworkaltinstallunixtools" +- FRAMEWORKPYTHONW="frameworkpythonw" +- FRAMEWORKINSTALLAPPSPREFIX="/Applications" + +- if test "x${prefix}" = "xNONE" ; then +- FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}" ++ case $ac_sys_system in #( ++ iOS) : ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" + +- else +- FRAMEWORKUNIXTOOLSPREFIX="${prefix}" +- fi ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=iOS/Resources + +- case "${enableval}" in +- /System*) +- FRAMEWORKINSTALLAPPSPREFIX="/Applications" +- if test "${prefix}" = "NONE" ; then +- # See below +- FRAMEWORKUNIXTOOLSPREFIX="/usr" +- fi +- ;; ++ AC_CONFIG_FILES([iOS/Resources/Info.plist]) ++ ;; ++ tvOS) : ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" + +- /Library*) +- FRAMEWORKINSTALLAPPSPREFIX="/Applications" +- ;; ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=tvOS/Resources + +- */Library/Frameworks) +- MDIR="`dirname "${enableval}"`" +- MDIR="`dirname "${MDIR}"`" +- FRAMEWORKINSTALLAPPSPREFIX="${MDIR}/Applications" +- +- if test "${prefix}" = "NONE"; then +- # User hasn't specified the +- # --prefix option, but wants to install +- # the framework in a non-default location, +- # ensure that the compatibility links get +- # installed relative to that prefix as well +- # instead of in /usr/local. +- FRAMEWORKUNIXTOOLSPREFIX="${MDIR}" +- fi +- ;; ++ AC_CONFIG_FILES([tvOS/Resources/Info.plist]) ++ ;; ++ watchOS) : ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" + +- *) +- FRAMEWORKINSTALLAPPSPREFIX="/Applications" +- ;; +- esac ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=watchOS/Resources + +- prefix=$PYTHONFRAMEWORKINSTALLDIR/Versions/$VERSION ++ AC_CONFIG_FILES([watchOS/Resources/Info.plist]) ++ ;; ++ *) : ++ FRAMEWORKINSTALLLAST="frameworkinstallmaclib frameworkinstallapps frameworkinstallunixtools" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmaclib frameworkinstallapps frameworkaltinstallunixtools" ++ FRAMEWORKPYTHONW="frameworkpythonw" ++ FRAMEWORKINSTALLAPPSPREFIX="/Applications" ++ INSTALLTARGETS="commoninstall bininstall maninstall" + +- # Add files for Mac specific code to the list of output +- # files: +- AC_CONFIG_FILES([Mac/Makefile]) +- AC_CONFIG_FILES([Mac/PythonLauncher/Makefile]) +- AC_CONFIG_FILES([Mac/Resources/framework/Info.plist]) +- AC_CONFIG_FILES([Mac/Resources/app/Info.plist]) +- esac ++ if test "x${prefix}" = "xNONE" ; then ++ FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}" ++ ++ else ++ FRAMEWORKUNIXTOOLSPREFIX="${prefix}" ++ fi ++ ++ case "${enableval}" in ++ /System*) ++ FRAMEWORKINSTALLAPPSPREFIX="/Applications" ++ if test "${prefix}" = "NONE" ; then ++ # See below ++ FRAMEWORKUNIXTOOLSPREFIX="/usr" ++ fi ++ ;; ++ ++ /Library*) ++ FRAMEWORKINSTALLAPPSPREFIX="/Applications" ++ ;; ++ ++ */Library/Frameworks) ++ MDIR="`dirname "${enableval}"`" ++ MDIR="`dirname "${MDIR}"`" ++ FRAMEWORKINSTALLAPPSPREFIX="${MDIR}/Applications" ++ ++ if test "${prefix}" = "NONE"; then ++ # User hasn't specified the ++ # --prefix option, but wants to install ++ # the framework in a non-default location, ++ # ensure that the compatibility links get ++ # installed relative to that prefix as well ++ # instead of in /usr/local. ++ FRAMEWORKUNIXTOOLSPREFIX="${MDIR}" ++ fi ++ ;; ++ ++ *) ++ FRAMEWORKINSTALLAPPSPREFIX="/Applications" ++ ;; ++ esac ++ ++ prefix=$PYTHONFRAMEWORKINSTALLDIR/Versions/$VERSION ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX=$(prefix) ++ RESSRCDIR=Mac/Resources/framework ++ ++ # Add files for Mac specific code to the list of output ++ # files: ++ AC_CONFIG_FILES([Mac/Makefile]) ++ AC_CONFIG_FILES([Mac/PythonLauncher/Makefile]) ++ AC_CONFIG_FILES([Mac/Resources/app/Info.plist]) ++ AC_CONFIG_FILES([Mac/Resources/framework/Info.plist]) ++ ;; ++ esac ++ esac + ],[ + PYTHONFRAMEWORK= + PYTHONFRAMEWORKDIR=no-framework + PYTHONFRAMEWORKPREFIX= + PYTHONFRAMEWORKINSTALLDIR= ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX= ++ RESSRCDIR= + FRAMEWORKINSTALLFIRST= + FRAMEWORKINSTALLLAST= + FRAMEWORKALTINSTALLFIRST= +@@ -522,6 +649,8 @@ + AC_SUBST([PYTHONFRAMEWORKDIR]) + AC_SUBST([PYTHONFRAMEWORKPREFIX]) + AC_SUBST([PYTHONFRAMEWORKINSTALLDIR]) ++AC_SUBST([PYTHONFRAMEWORKINSTALLNAMEPREFIX]) ++AC_SUBST([RESSRCDIR]) + AC_SUBST([FRAMEWORKINSTALLFIRST]) + AC_SUBST([FRAMEWORKINSTALLLAST]) + AC_SUBST([FRAMEWORKALTINSTALLFIRST]) +@@ -529,105 +658,113 @@ + AC_SUBST([FRAMEWORKPYTHONW]) + AC_SUBST([FRAMEWORKUNIXTOOLSPREFIX]) + AC_SUBST([FRAMEWORKINSTALLAPPSPREFIX]) ++AC_SUBST([INSTALLTARGETS]) + + AC_DEFINE_UNQUOTED([_PYTHONFRAMEWORK], ["${PYTHONFRAMEWORK}"], + [framework name]) + +-# Set name for machine-dependent library files +-AC_ARG_VAR([MACHDEP], [name for machine-dependent library files]) +-AC_MSG_CHECKING([MACHDEP]) +-if test -z "$MACHDEP" +-then +- # avoid using uname for cross builds +- if test "$cross_compiling" = yes; then +- # ac_sys_system and ac_sys_release are used for setting +- # a lot of different things including 'define_xopen_source' +- # in the case statement below. +- case "$host" in +- *-*-linux-android*) +- ac_sys_system=Linux-android +- ;; +- *-*-linux*) +- ac_sys_system=Linux +- ;; +- *-*-cygwin*) +- ac_sys_system=Cygwin +- ;; +- *-*-vxworks*) +- ac_sys_system=VxWorks +- ;; +- *-*-emscripten) +- ac_sys_system=Emscripten +- ;; +- *-*-wasi) +- ac_sys_system=WASI +- ;; +- *) +- # for now, limit cross builds to known configurations +- MACHDEP="unknown" +- AC_MSG_ERROR([cross build not supported for $host]) +- esac +- ac_sys_release= +- else +- ac_sys_system=`uname -s` +- if test "$ac_sys_system" = "AIX" \ +- -o "$ac_sys_system" = "UnixWare" -o "$ac_sys_system" = "OpenUNIX"; then +- ac_sys_release=`uname -v` +- else +- ac_sys_release=`uname -r` +- fi +- fi +- ac_md_system=`echo $ac_sys_system | +- tr -d '[/ ]' | tr '[[A-Z]]' '[[a-z]]'` +- ac_md_release=`echo $ac_sys_release | +- tr -d '[/ ]' | sed 's/^[[A-Z]]\.//' | sed 's/\..*//'` +- MACHDEP="$ac_md_system$ac_md_release" +- +- case $MACHDEP in +- aix*) MACHDEP="aix";; +- linux*) MACHDEP="linux";; +- cygwin*) MACHDEP="cygwin";; +- darwin*) MACHDEP="darwin";; +- '') MACHDEP="unknown";; +- esac +- +- if test "$ac_sys_system" = "SunOS"; then +- # For Solaris, there isn't an OS version specific macro defined +- # in most compilers, so we define one here. +- SUNOS_VERSION=`echo $ac_sys_release | sed -e 's!\.\([0-9]\)$!.0\1!g' | tr -d '.'` +- AC_DEFINE_UNQUOTED([Py_SUNOS_VERSION], [$SUNOS_VERSION], +- [The version of SunOS/Solaris as reported by `uname -r' without the dot.]) +- fi +-fi +-AC_MSG_RESULT(["$MACHDEP"]) +- + AC_SUBST([_PYTHON_HOST_PLATFORM]) + if test "$cross_compiling" = yes; then + case "$host" in *-*-linux*) case "$host_cpu" in arm*) @@ -2006,63 +2974,69 @@ index cd69f0ede5..135036bf67 100644 + _host_ident= + ;; + *-apple-ios*-simulator) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` ++ _host_os=`echo $host | cut -d '-' -f3` ++ IOS_DEPLOYMENT_TARGET=${_host_os:3} + case "$host_cpu" in + aarch64) -+ _host_ident=${_host_os_min_version:3}-iphonesimulator-arm64 ++ _host_ident=${IOS_DEPLOYMENT_TARGET}-iphonesimulator-arm64 + ;; + *) -+ _host_ident=${_host_os_min_version:3}-iphonesimulator-$host_cpu ++ _host_ident=${IOS_DEPLOYMENT_TARGET}-iphonesimulator-$host_cpu + esac + ;; + *-apple-ios*) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` ++ _host_os=`echo $host | cut -d '-' -f3` ++ IOS_DEPLOYMENT_TARGET=${_host_os:3} + case "$host_cpu" in + aarch64) -+ _host_ident=${_host_os_min_version:3}-iphoneos-arm64 ++ _host_ident=${IOS_DEPLOYMENT_TARGET}-iphoneos-arm64 + ;; + *) -+ _host_ident=${_host_os_min_version:3}-iphoneos-$host_cpu ++ _host_ident=${IOS_DEPLOYMENT_TARGET}-iphoneos-$host_cpu + esac + ;; + *-apple-tvos*-simulator) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` ++ _host_os=`echo $host | cut -d '-' -f3` ++ TVOS_DEPLOYMENT_TARGET=${_host_os:4} + case "$host_cpu" in + aarch64) -+ _host_ident=${_host_os_min_version:3}-appletvsimulator-arm64 ++ _host_ident=${TVOS_DEPLOYMENT_TARGET}-appletvsimulator-arm64 + ;; + *) -+ _host_ident=${_host_os_min_version:3}-appletvsimulator-$host_cpu ++ _host_ident=${TVOS_DEPLOYMENT_TARGET}-appletvsimulator-$host_cpu + esac + ;; + *-apple-tvos*) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` ++ _host_os=`echo $host | cut -d '-' -f3` ++ TVOS_DEPLOYMENT_TARGET=${_host_os:4} + case "$host_cpu" in + aarch64) -+ _host_ident=${_host_os_min_version:3}-appletvos-arm64 ++ _host_ident=${TVOS_DEPLOYMENT_TARGET}-appletvos-arm64 + ;; + *) -+ _host_ident=${_host_os_min_version:3}-appletvos-$host_cpu ++ _host_ident=${TVOS_DEPLOYMENT_TARGET}-appletvos-$host_cpu + esac + ;; + *-apple-watchos*-simulator) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` ++ _host_os=`echo $host | cut -d '-' -f3` ++ WATCHOS_DEPLOYMENT_TARGET=${_host_os:7} + case "$host_cpu" in + aarch64) -+ _host_ident=${_host_os_min_version:3}-watchsimualtor-arm64 ++ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-watchsimulator-arm64 + ;; + *) -+ _host_ident=${_host_os_min_version:3}-watchsimualtor-$host_cpu ++ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-watchsimulator-$host_cpu + esac + ;; + *-apple-watchos*) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` ++ _host_os=`echo $host | cut -d '-' -f3` ++ WATCHOS_DEPLOYMENT_TARGET=${_host_os:7} + case "$host_cpu" in + aarch64) -+ _host_ident=${_host_os_min_version:3}-watchosos-arm64_32 ++ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-watchosos-arm64_32 + ;; + *) -+ _host_ident=${_host_os_min_version:3}-watchosos-$host_cpu ++ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-watchosos-$host_cpu + esac + ;; + *-apple-*) @@ -2092,7 +3066,7 @@ index cd69f0ede5..135036bf67 100644 fi # Some systems cannot stand _XOPEN_SOURCE being defined at all; they -@@ -693,6 +771,13 @@ +@@ -693,6 +830,13 @@ define_xopen_source=no;; Darwin/@<:@[12]@:>@@<:@0-9@:>@.*) define_xopen_source=no;; @@ -2106,7 +3080,34 @@ index cd69f0ede5..135036bf67 100644 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -941,11 +1026,13 @@ +@@ -783,6 +927,26 @@ + ], + ) + ++dnl iOS/tvOS/watchOS need to enforce the deployment target. ++AS_CASE([$ac_sys_system], ++ [iOS], [ ++ IOS_DEPLOYMENT_TARGET=${IOS_DEPLOYMENT_TARGET:=12.0} ++ AS_VAR_APPEND([CFLAGS], [" -mios-version-min=${IOS_DEPLOYMENT_TARGET}"]) ++ AS_VAR_APPEND([LDFLAGS], [" -mios-version-min=${IOS_DEPLOYMENT_TARGET}"]) ++ AC_SUBST([IOS_DEPLOYMENT_TARGET]) ++ ],[tvOS], [ ++ TVOS_DEPLOYMENT_TARGET=${TVOS_DEPLOYMENT_TARGET:=9.0} ++ AS_VAR_APPEND([CFLAGS], [" -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}"]) ++ AS_VAR_APPEND([LDFLAGS], [" -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}"]) ++ AC_SUBST([TVOS_DEPLOYMENT_TARGET]) ++ ],[watchOS], [ ++ WATCHOS_DEPLOYMENT_TARGET=${WATCHOS_DEPLOYMENT_TARGET:=4.0} ++ AS_VAR_APPEND([CFLAGS], [" -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}"]) ++ AS_VAR_APPEND([LDFLAGS], [" -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}"]) ++ AC_SUBST([WATCHOS_DEPLOYMENT_TARGET]) ++ ], ++) ++ + if test "$ac_sys_system" = "Darwin" + then + dnl look for SDKROOT +@@ -941,11 +1105,13 @@ AC_MSG_CHECKING([for multiarch]) AS_CASE([$ac_sys_system], [Darwin*], [MULTIARCH=""], @@ -2121,7 +3122,7 @@ index cd69f0ede5..135036bf67 100644 if test x$PLATFORM_TRIPLET != x && test x$MULTIARCH != x; then if test x$PLATFORM_TRIPLET != x$MULTIARCH; then -@@ -955,6 +1042,12 @@ +@@ -955,6 +1121,12 @@ MULTIARCH=$PLATFORM_TRIPLET fi AC_SUBST([PLATFORM_TRIPLET]) @@ -2134,7 +3135,7 @@ index cd69f0ede5..135036bf67 100644 if test x$MULTIARCH != x; then MULTIARCH_CPPFLAGS="-DMULTIARCH=\\\"$MULTIARCH\\\"" -@@ -985,6 +1078,9 @@ +@@ -985,6 +1157,9 @@ [wasm32-unknown-emscripten/clang], [PY_SUPPORT_TIER=3], dnl WebAssembly Emscripten [wasm32-unknown-wasi/clang], [PY_SUPPORT_TIER=3], dnl WebAssembly System Interface [x86_64-*-freebsd*/clang], [PY_SUPPORT_TIER=3], dnl FreeBSD on AMD64 @@ -2144,7 +3145,56 @@ index cd69f0ede5..135036bf67 100644 [PY_SUPPORT_TIER=0] ) -@@ -3085,6 +3181,7 @@ +@@ -1298,17 +1473,23 @@ + + AC_MSG_CHECKING([LDLIBRARY]) + +-# MacOSX framework builds need more magic. LDLIBRARY is the dynamic ++# iOS/MacOSX framework builds need more magic. LDLIBRARY is the dynamic + # library that we build, but we do not want to link against it (we + # will find it with a -framework option). For this reason there is an + # extra variable BLDLIBRARY against which Python and the extension + # modules are linked, BLDLIBRARY. This is normally the same as +-# LDLIBRARY, but empty for MacOSX framework builds. ++# LDLIBRARY, but empty for MacOSX framework builds. iOS does the same, ++# but uses a non-versioned framework layout. + if test "$enable_framework" + then +- LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' +- RUNSHARED=DYLD_FRAMEWORK_PATH=`pwd`${DYLD_FRAMEWORK_PATH:+:${DYLD_FRAMEWORK_PATH}} ++ case $ac_sys_system in ++ iOS|tvOS|watchOS) ++ LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; ++ *) ++ LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; ++ esac + BLDLIBRARY='' ++ RUNSHARED=DYLD_FRAMEWORK_PATH=`pwd`${DYLD_FRAMEWORK_PATH:+:${DYLD_FRAMEWORK_PATH}} + else + BLDLIBRARY='$(LDLIBRARY)' + fi +@@ -1357,12 +1538,16 @@ + ;; + Darwin*) + LDLIBRARY='libpython$(LDVERSION).dylib' +- BLDLIBRARY='-L. -lpython$(LDVERSION)' +- RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ++ BLDLIBRARY='-L. -lpython$(LDVERSION)' ++ RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ++ ;; ++ iOS|tvOS|watchOS) ++ LDLIBRARY='libpython$(LDVERSION).dylib' ++ BLDLIBRARY='-L. -lpython$(LDVERSION)' + ;; + AIX*) +- LDLIBRARY='libpython$(LDVERSION).so' +- RUNSHARED=LIBPATH=`pwd`${LIBPATH:+:${LIBPATH}} ++ LDLIBRARY='libpython$(LDVERSION).so' ++ RUNSHARED=LIBPATH=`pwd`${LIBPATH:+:${LIBPATH}} + ;; + + esac +@@ -3085,6 +3270,7 @@ esac ;; CYGWIN*) SHLIB_SUFFIX=.dll;; @@ -2152,17 +3202,44 @@ index cd69f0ede5..135036bf67 100644 *) SHLIB_SUFFIX=.so;; esac fi -@@ -3165,6 +3262,9 @@ +@@ -3165,6 +3351,11 @@ BLDSHARED="$LDSHARED" fi ;; + iOS/*|tvOS/*|watchOS/*) -+ LDSHARED='$(CC) -dynamiclib -undefined dynamic_lookup' -+ LDCXXSHARED='$(CXX) -dynamiclib -undefined dynamic_lookup';; ++ LDSHARED='$(CC) -dynamiclib -F . -framework Python' ++ LDCXXSHARED='$(CXX) -dynamiclib -F . -framework Python' ++ BLDSHARED="$LDSHARED" ++ ;; Emscripten|WASI) LDSHARED='$(CC) -shared' LDCXXSHARED='$(CXX) -shared';; -@@ -3682,6 +3782,9 @@ +@@ -3309,6 +3500,24 @@ + LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' + fi + LINKFORSHARED="$LINKFORSHARED";; ++ iOS/*|tvOS/*|watchOS/*) ++ LINKFORSHARED="$extra_undefs -framework CoreFoundation" ++ ++ # Issue #18075: the default maximum stack size (8MBytes) is too ++ # small for the default recursion limit. Increase the stack size ++ # to ensure that tests don't crash ++ stack_size="1000000" # 16 MB ++ if test "$with_ubsan" = "yes" ++ then ++ # Undefined behavior sanitizer requires an even deeper stack ++ stack_size="4000000" # 64 MB ++ fi ++ ++ AC_DEFINE_UNQUOTED([THREAD_STACK_SIZE], ++ [0x$stack_size], ++ [Custom thread stack size depending on chosen sanitizer runtimes.]) ++ ++ LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; + OpenUNIX*|UnixWare*) LINKFORSHARED="-Wl,-Bexport";; + SCO_SV*) LINKFORSHARED="-Wl,-Bexport";; + ReliantUNIX*) LINKFORSHARED="-W1 -Blargedynsym";; +@@ -3682,6 +3891,9 @@ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], @@ -2172,7 +3249,7 @@ index cd69f0ede5..135036bf67 100644 [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] ) AS_VAR_IF([ctypes_malloc_closure], [yes], [ -@@ -5714,7 +5817,7 @@ +@@ -5714,7 +5926,7 @@ AC_MSG_CHECKING([ABIFLAGS]) AC_MSG_RESULT([$ABIFLAGS]) AC_MSG_CHECKING([SOABI]) @@ -2181,7 +3258,7 @@ index cd69f0ede5..135036bf67 100644 AC_MSG_RESULT([$SOABI]) # Release build, debug build (Py_DEBUG), and trace refs build (Py_TRACE_REFS) -@@ -5722,7 +5825,7 @@ +@@ -5722,7 +5934,7 @@ if test "$Py_DEBUG" = 'true'; then # Similar to SOABI but remove "d" flag from ABIFLAGS AC_SUBST([ALT_SOABI]) @@ -2190,7 +3267,7 @@ index cd69f0ede5..135036bf67 100644 AC_DEFINE_UNQUOTED([ALT_SOABI], ["${ALT_SOABI}"], [Alternative SOABI used in debug build to load C extensions built in release mode]) fi -@@ -7068,6 +7171,29 @@ +@@ -7068,6 +7280,29 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], @@ -2220,3 +3297,581 @@ index cd69f0ede5..135036bf67 100644 [CYGWIN*], [PY_STDLIB_MOD_SET_NA([_scproxy])], [QNX*], [PY_STDLIB_MOD_SET_NA([_scproxy])], [FreeBSD*], [PY_STDLIB_MOD_SET_NA([_scproxy])], +--- /dev/null ++++ b/iOS/README.rst +@@ -0,0 +1,107 @@ ++==================== ++Python on iOS README ++==================== ++ ++:Authors: ++ Russell Keith-Magee (2023-11) ++ ++This document provides a quick overview of some iOS specific features in the ++Python distribution. ++ ++Compilers for building on iOS ++============================= ++ ++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, on the ++most recently released macOS. ++ ++iOS 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 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. ++ ++iOS 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 iOS? ++------------------------------ ++ ++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 iOS ++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. ++ ++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 commands for building ++Python for iOS will look somethign like:: ++ ++ $ ./configure \ ++ --enable-framework=/path/to/install \ ++ --host=aarch64-apple-ios \ ++ --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-ios`` for ARM64 iOS devices. ++ - ``aarch64-apple-ios-simulator`` for the iOS simulator running on Apple ++ Silicon devices. ++ - ``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 ++ 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 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 iOS ++===================================== +--- /dev/null ++++ b/iOS/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 ++ ++ iPhoneOS ++ ++ MinimumOSVersion ++ @IOS_DEPLOYMENT_TARGET@ ++ ++ +--- /dev/null ++++ b/iOS/Resources/bin/arm64-apple-ios-ar +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk iphoneos ar $@ +--- /dev/null ++++ b/iOS/Resources/bin/arm64-apple-ios-clang +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk iphoneos clang -target arm64-apple-ios $@ +--- /dev/null ++++ b/iOS/Resources/bin/arm64-apple-ios-cpp +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk iphoneos clang -target arm64-apple-ios -E $@ +--- /dev/null ++++ b/iOS/Resources/bin/arm64-apple-ios-simulator-ar +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk iphonesimulator ar $@ +--- /dev/null ++++ b/iOS/Resources/bin/arm64-apple-ios-simulator-clang +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk iphonesimulator clang -target arm64-apple-ios-simulator $@ +--- /dev/null ++++ b/iOS/Resources/bin/arm64-apple-ios-simulator-cpp +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk iphonesimulator clang -target arm64-apple-ios-simulator -E $@ +--- /dev/null ++++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-ar +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk iphonesimulator ar $@ +--- /dev/null ++++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-clang +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk iphonesimulator clang -target x86_64-apple-ios-simulator $@ +--- /dev/null ++++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk iphonesimulator clang -target x86_64-apple-ios-simulator -E $@ +--- /dev/null ++++ b/tvOS/README.rst +@@ -0,0 +1,108 @@ ++===================== ++Python on tvOS README ++===================== ++ ++:Authors: ++ Russell Keith-Magee (2023-11) ++ ++This document provides a quick overview of some tvOS specific features in the ++Python distribution. ++ ++Compilers for building on tvOS ++============================== ++ ++Building for tvOS requires the use of Apple's Xcode tooling. It is strongly ++recommended that you use the most recent stable release of Xcode, on the ++most recently released macOS. ++ ++tvOS specific arguments to configure ++=================================== ++ ++* ``--enable-framework[=DIR]`` ++ ++ This argument specifies the location where the Python.framework will ++ be installed. ++ ++* ``--with-framework-name=NAME`` ++ ++ Specify the name for the python framework, defaults to ``Python``. ++ ++ ++Building and using Python on tvOS ++================================= ++ ++ABIs and Architectures ++---------------------- ++ ++tvOS apps can be deployed on physical devices, and on the tvOS simulator. ++Although the API used on these devices is identical, the ABI is different - you ++need to link against different libraries for an tvOS device build ++(``appletvos``) or an tvOS simulator build (``appletvsimulator``). Apple uses ++the XCframework format to allow specifying a single dependency that supports ++multiple ABIs. An XCframework is a wrapper around multiple ABI-specific ++frameworks. ++ ++tvOS can also support different CPU architectures within each ABI. At present, ++there is only a single support ed architecture on physical devices - ARM64. ++However, the *simulator* supports 2 architectures - ARM64 (for running on Apple ++Silicon machines), and x86_64 (for running on older Intel-based machines.) ++ ++To support multiple CPU architectures on a single platform, Apple uses a "fat ++binary" format - a single physical file that contains support for multiple ++architectures. ++ ++How do I build Python for tvOS? ++------------------------------- ++ ++The Python build system will build a ``Python.framework`` that supports a ++*single* ABI with a *single* architecture. If you want to use Python in an tvOS ++project, you need to: ++ ++1. Produce multiple ``Python.framework`` builds, one for each ABI and architecture; ++2. Merge the binaries for each architecture on a given ABI into a single "fat" binary; ++3. Merge the "fat" frameworks for each ABI into a single XCframework. ++ ++tvOS builds of Python *must* be constructed as framework builds. To support this, ++you must provide the ``--enable-framework`` flag when configuring the build. ++ ++The build also requires the use of cross-compilation. The commands for building ++Python for tvOS will look somethign like:: ++ ++ $ ./configure \ ++ --enable-framework=/path/to/install \ ++ --host=aarch64-apple-tvos \ ++ --build=aarch64-apple-darwin \ ++ --with-build-python=/path/to/python.exe ++ $ make ++ $ make install ++ ++In this invocation: ++ ++* ``/path/to/install`` is the location where the final Python.framework will be ++ output. ++ ++* ``--host`` is the architecture and ABI that you want to build, in GNU compiler ++ triple format. This will be one of: ++ ++ - ``aarch64-apple-tvos`` for ARM64 tvOS devices. ++ - ``aarch64-apple-tvos-simulator`` for the tvOS simulator running on Apple ++ Silicon devices. ++ - ``x86_64-apple-tvos-simulator`` for the tvOS simulator running on Intel ++ devices. ++ ++* ``--build`` is the GNU compiler triple for the machine that will be running ++ the compiler. This is one of: ++ ++ - ``aarch64-apple-darwin`` for Apple Silicon devices. ++ - ``x86_64-apple-darwin`` for Intel devices. ++ ++* ``/path/to/python.exe`` is the path to a Python binary on the machine that ++ will be running the compiler. This is needed because the Python compilation ++ process involves running some Python code. On a normal desktop build of ++ Python, you can compile a python interpreter and then use that interpreter to ++ run Python code. However, the binaries produced for tvOS won't run on macOS, so ++ you need to provide an external Python interpreter. This interpreter must be ++ the version as the Python that is being compiled. ++ ++Using a framework-based Python on tvOS ++====================================== +--- /dev/null ++++ b/tvOS/Resources/Info.plist.in +@@ -0,0 +1,34 @@ ++ ++ ++ ++ ++ CFBundleDevelopmentRegion ++ en ++ CFBundleExecutable ++ Python ++ CFBundleGetInfoString ++ Python Runtime and Library ++ CFBundleIdentifier ++ @PYTHONFRAMEWORKIDENTIFIER@ ++ CFBundleInfoDictionaryVersion ++ 6.0 ++ CFBundleName ++ Python ++ CFBundlePackageType ++ FMWK ++ CFBundleShortVersionString ++ %VERSION% ++ CFBundleLongVersionString ++ %VERSION%, (c) 2001-2023 Python Software Foundation. ++ CFBundleSignature ++ ???? ++ CFBundleVersion ++ %VERSION% ++ CFBundleSupportedPlatforms ++ ++ tvOS ++ ++ MinimumOSVersion ++ @TVOS_DEPLOYMENT_TARGET@ ++ ++ +--- /dev/null ++++ b/tvOS/Resources/bin/arm64-apple-tvos-ar +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk appletvos ar $@ +--- /dev/null ++++ b/tvOS/Resources/bin/arm64-apple-tvos-clang +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk appletvos clang -target arm64-apple-tvos $@ +--- /dev/null ++++ b/tvOS/Resources/bin/arm64-apple-tvos-cpp +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk appletvos clang -target arm64-apple-tvos -E $@ +--- /dev/null ++++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-ar +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk appletvsimulator ar $@ +--- /dev/null ++++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-clang +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk appletvsimulator clang -target arm64-apple-tvos-simulator $@ +--- /dev/null ++++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-cpp +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk appletvsimulator clang -target arm64-apple-tvos-simulator -E $@ +--- /dev/null ++++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-ar +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk appletvsimulator ar $@ +--- /dev/null ++++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-clang +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk appletvsimulator clang -target x86_64-apple-tvos-simulator $@ +--- /dev/null ++++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-cpp +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk appletvsimulator clang -target x86_64-apple-tvos-simulator -E $@ +--- /dev/null ++++ b/watchOS/README.rst +@@ -0,0 +1,108 @@ ++======================== ++Python on watchOS README ++======================== ++ ++:Authors: ++ Russell Keith-Magee (2023-11) ++ ++This document provides a quick overview of some watchOS specific features in the ++Python distribution. ++ ++Compilers for building on watchOS ++================================= ++ ++Building for watchOS requires the use of Apple's Xcode tooling. It is strongly ++recommended that you use the most recent stable release of Xcode, on the ++most recently released macOS. ++ ++watchOS specific arguments to configure ++======================================= ++ ++* ``--enable-framework[=DIR]`` ++ ++ This argument specifies the location where the Python.framework will ++ be installed. ++ ++* ``--with-framework-name=NAME`` ++ ++ Specify the name for the python framework, defaults to ``Python``. ++ ++ ++Building and using Python on watchOS ++==================================== ++ ++ABIs and Architectures ++---------------------- ++ ++watchOS apps can be deployed on physical devices, and on the watchOS simulator. ++Although the API used on these devices is identical, the ABI is different - you ++need to link against different libraries for an watchOS device build ++(``watchos``) or an watchOS simulator build (``watchsimulator``). Apple uses the ++XCframework format to allow specifying a single dependency that supports ++multiple ABIs. An XCframework is a wrapper around multiple ABI-specific ++frameworks. ++ ++watchOS can also support different CPU architectures within each ABI. At present, ++there is only a single support ed architecture on physical devices - ARM64. ++However, the *simulator* supports 2 architectures - ARM64 (for running on Apple ++Silicon machines), and x86_64 (for running on older Intel-based machines.) ++ ++To support multiple CPU architectures on a single platform, Apple uses a "fat ++binary" format - a single physical file that contains support for multiple ++architectures. ++ ++How do I build Python for watchOS? ++------------------------------- ++ ++The Python build system will build a ``Python.framework`` that supports a ++*single* ABI with a *single* architecture. If you want to use Python in an watchOS ++project, you need to: ++ ++1. Produce multiple ``Python.framework`` builds, one for each ABI and architecture; ++2. Merge the binaries for each architecture on a given ABI into a single "fat" binary; ++3. Merge the "fat" frameworks for each ABI into a single XCframework. ++ ++watchOS builds of Python *must* be constructed as framework builds. To support this, ++you must provide the ``--enable-framework`` flag when configuring the build. ++ ++The build also requires the use of cross-compilation. The commands for building ++Python for watchOS will look somethign like:: ++ ++ $ ./configure \ ++ --enable-framework=/path/to/install \ ++ --host=aarch64-apple-watchos \ ++ --build=aarch64-apple-darwin \ ++ --with-build-python=/path/to/python.exe ++ $ make ++ $ make install ++ ++In this invocation: ++ ++* ``/path/to/install`` is the location where the final Python.framework will be ++ output. ++ ++* ``--host`` is the architecture and ABI that you want to build, in GNU compiler ++ triple format. This will be one of: ++ ++ - ``arm64_32-apple-watchos`` for ARM64-32 watchOS devices. ++ - ``aarch64-apple-watchos-simulator`` for the watchOS simulator running on Apple ++ Silicon devices. ++ - ``x86_64-apple-watchos-simulator`` for the watchOS simulator running on Intel ++ devices. ++ ++* ``--build`` is the GNU compiler triple for the machine that will be running ++ the compiler. This is one of: ++ ++ - ``aarch64-apple-darwin`` for Apple Silicon devices. ++ - ``x86_64-apple-darwin`` for Intel devices. ++ ++* ``/path/to/python.exe`` is the path to a Python binary on the machine that ++ will be running the compiler. This is needed because the Python compilation ++ process involves running some Python code. On a normal desktop build of ++ Python, you can compile a python interpreter and then use that interpreter to ++ run Python code. However, the binaries produced for watchOS won't run on macOS, so ++ you need to provide an external Python interpreter. This interpreter must be ++ the version as the Python that is being compiled. ++ ++Using a framework-based Python on watchOS ++====================================== +--- /dev/null ++++ b/watchOS/Resources/Info.plist.in +@@ -0,0 +1,34 @@ ++ ++ ++ ++ ++ CFBundleDevelopmentRegion ++ en ++ CFBundleExecutable ++ Python ++ CFBundleGetInfoString ++ Python Runtime and Library ++ CFBundleIdentifier ++ @PYTHONFRAMEWORKIDENTIFIER@ ++ CFBundleInfoDictionaryVersion ++ 6.0 ++ CFBundleName ++ Python ++ CFBundlePackageType ++ FMWK ++ CFBundleShortVersionString ++ %VERSION% ++ CFBundleLongVersionString ++ %VERSION%, (c) 2001-2023 Python Software Foundation. ++ CFBundleSignature ++ ???? ++ CFBundleVersion ++ %VERSION% ++ CFBundleSupportedPlatforms ++ ++ watchOS ++ ++ MinimumOSVersion ++ @WATCHOS_DEPLOYMENT_TARGET@ ++ ++ +--- /dev/null ++++ b/watchOS/Resources/bin/arm64-apple-watchos-simulator-ar +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk watchsimulator ar $@ +--- /dev/null ++++ b/watchOS/Resources/bin/arm64-apple-watchos-simulator-clang +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk watchsimulator 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 ar $@ +--- /dev/null ++++ b/watchOS/Resources/bin/arm64_32-apple-watchos-clang +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk watchos 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 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 ar $@ +--- /dev/null ++++ b/watchOS/Resources/bin/x86_64-apple-watchos-simulator-clang +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk watchsimulator 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 clang -target x86_64-apple-watchos-simulator -E $@ diff --git a/patch/Python/release.common.exclude b/patch/Python/release.common.exclude deleted file mode 100644 index bec2b860..00000000 --- a/patch/Python/release.common.exclude +++ /dev/null @@ -1,26 +0,0 @@ -# This is a list of support package path patterns that we exclude -# from all Python-Apple-support tarballs. -# It is used by `tar -X` during the Makefile build. -# Remove standard library test suites. -python-stdlib/ctypes/test -python-stdlib/distutils/tests -python-stdlib/lib2to3/tests -python-stdlib/sqlite3/test -python-stdlib/test -# Remove config-* directory, which is used for compiling C extension modules. -python-stdlib/config-* -# Remove ensurepip. If user code needs pip, it can add it to -python-stdlib/ensurepip -# Remove libraries supporting IDLE. We don't need to ship an IDE -python-stdlib/idlelib -# Remove Tcl/Tk GUI code. We don't build against Tcl/Tk at the moment, so this -# will not work. -python-stdlib/tkinter -python-stdlib/turtle.py -python-stdlib/turtledemo -# Remove site-packages directory. The template unpacks user code and -# dependencies to a different path. -python-stdlib/site-packages -# Remove pyc files. These take up space, but since most stdlib modules are -# never imported by user code, they mostly have no value. -*/__pycache__ diff --git a/patch/Python/release.iOS.exclude b/patch/Python/release.iOS.exclude index fb1f841b..f1d0f75e 100644 --- a/patch/Python/release.iOS.exclude +++ b/patch/Python/release.iOS.exclude @@ -1,10 +1,6 @@ # This is a list of support package path patterns that we exclude -# from iOS Python-Apple-support tarballs. +# from all Python-Apple-support tarballs. # It is used by `tar -X` during the Makefile build. -# -# Remove command-line curses toolkit. -python-stdlib/curses -# Remove the testing binary modules -python-stdlib/lib-dynload/*_test*.dylib -python-stdlib/lib-dynload/_xx*.dylib -python-stdlib/lib-dynload/xx*.dylib +# Remove pyc files. These take up space, but since most stdlib modules are +# never imported by user code, they mostly have no value. +*/__pycache__ diff --git a/patch/Python/release.tvOS.exclude b/patch/Python/release.tvOS.exclude index e9ff418a..f1d0f75e 100644 --- a/patch/Python/release.tvOS.exclude +++ b/patch/Python/release.tvOS.exclude @@ -1,10 +1,6 @@ # This is a list of support package path patterns that we exclude -# from tvOS Python-Apple-support tarballs. +# from all Python-Apple-support tarballs. # It is used by `tar -X` during the Makefile build. -# -# Remove command-line curses toolkit. -python-stdlib/curses -# Remove the testing binary modules -python-stdlib/lib-dynload/*_test*.dylib -python-stdlib/lib-dynload/_xx*.dylib -python-stdlib/lib-dynload/xx*.dylib +# Remove pyc files. These take up space, but since most stdlib modules are +# never imported by user code, they mostly have no value. +*/__pycache__ diff --git a/patch/Python/release.watchOS.exclude b/patch/Python/release.watchOS.exclude index c8b1d66c..f1d0f75e 100644 --- a/patch/Python/release.watchOS.exclude +++ b/patch/Python/release.watchOS.exclude @@ -1,10 +1,6 @@ # This is a list of support package path patterns that we exclude -# from watchOS Python-Apple-support tarballs. +# from all Python-Apple-support tarballs. # It is used by `tar -X` during the Makefile build. -# -# Remove command-line curses toolkit. -python-stdlib/curses -# Remove the testing binary modules -python-stdlib/lib-dynload/*_test*.dylib -python-stdlib/lib-dynload/_xx*.dylib -python-stdlib/lib-dynload/xx*.dylib +# Remove pyc files. These take up space, but since most stdlib modules are +# never imported by user code, they mostly have no value. +*/__pycache__ diff --git a/patch/make-macho-standalone.py b/patch/make-macho-standalone.py new file mode 100644 index 00000000..975765d8 --- /dev/null +++ b/patch/make-macho-standalone.py @@ -0,0 +1,10 @@ +import os +import sys +from macholib.MachOStandalone import MachOStandalone + + +if __name__ == "__main__": + MachOStandalone( + sys.argv[1], + os.path.abspath(f"{sys.argv[1]}/") + ).run(contents="@rpath/") From b8fcfd7cca2237163ba9c7f2b64648058b693a6b Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 14 Nov 2023 14:13:09 +0800 Subject: [PATCH 009/113] Update docs describing the framework build and usage. --- README.rst | 39 ++++++++++++++++++---------------- USAGE.md | 61 ++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 62 insertions(+), 38 deletions(-) diff --git a/README.rst b/README.rst index dff2a8aa..ae9d2aeb 100644 --- a/README.rst +++ b/README.rst @@ -19,24 +19,24 @@ pre-requisites, and packaging them as static libraries that can be incorporated XCode project. The binary modules in the Python standard library are statically compiled, but are distributed as objects that can be dynamically loaded at runtime. -It exposes *almost* all the modules in the Python standard library except for: +The macOS package is a re-bundling of the official macOS binary, modified so that +it is relocatable. -* ``dbm.gnu`` -* ``tkinter`` -* ``readline`` -* ``nis`` (Deprecated by PEP594) -* ``ossaudiodev`` (Deprecated by PEP594) -* ``spwd`` (Deprecated by PEP594) - -The following standard library modules are available on macOS, but not the other -Apple platforms: +The iOS, tvOS and watchOS packages are compiled by this project. They expose +*almost* all the modules in the Python standard library except for: * ``curses`` +* ``dbm.gnu`` * ``grp`` * ``multiprocessing`` +* ``nis`` (Deprecated by PEP594) +* ``ossaudiodev`` (Deprecated by PEP594) * ``posixshmem`` * ``posixsubprocess`` +* ``readline`` +* ``spwd`` (Deprecated by PEP594) * ``syslog`` +* ``tkinter`` The binaries support x86_64 and arm64 for macOS; arm64 for iOS and appleTV devices; and arm64_32 for watchOS. It also supports device simulators on both @@ -93,10 +93,6 @@ Each support package contains: * ``VERSIONS``, a text file describing the specific versions of code used to build the support package; -* ``bin``, a folder containing shell aliases for the compilers that are needed - to build packages. This is required because Xcode uses the ``xcrun`` alias to - dynamically generate the name of binaries, but a lot of C tooling expects that ``CC`` - will not contain spaces. * ``platform-site``, a folder that contains site customization scripts that can be used to make your local Python install look like it is an on-device install for each of the underlying target architectures supported by the platform. This is needed because when @@ -107,10 +103,17 @@ Each support package contains: return ``platform`` and ``sysconfig`` responses consistent with on-device behavior, which will cause ``pip`` to install platform-appropriate packages. * ``Python.xcframework``, a multi-architecture build of the Python runtime library -* ``python-stdlib``, the code and binary modules comprising the Python standard library. - On iOS, tvOS and watchOS, there are 2 copies of every binary module - one for physical - devices, and one for the simulator. The simulator binaries are "fat", containing code - for both x86_64 and arm64. + +On iOS/tvOS/watchOS, the ``Python.xcframework`` contains a +slice for each supported ABI (device and simulator). The folder containing the +slice can also be used as a ``PYTHONHOME``, as it contains a ``bin``, ``include`` +and ``lib`` directory. + +The ``bin`` folder does not contain Python executables (as they can't be +invoked). However, it *does* contain shell aliases for the compilers that are +needed to build packages. This is required because Xcode uses the ``xcrun`` +alias to dynamically generate the name of binaries, but a lot of C tooling +expects that ``CC`` will not contain spaces. For a detailed instructions on using the support package in your own project, see the `usage guide <./USAGE.md>`__ diff --git a/USAGE.md b/USAGE.md index 706fe058..cf507baf 100644 --- a/USAGE.md +++ b/USAGE.md @@ -23,31 +23,42 @@ To add this support package to your own project: 1. [Download a release tarball for your desired Python version and Apple platform](https://github.com/beeware/Python-Apple-support/releases) -2. Add the `python-stdlib` and `Python.xcframework` to your Xcode project. Both - the `python-stdlib` folder and the `Python.xcframework` should be members of - any target that needs to use Python. +2. Add `Python.xcframework` to your Xcode project. You can put it anywhere in your + project that you want; the following instructions assume it has been put in a + folder named "Support". 3. In Xcode, select the root node of the project tree, and select the target you want to build. 4. Select "General" -> "Frameworks, Libraries and Embedded Content", and ensure that `Python.xcframework` is on the list of frameworks. It should be marked - "Do not embed". + "Embed and sign". -5. Select "General" -> "Build Phases", and ensure that the `python-stdlib` folder - is listed in the "Copy Bundle Resources" step. +5. Select "General" -> "Build Settings", and set the following values: + - Linking - General: + - `@executable_path/Frameworks` + - Search paths: + - Framework Search paths: `"$(PROJECT_DIR)/Support"` + - Header Search paths: `"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers"` -6. If you're on iOS, Add a new "Run script" build phase named "Purge Python Binary - Modules for Non-Target Platforms". This script will purge any dynamic module for the - platform you are *not* targeting. The script should have the following content: +6. Add a new "Run script" build phase named "Install target specific Python + Modules". This script will install the standard library for your target. The + script should have the following content: ```bash +set -e + +mkdir -p "$CODESIGNING_FOLDER_PATH/python/lib" if [ "$EFFECTIVE_PLATFORM_NAME" = "-iphonesimulator" ]; then - echo "Purging Python modules for iOS Device" - find "$CODESIGNING_FOLDER_PATH/python-stdlib" -name "*.*-iphoneos.dylib" -exec rm -f "{}" \; + echo "Installing Python modules for iOS Simulator" + rsync -au --delete "$PROJECT_DIR/Support/Python.xcframework/iphonesimulator/lib/" "$CODESIGNING_FOLDER_PATH/python/lib/" + # Also install any user-provided modules + # rsync -au --delete "$PROJECT_DIR/Testbed/app_packages.iphonesimulator/" "$CODESIGNING_FOLDER_PATH/app_packages" else - echo "Purging Python modules for iOS Simulator" - find "$CODESIGNING_FOLDER_PATH" -name "*.*-iphonesimulator.dylib" -exec rm -f "{}" \; + echo "Installing Python modules for iOS Device" + rsync -au --delete "$PROJECT_DIR/Support/Python.xcframework/iphoneos/lib/" "$CODESIGNING_FOLDER_PATH/python/lib" + # Also install any user-provided modules + # rsync -au --delete "$PROJECT_DIR/Testbed/app_packages.iphoneos/" "$CODESIGNING_FOLDER_PATH/app_packages" fi ``` @@ -69,13 +80,12 @@ install_dylib () { DYLIB=$(basename "$FULL_DYLIB") # The name of the .dylib file, relative to the install base RELATIVE_DYLIB=${FULL_DYLIB#$CODESIGNING_FOLDER_PATH/$INSTALL_BASE/} - # The (hopefully unique) name of the framework, constructed by replacing path - # separators in the relative name with underscores. - FRAMEWORK_NAME=$(echo $RELATIVE_DYLIB | cut -d "." -f 1 | tr "/" "_"); + # The full dotted name of the binary module, constructed from the file path. + FULL_MODULE_NAME=$(echo $RELATIVE_DYLIB | cut -d "." -f 1 | tr "/" "."); # A bundle identifier; not actually used, but required by Xcode framework packaging - FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FRAMEWORK_NAME | tr "_" "-") + FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr "_" "-") # The name of the framework folder. - FRAMEWORK_FOLDER="Frameworks/$FRAMEWORK_NAME.framework" + FRAMEWORK_FOLDER="Frameworks/$FULL_MODULE_NAME.framework" # If the framework folder doesn't exist, create it. if [ ! -d "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER" ]; then @@ -91,10 +101,16 @@ install_dylib () { mv "$FULL_DYLIB" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER" } +# Make sure to update the Python version version reference here echo "Install standard library dylibs..." -find "$CODESIGNING_FOLDER_PATH/python-stdlib/lib-dynload" -name "*.dylib" | while read FULL_DYLIB; do - install_dylib python-stdlib/lib-dynload "$FULL_DYLIB" +find "$CODESIGNING_FOLDER_PATH/python/lib/python3.13/lib-dynload" -name "*.dylib" | while read FULL_DYLIB; do + install_dylib python/lib/python3.13/lib-dynload "$FULL_DYLIB" done +# Also install any user-provided dynamic modules; e.g., +# echo "Install app package dylibs..." +# find "$CODESIGNING_FOLDER_PATH/app_packages" -name "*.dylib" | while read FULL_DYLIB; do +# install_dylib app_packages "$FULL_DYLIB" +# done # Clean up dylib template rm -f "$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist" @@ -103,6 +119,11 @@ echo "Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SI 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 "{}" \; ``` + Make sure that you update these scripts to update the references to the + Python version, and include any user-provided code that you want to bundle. + If you use the ``rsync`` approach above, user-provided code should *not* be + included as part of the "Copy Bundle Resources" step. + You'll also need to add a file named `dylib-Info-template.plist` to your Xcode project, and make it a member of any target that needs to use Python. The template should have the following content: From 323aa5f800c3b471dfafb638cd3411e287f96901 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 20 Nov 2023 14:35:01 +0800 Subject: [PATCH 010/113] Update syntax for github outputs. --- .github/workflows/publish.yaml | 16 +++++-------- .github/workflows/release.yaml | 42 +++++++++++++--------------------- 2 files changed, 22 insertions(+), 36 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 6cb84f82..79654eec 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -20,17 +20,13 @@ jobs: env: TAG_NAME: ${{ github.ref }} run: | - export TAG=$(basename $TAG_NAME) - echo "TAG=${TAG}" - export PYTHON_VER="${TAG%-*}" - export BUILD_NUMBER="${TAG#*-}" + TAG=$(basename $TAG_NAME) + PYTHON_VER="${TAG%-*}" + BUILD_NUMBER="${TAG#*-}" - echo "PYTHON_VER=${PYTHON_VER}" - echo "BUILD_NUMBER=${BUILD_NUMBER}" - - echo "TAG=${TAG}" >> ${GITHUB_OUTPUT} - echo "PYTHON_VER=${PYTHON_VER}" >> ${GITHUB_OUTPUT} - echo "BUILD_NUMBER=${BUILD_NUMBER}" >> ${GITHUB_OUTPUT} + echo "TAG=${TAG}" | tee -a ${GITHUB_OUTPUT} + echo "PYTHON_VER=${PYTHON_VER}" | tee -a ${GITHUB_OUTPUT} + echo "BUILD_NUMBER=${BUILD_NUMBER}" | tee -a ${GITHUB_OUTPUT} - name: Update Release Asset to S3 env: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 34448867..4bb23681 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -30,17 +30,13 @@ jobs: env: TAG_NAME: ${{ github.ref }} run: | - export TAG=$(basename $TAG_NAME) - echo "TAG=${TAG}" - export PYTHON_VER="${TAG%-*}" - export BUILD_NUMBER="${TAG#*-}" + TAG=$(basename $TAG_NAME) + echo "TAG=${TAG}" | tee -a ${GITHUB_OUTPUT} - echo "PYTHON_VER=${PYTHON_VER}" - echo "BUILD_NUMBER=${BUILD_NUMBER}" - - echo "TAG=${TAG}" >> ${GITHUB_OUTPUT} - echo "PYTHON_VER=${PYTHON_VER}" >> ${GITHUB_OUTPUT} - echo "BUILD_NUMBER=${BUILD_NUMBER}" >> ${GITHUB_OUTPUT} + PYTHON_VER="${TAG%-*}" + BUILD_NUMBER="${TAG#*-}" + echo "PYTHON_VER=${PYTHON_VER}" | tee -a ${GITHUB_OUTPUT} + echo "BUILD_NUMBER=${BUILD_NUMBER}" | tee -a ${GITHUB_OUTPUT} - name: Set up Python uses: actions/setup-python@v4.7.1 @@ -55,23 +51,17 @@ jobs: - name: Extract version details id: version-details run: | - export PYTHON_VERSION=$(grep "Python version:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 3) - export BZIP2_VERSION=$(grep "BZip2:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) - export XZ_VERSION=$(grep "XZ:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) - export OPENSSL_VERSION=$(grep "OpenSSL:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) - export LIBFFI_VERSION=$(grep "libFFI:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) - - echo "PYTHON_VERSION=${PYTHON_VERSION}" - echo "BZIP2_VERSION=${BZIP2_VERSION}" - echo "XZ_VERSION=${XZ_VERSION}" - echo "OPENSSL_VERSION=${OPENSSL_VERSION}" - echo "LIBFFI_VERSION=${LIBFFI_VERSION}" + PYTHON_VERSION=$(grep "Python version:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 3) + BZIP2_VERSION=$(grep "BZip2:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) + XZ_VERSION=$(grep "XZ:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) + OPENSSL_VERSION=$(grep "OpenSSL:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) + LIBFFI_VERSION=$(grep "libFFI:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) - echo "PYTHON_VERSION=${PYTHON_VERSION}" >> ${GITHUB_OUTPUT} - echo "BZIP2_VERSION=${BZIP2_VERSION}" >> ${GITHUB_OUTPUT} - echo "XZ_VERSION=${XZ_VERSION}" >> ${GITHUB_OUTPUT} - echo "OPENSSL_VERSION=${OPENSSL_VERSION}" >> ${GITHUB_OUTPUT} - echo "LIBFFI_VERSION=${LIBFFI_VERSION}" >> ${GITHUB_OUTPUT} + echo "PYTHON_VERSION=${PYTHON_VERSION}" | tee -a ${GITHUB_OUTPUT} + echo "BZIP2_VERSION=${BZIP2_VERSION}" | tee -a ${GITHUB_OUTPUT} + echo "XZ_VERSION=${XZ_VERSION}" | tee -a ${GITHUB_OUTPUT} + echo "OPENSSL_VERSION=${OPENSSL_VERSION}" | tee -a ${GITHUB_OUTPUT} + echo "LIBFFI_VERSION=${LIBFFI_VERSION}" | tee -a ${GITHUB_OUTPUT} - name: Upload build artifact uses: actions/upload-artifact@v3.1.3 From 252d48be0c03f23c3e8050a4c5a672516922be68 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 20 Nov 2023 14:38:22 +0800 Subject: [PATCH 011/113] Add a makefile target to extract the Makefile config. --- Makefile | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 40d637e0..58c87c47 100644 --- a/Makefile +++ b/Makefile @@ -65,7 +65,7 @@ PATH=/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin all: $(OS_LIST) .PHONY: \ - all clean distclean update-patch vars \ + all clean distclean update-patch vars config \ $(foreach os,$(OS_LIST),$(os) clean-$(os) dev-clean-$(os) vars-$(os)) \ $(foreach os,$(OS_LIST),$(foreach sdk,$$(sort $$(basename $$(TARGETS-$(os)))),$(sdk) vars-$(sdk))) $(foreach os,$(OS_LIST),$(foreach target,$$(TARGETS-$(os)),$(target) vars-$(target))) @@ -642,6 +642,15 @@ vars: $(foreach os,$(OS_LIST),vars-$(os)) @echo "HOST_PYTHON: $(HOST_PYTHON)" @echo +config: + @echo "PYTHON_VERSION=$(PYTHON_VERSION)" + @echo "PYTHON_VER=$(PYTHON_VER)" + @echo "BUILD_NUMBER=$(BUILD_NUMBER)" + @echo "BZIP2_VERSION=$(BZIP2_VERSION)" + @echo "XZ_VERSION=$(XZ_VERSION)" + @echo "OPENSSL_VERSION=$(OPENSSL_VERSION)" + @echo "LIBFFI_VERSION=$(LIBFFI_VERSION)" + # Expand cross-platform build and clean targets for each output product clean: $(foreach os,$(OS_LIST),clean-$(os)) dev-clean: $(foreach os,$(OS_LIST),dev-clean-$(os)) From 6ec85e76b2313c3fa595c50874988688d0942a07 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 20 Nov 2023 14:38:31 +0800 Subject: [PATCH 012/113] Add a CI workflow. --- .github/workflows/ci.yaml | 64 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 .github/workflows/ci.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 00000000..7859373f --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,64 @@ +name: CI +on: + pull_request: + +env: + FORCE_COLOR: "1" + +defaults: + run: + shell: bash + +# Cancel active CI runs for a PR before starting another run +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: macOS-latest + strategy: + matrix: + # target: ['macOS', 'iOS', 'tvOS', 'watchOS'] + target: ['macOS'] + include: + - briefcase-run-args: + + # - target: iOS + # briefcase-run-args: ' -d "iPhone SE (3rd generation)"' + steps: + - uses: actions/checkout@v4 + + - name: Extract config variables + id: config-vars + run: | + PYTHON_VER=$(make config | grep "PYTHON_VER=" | cut -d "=" -f 2) + echo "PYTHON_VER=${PYTHON_VER}" | tee -a ${GITHUB_OUTPUT} + + - name: Set up Python + uses: actions/setup-python@v4.7.1 + with: + # Appending -dev ensures that we can always build the dev release. + # It's a no-op for versions that have been published. + python-version: ${{ steps.config-vars.outputs.PYTHON_VER }}-dev + + - name: Build ${{ matrix.target }} + run: | + # Do the build for the requested target. + make ${{ matrix.target }} + + - uses: actions/checkout@v4 + with: + repository: beeware/Python-support-testbed + path: Python-support-testbed + ref: py3.13-support + + - name: Install dependencies + run: | + # Use the development version of Briefcase + python -m pip install git+https://github.com/beeware/briefcase.git + + - name: Run support testbed check + working-directory: Python-support-testbed + # TODO - remove the template_branch option. + run: briefcase run ${{ matrix.target }} Xcode --test ${{ matrix.briefcase-run-args }} -C support_package=\'../dist/Python-${{ steps.config-vars.outputs.PYTHON_VER }}-${{ matrix.target }}-support.custom.tar.gz\' -C template_branch=\'framework-lib\' From 55cf5f90d00497718e0be87fd18ef7eac040d821 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 21 Nov 2023 10:13:58 +0800 Subject: [PATCH 013/113] Add other platforms into CI check. --- .github/workflows/ci.yaml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7859373f..242c3482 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -18,14 +18,20 @@ jobs: build: runs-on: macOS-latest strategy: + fail-fast: false matrix: - # target: ['macOS', 'iOS', 'tvOS', 'watchOS'] - target: ['macOS'] + target: ['macOS', 'iOS', 'tvOS', 'watchOS'] include: - briefcase-run-args: + - run-tests: false + + - target: macOS + run-tests: true + + - target: iOS + briefcase-run-args: ' -d "iPhone SE (3rd generation)"' + run-tests: true - # - target: iOS - # briefcase-run-args: ' -d "iPhone SE (3rd generation)"' steps: - uses: actions/checkout@v4 @@ -48,17 +54,20 @@ jobs: make ${{ matrix.target }} - uses: actions/checkout@v4 + if: matrix.run-tests with: repository: beeware/Python-support-testbed path: Python-support-testbed ref: py3.13-support - name: Install dependencies + if: matrix.run-tests run: | # Use the development version of Briefcase python -m pip install git+https://github.com/beeware/briefcase.git - name: Run support testbed check + if: matrix.run-tests working-directory: Python-support-testbed # TODO - remove the template_branch option. run: briefcase run ${{ matrix.target }} Xcode --test ${{ matrix.briefcase-run-args }} -C support_package=\'../dist/Python-${{ steps.config-vars.outputs.PYTHON_VER }}-${{ matrix.target }}-support.custom.tar.gz\' -C template_branch=\'framework-lib\' From fd7051e0b8dfa8d47c0869a08517a87ff141a794 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 21 Nov 2023 11:19:05 +0800 Subject: [PATCH 014/113] Upload build artefacts as part of CI. --- .github/workflows/ci.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 242c3482..38dce29c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -53,11 +53,18 @@ jobs: # Do the build for the requested target. make ${{ matrix.target }} + - name: Upload build artefacts + uses: actions/upload-artifact@v3.1.3 + with: + name: Python-${{ steps.config-vars.outputs.PYTHON_VER }}-${{ matrix.target }}-support.custom.tar.gz + path: dist/Python-${{ steps.config-vars.outputs.PYTHON_VER }}-${{ matrix.target }}-support.custom.tar.gz + - uses: actions/checkout@v4 if: matrix.run-tests with: repository: beeware/Python-support-testbed path: Python-support-testbed + # TODO - remove the py3.13 reference option. ref: py3.13-support - name: Install dependencies From 6d6abb0395226caba883ac9601f5fd445f2698b7 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 22 Nov 2023 12:51:22 +0800 Subject: [PATCH 015/113] Add a non-executable stub binary for crossenv detection purposes. --- Makefile | 13 +++++++++---- patch/Python/Python.patch | 8 ++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 58c87c47..aa4d33c8 100644 --- a/Makefile +++ b/Makefile @@ -20,10 +20,7 @@ PYTHON_MICRO_VERSION=$(shell echo $(PYTHON_VERSION) | grep -Eo "\d+\.\d+\.\d+") PYTHON_VER=$(basename $(PYTHON_VERSION)) # The binary releases of dependencies, published at: -# macOS: -# https://github.com/beeware/cpython-macOS-source-deps/releases -# iOS, tvOS, watchOS: -# https://github.com/beeware/cpython-apple-source-deps/releases +# https://github.com/beeware/cpython-apple-source-deps/releases BZIP2_VERSION=1.0.8-1 XZ_VERSION=5.4.4-1 OPENSSL_VERSION=3.0.12-1 @@ -417,13 +414,21 @@ $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h: $$(PYTHON_LIB-$(sdk)) @echo ">>> Build Python fat headers for the $(sdk) SDK" # Copy binary helpers from the first target in the $(sdk) SDK cp -r $$(PYTHON_BIN-$$(firstword $$(SDK_TARGETS-$(sdk)))) $$(PYTHON_BIN-$(sdk)) + + # Create a non-executable stub binary python3 + echo "#!/bin/bash\necho Can\\'t run $(sdk) binary\nexit 1" > $$(PYTHON_BIN-$(sdk))/python$(PYTHON_VER) + chmod 755 $$(PYTHON_BIN-$(sdk))/python$(PYTHON_VER) + # Copy headers as-is from the first target in the $(sdk) SDK cp -r $$(PYTHON_INCLUDE-$$(firstword $$(SDK_TARGETS-$(sdk)))) $$(PYTHON_INCLUDE-$(sdk)) + # Link the PYTHONHOME version of the headers mkdir -p $$(PYTHON_INSTALL-$(sdk))/include ln -si ../Python.framework/Headers $$(PYTHON_INSTALL-$(sdk))/include/python$(PYTHON_VER) + # 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 patch folder cp $(PROJECT_DIR)/patch/Python/pyconfig-$(os).h $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index cd30184a..bf95e9ec 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1842,7 +1842,7 @@ index d74fb6deac..09ebc4287c 100755 # Blank kernel with real OS is always fine. ;; diff --git a/configure b/configure -index c87f518382..45f021d1b1 100755 +index c87f518382..ad0cd39350 100755 --- a/configure +++ b/configure @@ -963,10 +963,13 @@ @@ -2121,7 +2121,7 @@ index c87f518382..45f021d1b1 100755 + esac + + prefix=$PYTHONFRAMEWORKINSTALLDIR/Versions/$VERSION -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX=$(prefix) ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX=${prefix} + RESSRCDIR=Mac/Resources/framework + + # Add files for Mac specific code to the list of output @@ -2606,7 +2606,7 @@ index c87f518382..45f021d1b1 100755 "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 cd69f0ede5..a96ca3a3c7 100644 +index cd69f0ede5..9306e1270b 100644 --- a/configure.ac +++ b/configure.ac @@ -310,6 +310,83 @@ @@ -2847,7 +2847,7 @@ index cd69f0ede5..a96ca3a3c7 100644 + esac + + prefix=$PYTHONFRAMEWORKINSTALLDIR/Versions/$VERSION -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX=$(prefix) ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX=${prefix} + RESSRCDIR=Mac/Resources/framework + + # Add files for Mac specific code to the list of output From c0cc33365aa04e1fe88ec2246accf7b42562a490 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 4 Dec 2023 14:18:08 +0800 Subject: [PATCH 016/113] Updated patch with version that passes CPython CI. --- Makefile | 5 +- patch/Python/Python.patch | 2649 ++++++++++++++++++++++++++----- patch/Python/pyconfig-iOS.h | 7 - patch/Python/pyconfig-tvOS.h | 7 - patch/Python/pyconfig-watchOS.h | 11 - patch/Python/test.exclude | 7 - 6 files changed, 2254 insertions(+), 432 deletions(-) delete mode 100644 patch/Python/pyconfig-iOS.h delete mode 100644 patch/Python/pyconfig-tvOS.h delete mode 100644 patch/Python/pyconfig-watchOS.h delete mode 100644 patch/Python/test.exclude diff --git a/Makefile b/Makefile index aa4d33c8..59bea0aa 100644 --- a/Makefile +++ b/Makefile @@ -429,8 +429,9 @@ $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h: $$(PYTHON_LIB-$(sdk)) # 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 patch folder - cp $(PROJECT_DIR)/patch/Python/pyconfig-$(os).h $$(PYTHON_INCLUDE-$(sdk))/pyconfig.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 + $$(PYTHON_STDLIB-$(sdk))/LICENSE.TXT: $$(PYTHON_LIB-$(sdk)) $$(PYTHON_FRAMEWORK-$(sdk))/Info.plist $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h @echo ">>> Build Python stdlib for the $(sdk) SDK" diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index bf95e9ec..24f7d218 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -19,43 +19,53 @@ index 6dd7d8d7ca..836c8a3fcf 100644 /* register() is useless on Windows, because only SIGSEGV, SIGABRT and --- /dev/null +++ b/Lib/_ios_support.py -@@ -0,0 +1,36 @@ -+from ctypes import cdll, c_void_p, c_char_p -+from ctypes import util +@@ -0,0 +1,46 @@ ++try: ++ from ctypes import cdll, c_void_p, c_char_p ++ from ctypes import util ++except ImportError: ++ # ctypes is an optional module. If it's not present, we're limited in what ++ # we can tell about the system, but we don't want to prevent the platform ++ # module from working. ++ cdll = None + + +def get_platform_ios(): -+ objc = cdll.LoadLibrary(util.find_library(b'objc')) ++ if cdll: ++ objc = cdll.LoadLibrary(util.find_library(b'objc')) + -+ objc.objc_getClass.restype = c_void_p -+ objc.objc_getClass.argtypes = [c_char_p] -+ objc.objc_msgSend.restype = c_void_p -+ objc.objc_msgSend.argtypes = [c_void_p, c_void_p] -+ objc.sel_registerName.restype = c_void_p -+ objc.sel_registerName.argtypes = [c_char_p] ++ objc.objc_getClass.restype = c_void_p ++ objc.objc_getClass.argtypes = [c_char_p] ++ objc.objc_msgSend.restype = c_void_p ++ objc.objc_msgSend.argtypes = [c_void_p, c_void_p] ++ objc.sel_registerName.restype = c_void_p ++ objc.sel_registerName.argtypes = [c_char_p] + -+ UIDevice = c_void_p(objc.objc_getClass(b'UIDevice')) -+ SEL_currentDevice = c_void_p(objc.sel_registerName(b'currentDevice')) -+ device = c_void_p(objc.objc_msgSend(UIDevice, SEL_currentDevice)) ++ UIDevice = c_void_p(objc.objc_getClass(b'UIDevice')) ++ SEL_currentDevice = c_void_p(objc.sel_registerName(b'currentDevice')) ++ device = c_void_p(objc.objc_msgSend(UIDevice, SEL_currentDevice)) + -+ SEL_systemVersion = c_void_p(objc.sel_registerName(b'systemVersion')) -+ systemVersion = c_void_p(objc.objc_msgSend(device, SEL_systemVersion)) ++ SEL_systemVersion = c_void_p(objc.sel_registerName(b'systemVersion')) ++ systemVersion = c_void_p(objc.objc_msgSend(device, SEL_systemVersion)) + -+ SEL_systemName = c_void_p(objc.sel_registerName(b'systemName')) -+ systemName = c_void_p(objc.objc_msgSend(device, SEL_systemName)) ++ SEL_systemName = c_void_p(objc.sel_registerName(b'systemName')) ++ systemName = c_void_p(objc.objc_msgSend(device, SEL_systemName)) + -+ SEL_model = c_void_p(objc.sel_registerName(b'model')) -+ systemModel = c_void_p(objc.objc_msgSend(device, SEL_model)) ++ SEL_model = c_void_p(objc.sel_registerName(b'model')) ++ systemModel = c_void_p(objc.objc_msgSend(device, SEL_model)) + -+ # UTF8String returns a const char*; -+ SEL_UTF8String = c_void_p(objc.sel_registerName(b'UTF8String')) -+ objc.objc_msgSend.restype = c_char_p ++ # UTF8String returns a const char*; ++ SEL_UTF8String = c_void_p(objc.sel_registerName(b'UTF8String')) ++ objc.objc_msgSend.restype = c_char_p + -+ system = objc.objc_msgSend(systemName, SEL_UTF8String).decode() -+ release = objc.objc_msgSend(systemVersion, SEL_UTF8String).decode() -+ model = objc.objc_msgSend(systemModel, SEL_UTF8String).decode() ++ system = objc.objc_msgSend(systemName, SEL_UTF8String).decode() ++ release = objc.objc_msgSend(systemVersion, SEL_UTF8String).decode() ++ model = objc.objc_msgSend(systemModel, SEL_UTF8String).decode() + -+ return system, release, model ++ return system, release, model ++ else: ++ # Return dummy values if we can't call system APIs. ++ return "iOS", "?", "iPhone" diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py index 0c2510e161..5567080ba5 100644 --- a/Lib/ctypes/util.py @@ -70,7 +80,7 @@ index 0c2510e161..5567080ba5 100644 def find_library(name): possible = ['lib%s.dylib' % name, diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py -index 0019897c94..0356d2ab5d 100644 +index 0019897c94..c11ead2b11 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -52,7 +52,7 @@ @@ -82,7 +92,7 @@ index 0019897c94..0356d2ab5d 100644 _CASE_INSENSITIVE_PLATFORMS = (_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY + _CASE_INSENSITIVE_PLATFORMS_STR_KEY) -@@ -1704,6 +1704,59 @@ +@@ -1704,6 +1704,65 @@ return f'FileFinder({self.path!r})' @@ -90,19 +100,20 @@ index 0019897c94..0356d2ab5d 100644 + """A loader for modules that have been packaged as Apple Frameworks for + compatibility with Apple's App Store policies. + -+ For compatibility with the App Store, *all* binary modules must be in .dylibs, -+ contained in a Framework, in the ``Frameworks`` folder of the packaged app. If -+ you're trying to run "from foo import _bar", and _bar is implemented with the binary -+ module "foo/_bar.abi3.dylib" (or any other .dylib extension), this loader will look -+ for "{sys.executable}/Frameworks/foo__bar.framework/_bar.abi3.dylib" (forming the -+ package name by taking the full path of the library, and replacing ``/`` with -+ ``_``). The app packaging tool is responsible for putting the library in this -+ location. -+ -+ However, the ``__file__`` attribute of the _bar module will report as the original -+ location inside the ``foo`` directory. This so that code that depends on walking -+ directory trees will continue to work as expected based on the *original* file -+ location. ++ For compatibility with the App Store, *all* binary modules must be .dylib ++ objects, contained in a Framework, stored in the ``Frameworks`` folder of ++ the packaged app. If you're trying to run "from foo import _bar", and _bar ++ is implemented with the binary module "foo/_bar.abi3.dylib" (or any other ++ .dylib extension), this loader will look for ++ "{sys.executable}/Frameworks/foo._bar.framework/_bar.abi3.dylib" (forming ++ the package name by taking the full path of the library, and replacing ``/`` ++ with ``.``). The app packaging tool is responsible for putting the library ++ in this location. ++ ++ However, the ``__file__`` attribute of the _bar module will report as the ++ original location inside the ``foo`` directory. This so that code that ++ depends on walking directory trees will continue to work as expected based ++ on the *original* file location. + """ + def __init__(self, fullname, dylib_file, path): + super().__init__(fullname, dylib_file) @@ -133,16 +144,21 @@ index 0019897c94..0356d2ab5d 100644 + for extension in EXTENSION_SUFFIXES: + dylib_file = _path_join(self.frameworks_path, f"{fullname}.framework", f"{name}{extension}") + _bootstrap._verbose_message('Looking for Apple Framework dylib {}', dylib_file) -+ if _path_isfile(dylib_file): -+ loader = AppleFrameworkLoader(fullname, dylib_file, path) -+ return _bootstrap.spec_from_loader(fullname, loader) ++ try: ++ dylib_exists = _path_isfile(dylib_file) ++ except ValueError: ++ pass ++ else: ++ if dylib_exists: ++ loader = AppleFrameworkLoader(fullname, dylib_file, path) ++ return _bootstrap.spec_from_loader(fullname, loader) + + return None + # Import setup ############################################################### def _fix_up_module(ns, name, pathname, cpathname=None): -@@ -1753,3 +1806,7 @@ +@@ -1753,3 +1812,7 @@ supported_loaders = _get_supported_file_loaders() sys.path_hooks.extend([FileFinder.path_hook(*supported_loaders)]) sys.meta_path.append(PathFinder) @@ -150,15 +166,27 @@ index 0019897c94..0356d2ab5d 100644 + frameworks_folder = _path_join(_path_split(sys.executable)[0], "Frameworks") + _bootstrap._verbose_message('Adding Apple Framework dylib finder at {}', frameworks_folder) + sys.meta_path.append(AppleFrameworkFinder(frameworks_folder)) +diff --git a/Lib/importlib/machinery.py b/Lib/importlib/machinery.py +index d9a19a13f7..fbd30b159f 100644 +--- a/Lib/importlib/machinery.py ++++ b/Lib/importlib/machinery.py +@@ -12,6 +12,7 @@ + from ._bootstrap_external import SourceFileLoader + from ._bootstrap_external import SourcelessFileLoader + from ._bootstrap_external import ExtensionFileLoader ++from ._bootstrap_external import AppleFrameworkLoader + from ._bootstrap_external import NamespaceLoader + + diff --git a/Lib/platform.py b/Lib/platform.py -index 7bb222088d..0a5ed0361e 100755 +index 7bb222088d..b4db9749da 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -496,6 +496,26 @@ # If that also doesn't work return the default values return release, versioninfo, machine -+def iOS_ver(): ++def ios_ver(): + """Get iOS/tvOS version information, and return it as a + tuple (system, release, model). All tuple entries are strings. + """ @@ -221,7 +249,7 @@ index 7bb222088d..0a5ed0361e 100755 + # Normalize responses on Apple mobile platforms + if sys.platform in {'ios', 'tvos'}: -+ system, release, model = iOS_ver() ++ system, release, model = ios_ver() + + # On iOS/tvOS simulators, os.uname() reports the machine as something + # like "arm64" or "x86_64". @@ -241,7 +269,7 @@ index 7bb222088d..0a5ed0361e 100755 - system = 'macOS' - release = macos_release + if sys.platform in {'ios', 'tvos'}: -+ system, release, _ = iOS_ver() ++ system, release, _ = ios_ver() + else: + macos_release = mac_ver()[0] + if macos_release: @@ -484,8 +512,32 @@ index 21e8770ab3..67958d247c 100644 def requires_subprocess(): """Used for subprocess, os.spawn calls, fd inheritance""" +diff --git a/Lib/test/support/os_helper.py b/Lib/test/support/os_helper.py +index 821a4b1ffd..517f7b57f4 100644 +--- a/Lib/test/support/os_helper.py ++++ b/Lib/test/support/os_helper.py +@@ -20,7 +20,7 @@ + + # TESTFN_UNICODE is a non-ascii filename + TESTFN_UNICODE = TESTFN_ASCII + "-\xe0\xf2\u0258\u0141\u011f" +-if sys.platform == 'darwin': ++if sys.platform in ('darwin', 'ios', 'tvos', 'watchos'): + # In Mac OS X's VFS API file names are, by definition, canonically + # decomposed Unicode, encoded using UTF-8. See QA1173: + # http://developer.apple.com/mac/library/qa/qa2001/qa1173.html +@@ -46,8 +46,8 @@ + 'encoding (%s). Unicode filename tests may not be effective' + % (TESTFN_UNENCODABLE, sys.getfilesystemencoding())) + TESTFN_UNENCODABLE = None +-# macOS and Emscripten deny unencodable filenames (invalid utf-8) +-elif sys.platform not in {'darwin', 'emscripten', 'wasi'}: ++# Apple and Emscripten deny unencodable filenames (invalid utf-8) ++elif sys.platform not in {'darwin', 'ios', 'tvos', 'watchos', 'emscripten', 'wasi'}: + try: + # ascii and utf-8 cannot encode the byte 0xff + b'\xff'.decode(sys.getfilesystemencoding()) diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py -index b25c097573..7f5f26248f 100644 +index b25c097573..7491782c7f 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -33,6 +33,7 @@ @@ -496,144 +548,93 @@ index b25c097573..7f5f26248f 100644 from test.support import socket_helper from test.support import threading_helper from test.support import ALWAYS_EQ, LARGEST, SMALLEST -@@ -543,6 +544,7 @@ - self._basetest_create_connection(conn_fut) - - @socket_helper.skip_unless_bind_unix_socket -+ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - def test_create_unix_connection(self): - # Issue #20682: On Mac OS X Tiger, getsockname() returns a - # zero-length address for UNIX socket. -@@ -635,6 +637,7 @@ - self.assertEqual(cm.exception.reason, 'CERTIFICATE_VERIFY_FAILED') - - @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - def test_create_ssl_connection(self): - with test_utils.run_test_server(use_ssl=True) as httpd: - create_connection = functools.partial( -@@ -646,6 +649,7 @@ - - @socket_helper.skip_unless_bind_unix_socket - @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - def test_create_ssl_unix_connection(self): - # Issue #20682: On Mac OS X Tiger, getsockname() returns a - # zero-length address for UNIX socket. -@@ -927,6 +931,7 @@ - return server, path - - @socket_helper.skip_unless_bind_unix_socket -+ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - def test_create_unix_server(self): - proto = MyProto(loop=self.loop) - server, path = self._make_unix_server(lambda: proto) -@@ -955,6 +960,7 @@ - server.close() - - @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'No UNIX Sockets') -+ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - def test_create_unix_server_path_socket_error(self): - proto = MyProto(loop=self.loop) - sock = socket.socket() -@@ -1020,6 +1026,7 @@ - - @socket_helper.skip_unless_bind_unix_socket - @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - def test_create_unix_server_ssl(self): - proto = MyProto(loop=self.loop) - server, path = self._make_ssl_unix_server( -@@ -1050,6 +1057,7 @@ - server.close() - - @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - def test_create_server_ssl_verify_failed(self): - proto = MyProto(loop=self.loop) - server, host, port = self._make_ssl_server( -@@ -1080,6 +1088,7 @@ - - @socket_helper.skip_unless_bind_unix_socket - @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - def test_create_unix_server_ssl_verify_failed(self): - proto = MyProto(loop=self.loop) - server, path = self._make_ssl_unix_server( -@@ -1140,6 +1149,7 @@ - - @socket_helper.skip_unless_bind_unix_socket - @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - def test_create_unix_server_ssl_verified(self): - proto = MyProto(loop=self.loop) - server, path = self._make_ssl_unix_server( +@@ -1799,6 +1800,7 @@ + next(it) + + ++@support.requires_subprocess() + class SubprocessTestsMixin: + + def check_terminated(self, returncode): diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py -index 9c92e75886..013a414729 100644 +index 9c92e75886..65ee41dbd1 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -18,6 +18,7 @@ import asyncio from test.test_asyncio import utils as test_utils -+from test.support import is_apple_mobile ++from test.support import requires_subprocess def tearDownModule(): -@@ -61,6 +62,7 @@ - self._basetest_open_connection(conn_fut) - - @socket_helper.skip_unless_bind_unix_socket -+ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - def test_open_unix_connection(self): - with test_utils.run_test_unix_server() as httpd: - conn_fut = asyncio.open_unix_connection(httpd.address) -@@ -92,6 +94,7 @@ - - @socket_helper.skip_unless_bind_unix_socket - @unittest.skipIf(ssl is None, 'No ssl module') -+ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - def test_open_unix_connection_no_loop_ssl(self): - with test_utils.run_test_unix_server(use_ssl=True) as httpd: - conn_fut = asyncio.open_unix_connection( -@@ -120,6 +123,7 @@ - self._basetest_open_connection_error(conn_fut) - - @socket_helper.skip_unless_bind_unix_socket -+ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - def test_open_unix_connection_error(self): - with test_utils.run_test_unix_server() as httpd: - conn_fut = asyncio.open_unix_connection(httpd.address) -@@ -638,6 +642,7 @@ - self.assertEqual(messages, []) - - @socket_helper.skip_unless_bind_unix_socket -+ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - def test_start_unix_server(self): - - class MyServer: -diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py -index d2c8cba6ac..a7bbe1d2b0 100644 ---- a/Lib/test/test_asyncio/test_unix_events.py -+++ b/Lib/test/test_asyncio/test_unix_events.py -@@ -18,6 +18,7 @@ - import warnings - +@@ -771,6 +772,7 @@ + self.assertEqual(msg2, b"hello world 2!\n") + + @unittest.skipIf(sys.platform == 'win32', "Don't have pipes") ++ @requires_subprocess() + def test_read_all_from_pipe_reader(self): + # See asyncio issue 168. This test is derived from the example + # subprocess_attach_read_pipe.py, but we configure the +diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py +index 179c8cb8cc..c8659f6ed6 100644 +--- a/Lib/test/test_asyncio/test_subprocess.py ++++ b/Lib/test/test_asyncio/test_subprocess.py +@@ -11,6 +11,7 @@ + from asyncio import subprocess + from test.test_asyncio import utils as test_utils from test import support +from test.support import is_apple_mobile from test.support import os_helper - from test.support import socket_helper - from test.support import wait_process -@@ -283,6 +284,7 @@ - @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), - 'UNIX Sockets are not supported') -+@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - class SelectorEventLoopUnixSocketTests(test_utils.TestCase): +@@ -47,6 +48,8 @@ + self._proc.pid = -1 + + ++@unittest.skipIf(is_apple_mobile, ++ f"{sys.platform} doesn't support subprocesses.") + class SubprocessTransportTests(test_utils.TestCase): def setUp(self): + super().setUp() +@@ -110,6 +113,8 @@ + transport.close() + + ++@unittest.skipIf(is_apple_mobile, ++ f"{sys.platform} doesn't support subprocesses.") + class SubprocessMixin: + + def test_stdin_stdout(self): +diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py +index d2c8cba6ac..c3795dede9 100644 +--- a/Lib/test/test_asyncio/test_unix_events.py ++++ b/Lib/test/test_asyncio/test_unix_events.py +@@ -1875,6 +1875,7 @@ + + + @unittest.skipUnless(hasattr(os, 'fork'), 'requires os.fork()') ++@support.requires_subprocess() + class TestFork(unittest.IsolatedAsyncioTestCase): + + async def test_fork_not_share_event_loop(self): +diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py +index 1b58882601..7ae237e6c1 100644 +--- a/Lib/test/test_cmd_line_script.py ++++ b/Lib/test/test_cmd_line_script.py +@@ -560,7 +560,9 @@ + # Python cannot a undecodable bytes argument to a subprocess. + # WASI does not permit invalid UTF-8 names. + if (os_helper.TESTFN_UNDECODABLE +- and sys.platform not in ('win32', 'darwin', 'emscripten', 'wasi')): ++ and sys.platform not in ( ++ 'win32', 'darwin', 'ios', 'tvos', 'watchos', 'emscripten', 'wasi' ++ )): + name = os.fsdecode(os_helper.TESTFN_UNDECODABLE) + elif os_helper.TESTFN_NONASCII: + name = os_helper.TESTFN_NONASCII diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py -index 203dd6fe57..8e0999ecd7 100644 +index 203dd6fe57..a4e7099da3 100644 --- a/Lib/test/test_fcntl.py +++ b/Lib/test/test_fcntl.py @@ -6,7 +6,7 @@ @@ -641,40 +642,272 @@ index 203dd6fe57..8e0999ecd7 100644 import sys import unittest -from test.support import verbose, cpython_only, get_pagesize -+from test.support import cpython_only, get_pagesize, is_apple_mobile, verbose ++from test.support import cpython_only, get_pagesize, is_apple_mobile, requires_subprocess, verbose from test.support.import_helper import import_module from test.support.os_helper import TESTFN, unlink -@@ -57,7 +57,7 @@ +@@ -57,7 +57,9 @@ start_len = "qq" if (sys.platform.startswith(('netbsd', 'freebsd', 'openbsd')) - or sys.platform == 'darwin'): -+ or sys.platform == 'darwin' or is_apple_mobile): ++ or sys.platform == 'darwin' ++ or is_apple_mobile ++ ): if struct.calcsize('l') == 8: off_t = 'l' pid_t = 'i' +@@ -157,6 +159,7 @@ + self.assertRaises(TypeError, fcntl.flock, 'spam', fcntl.LOCK_SH) + + @unittest.skipIf(platform.system() == "AIX", "AIX returns PermissionError") ++ @requires_subprocess() + def test_lockf_exclusive(self): + self.f = open(TESTFN, 'wb+') + cmd = fcntl.LOCK_EX | fcntl.LOCK_NB +@@ -169,6 +172,7 @@ + self.assertEqual(p.exitcode, 0) + + @unittest.skipIf(platform.system() == "AIX", "AIX returns PermissionError") ++ @requires_subprocess() + def test_lockf_share(self): + self.f = open(TESTFN, 'wb+') + cmd = fcntl.LOCK_SH | fcntl.LOCK_NB +diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py +index 2f191ea7a4..81115e9db8 100644 +--- a/Lib/test/test_ftplib.py ++++ b/Lib/test/test_ftplib.py +@@ -18,6 +18,7 @@ + + from unittest import TestCase, skipUnless + from test import support ++from test.support import requires_subprocess + from test.support import threading_helper + from test.support import socket_helper + from test.support import warnings_helper +@@ -900,6 +901,7 @@ + + + @skipUnless(ssl, "SSL not available") ++@requires_subprocess() + class TestTLS_FTPClassMixin(TestFTPClass): + """Repeat TestFTPClass tests starting the TLS layer for both control + and data connections first. +@@ -916,6 +918,7 @@ + + + @skipUnless(ssl, "SSL not available") ++@requires_subprocess() + class TestTLS_FTPClass(TestCase): + """Specific TLS_FTP class tests.""" + +diff --git a/Lib/test/test_genericpath.py b/Lib/test/test_genericpath.py +index 4f311c2d49..c0a6a48dea 100644 +--- a/Lib/test/test_genericpath.py ++++ b/Lib/test/test_genericpath.py +@@ -488,7 +488,9 @@ + # invalid UTF-8 name. Windows allows creating a directory with an + # arbitrary bytes name, but fails to enter this directory + # (when the bytes name is used). +- and sys.platform not in ('win32', 'darwin', 'emscripten', 'wasi')): ++ and sys.platform not in ( ++ 'win32', 'darwin', 'ios', 'tvos', 'watchos', 'emscripten', 'wasi' ++ )): + name = os_helper.TESTFN_UNDECODABLE + elif os_helper.TESTFN_NONASCII: + name = os_helper.TESTFN_NONASCII diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py -index 9fa6ecf9c0..53eccef97f 100644 +index 9fa6ecf9c0..01dc990b1e 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py -@@ -30,6 +30,7 @@ +@@ -30,7 +30,9 @@ import unittest from test import support +from test.support import is_apple_mobile from test.support import os_helper ++from test.support import requires_subprocess from test.support import threading_helper -@@ -422,7 +423,7 @@ + support.requires_working_socket(module=True) +@@ -410,8 +412,8 @@ + reader.close() + return body + +- @unittest.skipIf(sys.platform == 'darwin', +- 'undecodable name cannot always be decoded on macOS') ++ @unittest.skipIf(sys.platform == 'darwin' or is_apple_mobile, ++ 'undecodable name cannot always be decoded on Apple platforms') + @unittest.skipIf(sys.platform == 'win32', + 'undecodable name cannot be decoded on win32') + @unittest.skipUnless(os_helper.TESTFN_UNDECODABLE, +@@ -422,9 +424,9 @@ with open(os.path.join(self.tempdir, filename), 'wb') as f: f.write(os_helper.TESTFN_UNDECODABLE) response = self.request(self.base_url + '/') - if sys.platform == 'darwin': +- # On Mac OS the HFS+ filesystem replaces bytes that aren't valid +- # UTF-8 into a percent-encoded value. + if sys.platform == 'darwin' or is_apple_mobile: - # On Mac OS the HFS+ filesystem replaces bytes that aren't valid - # UTF-8 into a percent-encoded value. ++ # On Apple platforms the HFS+ filesystem replaces bytes that ++ # aren't valid UTF-8 into a percent-encoded value. for name in os.listdir(self.tempdir): + if name != 'test': # Ignore a filename created in setUp(). + filename = name +@@ -697,6 +699,7 @@ + + @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0, + "This test can't be run reliably as root (issue #13308).") ++@requires_subprocess() + class CGIHTTPServerTestCase(BaseTestCase): + class request_handler(NoLogRequestHandler, CGIHTTPRequestHandler): + _test_case_self = None # populated by each setUp() method call. +diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py +index aa465c70df..16337bb12b 100644 +--- a/Lib/test/test_import/__init__.py ++++ b/Lib/test/test_import/__init__.py +@@ -5,7 +5,7 @@ + import importlib.util + from importlib._bootstrap_external import _get_sourcefile + from importlib.machinery import ( +- BuiltinImporter, ExtensionFileLoader, FrozenImporter, SourceFileLoader, ++ AppleFrameworkLoader, BuiltinImporter, ExtensionFileLoader, FrozenImporter, SourceFileLoader, + ) + import marshal + import os +@@ -25,7 +25,7 @@ + + from test.support import os_helper + from test.support import ( +- STDLIB_DIR, swap_attr, swap_item, cpython_only, is_emscripten, ++ STDLIB_DIR, swap_attr, swap_item, cpython_only, is_apple_mobile, is_emscripten, + is_wasi, run_in_subinterp, run_in_subinterp_with_config, Py_TRACE_REFS) + from test.support.import_helper import ( + forget, make_legacy_pyc, unlink, unload, ready_to_import, +@@ -66,6 +66,7 @@ + MODULE_KINDS = { + BuiltinImporter: 'built-in', + ExtensionFileLoader: 'extension', ++ AppleFrameworkLoader: 'framework extension', + FrozenImporter: 'frozen', + SourceFileLoader: 'pure Python', + } +@@ -91,7 +92,10 @@ + assert module.__spec__.origin == 'built-in', module.__spec__ + + def require_extension(module, *, skip=False): +- _require_loader(module, ExtensionFileLoader, skip) ++ if is_apple_mobile: ++ _require_loader(module, AppleFrameworkLoader, skip) ++ else: ++ _require_loader(module, ExtensionFileLoader, skip) + + def require_frozen(module, *, skip=True): + module = _require_loader(module, FrozenImporter, skip) +@@ -360,7 +364,7 @@ + self.assertEqual(cm.exception.path, _testcapi.__file__) + self.assertRegex( + str(cm.exception), +- r"cannot import name 'i_dont_exist' from '_testcapi' \(.*\.(so|pyd)\)" ++ r"cannot import name 'i_dont_exist' from '_testcapi' \(.*\.(so|dylib|pyd)\)" + ) + else: + self.assertEqual( +@@ -1678,6 +1682,12 @@ + os.set_blocking(r, False) + return (r, w) + ++ def create_extension_loader(self, modname, filename): ++ if is_apple_mobile: ++ return AppleFrameworkLoader(modname, filename, None) ++ else: ++ return ExtensionFileLoader(modname, filename) ++ + def import_script(self, name, fd, filename=None, check_override=None): + override_text = '' + if check_override is not None: +@@ -1872,7 +1882,7 @@ + def test_multi_init_extension_non_isolated_compat(self): + modname = '_test_non_isolated' + filename = _testmultiphase.__file__ +- loader = ExtensionFileLoader(modname, filename) ++ loader = self.create_extension_loader(modname, filename) + spec = importlib.util.spec_from_loader(modname, loader) + module = importlib.util.module_from_spec(spec) + loader.exec_module(module) +@@ -1890,7 +1900,7 @@ + def test_multi_init_extension_per_interpreter_gil_compat(self): + modname = '_test_shared_gil_only' + filename = _testmultiphase.__file__ +- loader = ExtensionFileLoader(modname, filename) ++ loader = self.create_extension_loader(modname, filename) + spec = importlib.util.spec_from_loader(modname, loader) + module = importlib.util.module_from_spec(spec) + loader.exec_module(module) +@@ -2020,10 +2030,13 @@ + @classmethod + def setUpClass(cls): + spec = importlib.util.find_spec(cls.NAME) +- from importlib.machinery import ExtensionFileLoader ++ from importlib.machinery import AppleFrameworkLoader, ExtensionFileLoader + cls.FILE = spec.origin + cls.LOADER = type(spec.loader) +- assert cls.LOADER is ExtensionFileLoader ++ if is_apple_mobile: ++ assert cls.LOADER is AppleFrameworkLoader ++ else: ++ assert cls.LOADER is ExtensionFileLoader + + # Start fresh. + cls.clean_up() +@@ -2063,7 +2076,10 @@ + """ + # This is essentially copied from the old imp module. + from importlib._bootstrap import _load +- loader = self.LOADER(name, path) ++ if is_apple_mobile: ++ loader = self.LOADER(name, path, None) ++ else: ++ loader = self.LOADER(name, path) + + # Issue bpo-24748: Skip the sys.modules check in _load_module_shim; + # always load new extension. +diff --git a/Lib/test/test_importlib/extension/test_finder.py b/Lib/test/test_importlib/extension/test_finder.py +index 1d5b6e7a5d..519a1c536b 100644 +--- a/Lib/test/test_importlib/extension/test_finder.py ++++ b/Lib/test/test_importlib/extension/test_finder.py +@@ -1,3 +1,4 @@ ++from test.support import is_apple_mobile + from test.test_importlib import abc, util + + machinery = util.import_importlib('importlib.machinery') +@@ -11,6 +12,8 @@ + """Test the finder for extension modules.""" + + def setUp(self): ++ if is_apple_mobile: ++ raise unittest.SkipTest(f"{sys.platform} uses a custom finder") + if not self.machinery.EXTENSION_SUFFIXES: + raise unittest.SkipTest("Requires dynamic loading support.") + if util.EXTENSIONS.name in sys.builtin_module_names: +diff --git a/Lib/test/test_importlib/extension/test_loader.py b/Lib/test/test_importlib/extension/test_loader.py +index 64c8a54851..fe3219c885 100644 +--- a/Lib/test/test_importlib/extension/test_loader.py ++++ b/Lib/test/test_importlib/extension/test_loader.py +@@ -1,3 +1,4 @@ ++from test.support import is_apple_mobile + from test.test_importlib import abc, util + + machinery = util.import_importlib('importlib.machinery') +@@ -16,6 +17,8 @@ + """Test ExtensionFileLoader.""" + + def setUp(self): ++ if is_apple_mobile: ++ raise unittest.SkipTest(f"{sys.platform} uses a custom loader") + if not self.machinery.EXTENSION_SUFFIXES: + raise unittest.SkipTest("Requires dynamic loading support.") + if util.EXTENSIONS.name in sys.builtin_module_names: diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 022cf21a47..67f484d40e 100644 --- a/Lib/test/test_io.py @@ -697,7 +930,7 @@ index 022cf21a47..67f484d40e 100644 'largefile', 'test requires %s bytes and a long time to run' % self.LARGE) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py -index ab969ce26a..9a9cacd6a5 100644 +index ab969ce26a..e6aee6c7de 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -43,6 +43,7 @@ @@ -708,30 +941,6 @@ index ab969ce26a..9a9cacd6a5 100644 from test.support import os_helper from test.support import socket_helper from test.support import threading_helper -@@ -1923,6 +1924,7 @@ - - - @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") -+@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - class UnixSocketHandlerTest(SocketHandlerTest): - - """Test for SocketHandler with unix sockets.""" -@@ -2003,6 +2005,7 @@ - self.assertEqual(self.log_output, "spam\neggs\n") - - @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") -+@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - class UnixDatagramHandlerTest(DatagramHandlerTest): - - """Test for DatagramHandler using Unix sockets.""" -@@ -2094,6 +2097,7 @@ - self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00') - - @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") -+@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - class UnixSysLogHandlerTest(SysLogHandlerTest): - - """Test for SysLogHandler with Unix sockets.""" diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py index 3d9d6d5d0a..dfb1d6f84d 100644 --- a/Lib/test/test_marshal.py @@ -784,8 +993,20 @@ index dfcf303942..5aabfac885 100644 requires('largefile', 'test requires %s bytes and a long time to run' % str(0x180000000)) f = open(TESTFN, 'w+b') +diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py +index 398393b233..52b0a2ac23 100644 +--- a/Lib/test/test_os.py ++++ b/Lib/test/test_os.py +@@ -3785,6 +3785,7 @@ + self.assertGreaterEqual(size.columns, 0) + self.assertGreaterEqual(size.lines, 0) + ++ @support.requires_subprocess() + def test_stty_match(self): + """Check if stty returns the same results + diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py -index 2169733503..753a137d66 100644 +index 2169733503..842fed6a67 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -8,7 +8,7 @@ @@ -816,8 +1037,18 @@ index 2169733503..753a137d66 100644 @unittest.skipUnless(sys.platform == 'darwin', "OSX only test") def test_mac_ver_with_fork(self): +@@ -472,7 +475,8 @@ + 'root:xnu-4570.71.2~1/RELEASE_X86_64'), + 'x86_64', 'i386') + arch = ('64bit', '') +- with mock.patch.object(platform, 'uname', return_value=uname), \ ++ with mock.patch.object(sys, "platform", "darwin"), \ ++ mock.patch.object(platform, 'uname', return_value=uname), \ + mock.patch.object(platform, 'architecture', return_value=arch): + for mac_ver, expected_terse, expected in [ + # darwin: mac_ver() returns empty strings diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py -index 9d72dba159..f12e9bb0cb 100644 +index 9d72dba159..a8b50606ac 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -2,6 +2,7 @@ @@ -873,15 +1104,73 @@ index 9d72dba159..f12e9bb0cb 100644 self.assertRaises(OSError, posix.sched_get_priority_min, -23) self.assertRaises(OSError, posix.sched_get_priority_max, -23) +@@ -1899,11 +1908,13 @@ + + + @unittest.skipUnless(hasattr(os, 'posix_spawn'), "test needs os.posix_spawn") ++@support.requires_subprocess() + class TestPosixSpawn(unittest.TestCase, _PosixSpawnMixin): + spawn_func = getattr(posix, 'posix_spawn', None) + + + @unittest.skipUnless(hasattr(os, 'posix_spawnp'), "test needs os.posix_spawnp") ++@support.requires_subprocess() + class TestPosixSpawnP(unittest.TestCase, _PosixSpawnMixin): + spawn_func = getattr(posix, 'posix_spawnp', None) + +diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py +index f31a68c5d8..062a7524dd 100644 +--- a/Lib/test/test_pty.py ++++ b/Lib/test/test_pty.py +@@ -1,11 +1,15 @@ ++import sys ++import unittest + from test.support import verbose, reap_children ++from test.support import is_apple_mobile, is_emscripten, is_wasi + from test.support.import_helper import import_module + + # Skip these tests if termios or fcntl are not available + import_module('termios') +-# fcntl is a proxy for not being one of the wasm32 platforms even though we +-# don't use this module... a proper check for what crashes those is needed. +-import_module("fcntl") ++ ++# Skip tests on WASM platforms, plus iOS/tvOS/watchOS ++if is_apple_mobile or is_emscripten or is_wasi: ++ raise unittest.SkipTest(f"pty tests not required on {sys.platform}") + + import errno + import os +@@ -16,7 +20,6 @@ + import signal + import socket + import io # readline +-import unittest + import warnings + + TEST_STRING_1 = b"I wish to buy a fish license.\n" +diff --git a/Lib/test/test_selectors.py b/Lib/test/test_selectors.py +index 677349c2bf..f4471bdf8c 100644 +--- a/Lib/test/test_selectors.py ++++ b/Lib/test/test_selectors.py +@@ -526,7 +526,7 @@ + try: + fds = s.select() + except OSError as e: +- if e.errno == errno.EINVAL and sys.platform == 'darwin': ++ if e.errno == errno.EINVAL and (sys.platform == 'darwin' or support.is_apple_mobile): + # unexplainable errors on macOS don't need to fail the test + self.skipTest("Invalid argument error calling poll()") + raise diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py -index d231e66b7b..748839cdfb 100644 +index d231e66b7b..182eeaaca4 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -2051,6 +2051,7 @@ check_chown(dirname, uid, gid) -+@unittest.skipIf(support.has_subprocess_support, 'Test requires support for subprocesses.') ++@support.requires_subprocess() class TestWhich(BaseTest, unittest.TestCase): def setUp(self): @@ -889,12 +1178,59 @@ index d231e66b7b..748839cdfb 100644 self.assertGreaterEqual(size.lines, 0) @unittest.skipUnless(os.isatty(sys.__stdout__.fileno()), "not on tty") -+ @unittest.skipUnless(support.has_subprocess_support, 'Test requires support for subprocesses.') ++ @support.requires_subprocess() @unittest.skipUnless(hasattr(os, 'get_terminal_size'), 'need os.get_terminal_size()') def test_stty_match(self): +diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py +index f2ae28c38d..f1f5c56eb8 100644 +--- a/Lib/test/test_signal.py ++++ b/Lib/test/test_signal.py +@@ -12,6 +12,7 @@ + import time + import unittest + from test import support ++from test.support import is_apple_mobile + from test.support import os_helper + from test.support.script_helper import assert_python_ok, spawn_python + from test.support import threading_helper +@@ -806,7 +807,7 @@ + self.assertEqual(self.hndl_called, True) + + # Issue 3864, unknown if this affects earlier versions of freebsd also +- @unittest.skipIf(sys.platform in ('netbsd5',), ++ @unittest.skipIf(sys.platform in ('netbsd5',) or is_apple_mobile, + 'itimer not reliable (does not mix well with threading) on some BSDs.') + def test_itimer_virtual(self): + self.itimer = signal.ITIMER_VIRTUAL +@@ -1242,6 +1243,8 @@ + + @unittest.skipUnless(hasattr(signal, "setitimer"), + "test needs setitimer()") ++ @unittest.skipIf(is_apple_mobile, ++ f"{sys.platform} doesn't support setitimer().") + def test_stress_delivery_dependent(self): + """ + This test uses dependent signal handlers. +@@ -1288,6 +1291,8 @@ + + @unittest.skipUnless(hasattr(signal, "setitimer"), + "test needs setitimer()") ++ @unittest.skipIf(is_apple_mobile, ++ f"{sys.platform} doesn't support setitimer().") + def test_stress_delivery_simultaneous(self): + """ + This test uses simultaneous signal handlers. +@@ -1321,6 +1326,7 @@ + @unittest.skipUnless(hasattr(signal, "SIGUSR1"), + "test needs SIGUSR1") + @threading_helper.requires_working_threading() ++ @unittest.skipIf(is_apple_mobile, "too stressful for iOS") + def test_stress_modifying_handlers(self): + # bpo-43406: race condition between trip_signal() and signal.signal + signum = signal.SIGUSR1 diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py -index 86701caf05..5f8459beed 100644 +index 86701caf05..155c4c458c 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1,5 +1,6 @@ @@ -958,42 +1294,8 @@ index 86701caf05..5f8459beed 100644 def testFDPassEmpty(self): # Try to pass an empty FD array. Can receive either no array # or an empty array. -@@ -4547,28 +4548,33 @@ - pass - - @requireAttrs(socket.socket, "sendmsg") -+@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - @requireAttrs(socket, "AF_UNIX") - class SendmsgUnixStreamTest(SendmsgStreamTests, SendrecvmsgUnixStreamTestBase): - pass - - @requireAttrs(socket.socket, "recvmsg") -+@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - @requireAttrs(socket, "AF_UNIX") - class RecvmsgUnixStreamTest(RecvmsgTests, RecvmsgGenericStreamTests, - SendrecvmsgUnixStreamTestBase): - pass - - @requireAttrs(socket.socket, "recvmsg_into") -+@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - @requireAttrs(socket, "AF_UNIX") - class RecvmsgIntoUnixStreamTest(RecvmsgIntoTests, RecvmsgGenericStreamTests, - SendrecvmsgUnixStreamTestBase): - pass - - @requireAttrs(socket.socket, "sendmsg", "recvmsg") -+@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - @requireAttrs(socket, "AF_UNIX", "SOL_SOCKET", "SCM_RIGHTS") - class RecvmsgSCMRightsStreamTest(SCMRightsTest, SendrecvmsgUnixStreamTestBase): - pass - - @requireAttrs(socket.socket, "sendmsg", "recvmsg_into") -+@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - @requireAttrs(socket, "AF_UNIX", "SOL_SOCKET", "SCM_RIGHTS") - class RecvmsgIntoSCMRightsStreamTest(RecvmsgIntoMixin, SCMRightsTest, - SendrecvmsgUnixStreamTestBase): diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py -index 0f62f9eb20..f7a8a82e1d 100644 +index 0f62f9eb20..d2daf044d6 100644 --- a/Lib/test/test_socketserver.py +++ b/Lib/test/test_socketserver.py @@ -8,12 +8,13 @@ @@ -1011,34 +1313,93 @@ index 0f62f9eb20..f7a8a82e1d 100644 from test.support import os_helper from test.support import socket_helper from test.support import threading_helper -@@ -181,12 +182,14 @@ - self.stream_examine) - - @requires_unix_sockets -+ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - def test_UnixStreamServer(self): - self.run_server(socketserver.UnixStreamServer, - socketserver.StreamRequestHandler, - self.stream_examine) - - @requires_unix_sockets -+ @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) - def test_ThreadingUnixStreamServer(self): - self.run_server(socketserver.ThreadingUnixStreamServer, - socketserver.StreamRequestHandler, +diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py +index f3efe0f52f..ff29b98a80 100644 +--- a/Lib/test/test_sqlite3/test_dbapi.py ++++ b/Lib/test/test_sqlite3/test_dbapi.py +@@ -31,7 +31,7 @@ + + from test.support import ( + SHORT_TIMEOUT, check_disallow_instantiation, requires_subprocess, +- is_emscripten, is_wasi ++ is_apple_mobile, is_emscripten, is_wasi + ) + from test.support import gc_collect + from test.support import threading_helper +@@ -667,7 +667,7 @@ + cx.execute(self._sql) + + @unittest.skipIf(sys.platform == "win32", "skipped on Windows") +- @unittest.skipIf(sys.platform == "darwin", "skipped on macOS") ++ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipped on Apple platforms") + @unittest.skipIf(is_emscripten or is_wasi, "not supported on Emscripten/WASI") + @unittest.skipUnless(TESTFN_UNDECODABLE, "only works if there are undecodable paths") + def test_open_with_undecodable_path(self): +@@ -713,7 +713,7 @@ + cx.execute(self._sql) + + @unittest.skipIf(sys.platform == "win32", "skipped on Windows") +- @unittest.skipIf(sys.platform == "darwin", "skipped on macOS") ++ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipped on Apple platforms") + @unittest.skipIf(is_emscripten or is_wasi, "not supported on Emscripten/WASI") + @unittest.skipUnless(TESTFN_UNDECODABLE, "only works if there are undecodable paths") + def test_open_undecodable_uri(self): diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py -index 2a6813f00b..c1a7d6eb08 100644 +index 2a6813f00b..1a65e8f44d 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py -@@ -338,7 +338,7 @@ +@@ -6,7 +6,7 @@ + from copy import copy + + from test.support import ( +- captured_stdout, PythonSymlink, requires_subprocess, is_wasi ++ captured_stdout, PythonSymlink, requires_subprocess, is_apple_mobile, is_wasi + ) + from test.support.import_helper import import_module + from test.support.os_helper import (TESTFN, unlink, skip_unless_symlink, +@@ -333,12 +333,14 @@ + # XXX more platforms to tests here + + @unittest.skipIf(is_wasi, "Incompatible with WASI mapdir and OOT builds") ++ @unittest.skipIf(is_apple_mobile, ++ f"{sys.platform} doesn't distribute config.h in the runtime environment") + def test_get_config_h_filename(self): + config_h = sysconfig.get_config_h_filename() self.assertTrue(os.path.isfile(config_h), config_h) def test_get_scheme_names(self): - wanted = ['nt', 'posix_home', 'posix_prefix', 'posix_venv', 'nt_venv', 'venv'] -+ wanted = ['nt', 'posix_home', 'posix_prefix', 'posix_venv', 'nt_venv', 'venv', 'tvos', 'watchos'] ++ wanted = ['ios', 'nt', 'posix_home', 'posix_prefix', 'posix_venv', 'nt_venv', 'venv', 'tvos', 'watchos'] if HAS_USER_BASE: wanted.extend(['nt_user', 'osx_framework_user', 'posix_user']) self.assertEqual(get_scheme_names(), tuple(sorted(wanted))) +@@ -410,6 +412,8 @@ + self.assertTrue(library.startswith(f'python{major}{minor}')) + self.assertTrue(library.endswith('.dll')) + self.assertEqual(library, ldlibrary) ++ elif is_apple_mobile: ++ self.assertEqual(ldlibrary, "Python.framework/Python") + else: + self.assertTrue(library.startswith(f'libpython{major}.{minor}')) + self.assertTrue(library.endswith('.a')) +@@ -460,6 +464,8 @@ + self.assertEqual(my_platform, test_platform) + + @unittest.skipIf(is_wasi, "Incompatible with WASI mapdir and OOT builds") ++ @unittest.skipIf(is_apple_mobile, ++ f"{sys.platform} doesn't distribute config.h in the runtime environment") + def test_srcdir(self): + # See Issues #15322, #15364. + srcdir = sysconfig.get_config_var('srcdir') +@@ -526,6 +532,8 @@ + @unittest.skipIf(sys.platform.startswith('win'), + 'Test is not Windows compatible') + @unittest.skipIf(is_wasi, "Incompatible with WASI mapdir and OOT builds") ++ @unittest.skipIf(is_apple_mobile, ++ f"{sys.platform} doesn't distribute config.h in the runtime environment") + def test_get_makefile_filename(self): + makefile = sysconfig.get_makefile_filename() + self.assertTrue(os.path.isfile(makefile), makefile) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 00a64372b3..539db5d7d7 100644 --- a/Lib/test/test_threading.py @@ -1068,6 +1429,86 @@ index 00a64372b3..539db5d7d7 100644 def test_threads_join_2(self): # Same as above, but a delay gets introduced after the thread's # Python code returned but before the thread state is deleted. +diff --git a/Lib/test/test_unicode_file_functions.py b/Lib/test/test_unicode_file_functions.py +index 47619c8807..3f36be1a49 100644 +--- a/Lib/test/test_unicode_file_functions.py ++++ b/Lib/test/test_unicode_file_functions.py +@@ -5,7 +5,7 @@ + import unittest + import warnings + from unicodedata import normalize +-from test.support import os_helper ++from test.support import is_apple_mobile, os_helper + from test import support + + +@@ -23,13 +23,13 @@ + '10_\u1fee\u1ffd', + ] + +-# Mac OS X decomposes Unicode names, using Normal Form D. ++# Apple platforms decompose Unicode names, using Normal Form D. + # http://developer.apple.com/mac/library/qa/qa2001/qa1173.html + # "However, most volume formats do not follow the exact specification for + # these normal forms. For example, HFS Plus uses a variant of Normal Form D + # in which U+2000 through U+2FFF, U+F900 through U+FAFF, and U+2F800 through + # U+2FAFF are not decomposed." +-if sys.platform != 'darwin': ++if sys.platform != 'darwin' and not is_apple_mobile: + filenames.extend([ + # Specific code points: NFC(fn), NFD(fn), NFKC(fn) and NFKD(fn) all different + '11_\u0385\u03d3\u03d4', +@@ -123,7 +123,7 @@ + # NFD (a variant of Unicode NFD form). Normalize the filename to NFC, NFKC, + # NFKD in Python is useless, because darwin will normalize it later and so + # open(), os.stat(), etc. don't raise any exception. +- @unittest.skipIf(sys.platform == 'darwin', 'irrelevant test on Mac OS X') ++ @unittest.skipIf(sys.platform == 'darwin' or is_apple_mobile, 'irrelevant test on Apple platforms') + @unittest.skipIf( + support.is_emscripten or support.is_wasi, + "test fails on Emscripten/WASI when host platform is macOS." +@@ -145,7 +145,7 @@ + # Skip the test on darwin, because darwin uses a normalization different + # than Python NFD normalization: filenames are different even if we use + # Python NFD normalization. +- @unittest.skipIf(sys.platform == 'darwin', 'irrelevant test on Mac OS X') ++ @unittest.skipIf(sys.platform == 'darwin' or is_apple_mobile, 'irrelevant test on Apple platforms') + def test_listdir(self): + sf0 = set(self.files) + with warnings.catch_warnings(): +diff --git a/Lib/test/test_unittest/test_break.py b/Lib/test/test_unittest/test_break.py +index 1da98af3e7..d413d5a478 100644 +--- a/Lib/test/test_unittest/test_break.py ++++ b/Lib/test/test_unittest/test_break.py +@@ -11,6 +11,8 @@ + + @unittest.skipUnless(hasattr(os, 'kill'), "Test requires os.kill") + @unittest.skipIf(sys.platform =="win32", "Test cannot run on Windows") ++@unittest.skipIf(support.is_apple_mobile, ++ f"{sys.platform} doesn't support SIGINT.") + class TestBreak(unittest.TestCase): + int_handler = None + # This number was smart-guessed, previously tests were failing +diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py +index 99c9e24994..fa528a6758 100644 +--- a/Lib/test/test_urllib2.py ++++ b/Lib/test/test_urllib2.py +@@ -1,6 +1,7 @@ + import unittest + from test import support + from test.support import os_helper ++from test.support import requires_subprocess + from test.support import warnings_helper + from test import test_urllib + from unittest import mock +@@ -998,6 +999,7 @@ + + file_obj.close() + ++ @requires_subprocess() + def test_http_body_pipe(self): + # A file reading from a pipe. + # A pipe cannot be seek'ed. There is no way to determine the diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 890672c5d2..dce68e1e40 100644 --- a/Lib/test/test_venv.py @@ -1153,7 +1594,7 @@ index 8b0628745c..2d8de8aecb 100755 # # Platform support for Windows diff --git a/Makefile.pre.in b/Makefile.pre.in -index 4996c5c309..24ee442643 100644 +index 4996c5c309..7571c5f4ad 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -184,6 +184,8 @@ @@ -1213,7 +1654,29 @@ index 4996c5c309..24ee442643 100644 # This rule builds the Cygwin Python DLL and import library if configured # for a shared core library; otherwise, this rule is a noop. $(DLLLIBRARY) libpython$(LDVERSION).dll.a: $(LIBRARY_OBJS) -@@ -1924,7 +1940,7 @@ +@@ -1885,6 +1901,21 @@ + $(RUNSHARED) /usr/libexec/oah/translate \ + ./$(BUILDPYTHON) -E -m test -j 0 -u all $(TESTOPTS) + ++# Run the test suite on the iOS simulator. ++# Must be run on a macOS machine with a full Xcode install that has an iPhone SE ++# (3rd edition) simulator available, after running `make install` on a configuration ++# with --enable-framework="./Tools/iOSTestbed/Python.xcframework/ios-arm64_x86_64-simulator" ++XCRESULT=./build/$(MULTIARCH).$(shell date +%s).xcresult ++.PHONY: testiOS ++testiOS: ++ # Run the test suite for the Xcode project, targeting the iOS simulator. ++ # If the suite fails, extract and print the console output, then re-raise the failure ++ if ! xcodebuild test -project ./Tools/iOSTestbed/iOSTestbed.xcodeproj -scheme "iOSTestbed" -destination "platform=iOS Simulator,name=iPhone SE (3rd Generation)" -resultBundlePath $(XCRESULT) ; then \ ++ xcrun xcresulttool get --path $(XCRESULT) --id $$(xcrun xcresulttool get --path $(XCRESULT) --format json | $(PYTHON_FOR_BUILD) -c "import sys, json; result = json.load(sys.stdin); print(result['actions']['_values'][0]['actionResult']['logRef']['id']['_value'])"); \ ++ echo ; \ ++ exit 1; \ ++ fi ++ + # Like test, but using --slow-ci which enables all test resources and use + # longer timeout. Run an optional pybuildbot.identify script to include + # information about the build environment. +@@ -1924,7 +1955,7 @@ # which can lead to two parallel `./python setup.py build` processes that # step on each others toes. .PHONY: install @@ -1222,7 +1685,7 @@ index 4996c5c309..24ee442643 100644 if test "x$(ENSUREPIP)" != "xno" ; then \ case $(ENSUREPIP) in \ upgrade) ensurepip="--upgrade" ;; \ -@@ -2507,20 +2523,32 @@ +@@ -2507,20 +2538,36 @@ exit 1; \ else true; \ fi @@ -1231,7 +1694,13 @@ index 4996c5c309..24ee442643 100644 - echo "Creating directory $(DESTDIR)$$i"; \ - $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$$i; \ - else true; \ -- fi; \ ++ # iOS/tvOS/watchOS uses a non-versioned framework with Info.plist in the ++ # framework root, no .lproj data, and binaries ++ @if test "$(MACHDEP)" = ios -o "$(MACHDEP)" = tvos -o "$(MACHDEP)" = watchos; then \ ++ if test -d $(PYTHONFRAMEWORKPREFIX)/include; then \ ++ echo "Clearing stale header symlink directory"; \ ++ rm -rf $(PYTHONFRAMEWORKPREFIX)/include; \ + fi; \ - done - $(LN) -fsn include/python$(LDVERSION) $(DESTDIR)$(prefix)/Headers - sed 's/%VERSION%/'"`$(RUNSHARED) ./$(BUILDPYTHON) -c 'import platform; print(platform.python_version())'`"'/g' < $(RESSRCDIR)/Info.plist > $(DESTDIR)$(prefix)/Resources/Info.plist @@ -1240,9 +1709,6 @@ index 4996c5c309..24ee442643 100644 - $(LN) -fsn Versions/Current/Headers $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Headers - $(LN) -fsn Versions/Current/Resources $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Resources - $(INSTALL_SHARED) $(LDLIBRARY) $(DESTDIR)$(PYTHONFRAMEWORKPREFIX)/$(LDLIBRARY) -+ # iOS/tvOS/watchOS uses a non-versioned framework with Info.plist in the -+ # framework root, no .lproj data, and binaries -+ @if test "$(MACHDEP)" = ios -o "$(MACHDEP)" = tvos -o "$(MACHDEP)" = watchos; then \ + $(INSTALL) -d -m $(DIRMODE) $(PYTHONFRAMEWORKINSTALLDIR); \ + sed 's/%VERSION%/'"`$(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import platform; print(platform.python_version())'`"'/g' < $(RESSRCDIR)/Info.plist > $(PYTHONFRAMEWORKINSTALLDIR)/Info.plist; \ + $(INSTALL_SHARED) $(LDLIBRARY) $(PYTHONFRAMEWORKPREFIX)/$(LDLIBRARY); \ @@ -1269,7 +1735,7 @@ index 4996c5c309..24ee442643 100644 # This installs Mac/Lib into the framework # Install a number of symlinks to keep software that expects a normal unix -@@ -2562,6 +2590,15 @@ +@@ -2562,6 +2609,19 @@ frameworkinstallextras: cd Mac && $(MAKE) installextras DESTDIR="$(DESTDIR)" @@ -1279,6 +1745,10 @@ index 4996c5c309..24ee442643 100644 +# Python.framework; Move the headers to their final framework-compatible home. +.PHONY: frameworkinstallmobileheaders +frameworkinstallmobileheaders: ++ if test -d $(PYTHONFRAMEWORKINSTALLDIR)/Headers; then \ ++ echo "Removing old framework headers"; \ ++ rm -rf $(PYTHONFRAMEWORKINSTALLDIR)/Headers; \ ++ fi + mv "$(PYTHONFRAMEWORKPREFIX)/include/python$(VERSION)" "$(PYTHONFRAMEWORKINSTALLDIR)/Headers" + $(LN) -fs "$(PYTHONFRAMEWORKDIR)" "$(PYTHONFRAMEWORKPREFIX)/include/python$(VERSION)" + @@ -1473,7 +1943,7 @@ index a4d9466559..8f51bef22d 100644 sin(pi*x), giving accurate results for all finite x (especially x integral or close to an integer). This is here for use in the diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c -index 650ae4bbd6..95c1b3633c 100644 +index 650ae4bbd6..f1d90ea23b 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -92,6 +92,8 @@ @@ -1485,34 +1955,19 @@ index 650ae4bbd6..95c1b3633c 100644 #if defined(__has_builtin) #if __has_builtin(__builtin_available) #define HAVE_BUILTIN_AVAILABLE 1 -@@ -369,6 +371,26 @@ +@@ -369,6 +371,11 @@ # define fsync _commit #endif /* ! __WATCOMC__ || __QNX__ */ -+// iOS/tvOS/watchOS *define* a number of POSIX functions, but you can't use them -+// because they aren't conventional multiprocess environment. ++// iOS/tvOS/watchOS *define* getgroups, but it returns an error if used. +#if TARGET_OS_IPHONE -+# undef HAVE_EXECV -+# undef HAVE_FORK -+# undef HAVE_FORK1 -+# undef HAVE_FORKPTY +# undef HAVE_GETGROUPS -+# undef HAVE_POSIX_SPAWN -+# undef HAVE_POSIX_SPAWNP -+# undef HAVE_SCHED_H -+# undef HAVE_SENDFILE -+# undef HAVE_SETPRIORITY -+# undef HAVE_SPAWNV -+# undef HAVE_WAIT -+# undef HAVE_WAIT3 -+# undef HAVE_WAIT4 -+# undef HAVE_WAITPID +#endif + /*[clinic input] # one of the few times we lie about this name! module os -@@ -1548,7 +1570,9 @@ +@@ -1548,7 +1555,9 @@ */ #include #elif !defined(_MSC_VER) && (!defined(__WATCOMC__) || defined(__QNX__) || defined(__VXWORKS__)) @@ -1522,7 +1977,7 @@ index 650ae4bbd6..95c1b3633c 100644 #endif /* !_MSC_VER */ static PyObject * -@@ -1564,6 +1588,7 @@ +@@ -1564,6 +1573,7 @@ d = PyDict_New(); if (d == NULL) return NULL; @@ -1530,7 +1985,7 @@ index 650ae4bbd6..95c1b3633c 100644 #ifdef MS_WINDOWS /* _wenviron must be initialized in this way if the program is started through main() instead of wmain(). */ -@@ -1617,6 +1642,7 @@ +@@ -1617,6 +1627,7 @@ Py_DECREF(k); Py_DECREF(v); } @@ -1538,7 +1993,7 @@ index 650ae4bbd6..95c1b3633c 100644 return d; } -@@ -5750,6 +5776,9 @@ +@@ -5750,6 +5761,9 @@ /*[clinic end generated code: output=290fc437dd4f33a0 input=86a58554ba6094af]*/ { long result; @@ -1548,7 +2003,7 @@ index 650ae4bbd6..95c1b3633c 100644 const char *bytes = PyBytes_AsString(command); if (PySys_Audit("os.system", "(O)", command) < 0) { -@@ -5759,6 +5788,7 @@ +@@ -5759,6 +5773,7 @@ Py_BEGIN_ALLOW_THREADS result = system(bytes); Py_END_ALLOW_THREADS @@ -1556,7 +2011,7 @@ index 650ae4bbd6..95c1b3633c 100644 return result; } #endif -@@ -15000,6 +15030,7 @@ +@@ -15000,6 +15015,7 @@ int is_symlink; int need_stat; #endif @@ -1564,7 +2019,7 @@ index 650ae4bbd6..95c1b3633c 100644 #ifdef MS_WINDOWS unsigned long dir_bits; #endif -@@ -15060,6 +15091,7 @@ +@@ -15060,6 +15076,7 @@ #endif return result; @@ -1634,10 +2089,21 @@ index b7034369c4..a7d63abe5d 100644 "getpwnam(): name not found: %R", name); } diff --git a/Modules/timemodule.c b/Modules/timemodule.c -index 6a872a285d..59b48c0ea4 100644 +index 6a872a285d..197eadf55f 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c -@@ -113,6 +113,11 @@ +@@ -59,6 +59,10 @@ + # define HAVE_CLOCK_GETTIME_RUNTIME 1 + #endif + ++// iOS/tvOS/watchOS *define* clock_settime, but it can't be used ++#if TARGET_OS_IPHONE ++# undef HAVE_CLOCK_SETTIME ++#endif + + #define SEC_TO_NS (1000 * 1000 * 1000) + +@@ -113,6 +117,11 @@ } @@ -1649,21 +2115,7 @@ index 6a872a285d..59b48c0ea4 100644 /* Forward declarations */ static int pysleep(_PyTime_t timeout); -@@ -304,11 +309,13 @@ - if (_PyTime_AsTimespec(t, &tp) == -1) - return NULL; - -+#if !TARGET_OS_IPHONE - ret = clock_settime((clockid_t)clk_id, &tp); - if (ret != 0) { - PyErr_SetFromErrno(PyExc_OSError); - return NULL; - } -+#endif - Py_RETURN_NONE; - } - -@@ -337,11 +344,13 @@ +@@ -337,11 +346,13 @@ return NULL; } @@ -1810,6 +2262,1035 @@ index 3debe7f7c1..612ba30da1 100644 /* dict ready */ ns = _PyNamespace_New(impl_info); +--- /dev/null ++++ b/Tools/iOSTestbed/Python.xcframework/Info.plist +@@ -0,0 +1,44 @@ ++ ++ ++ ++ ++ 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 ++ ++ ++ CFBundlePackageType ++ XFWK ++ XCFrameworkFormatVersion ++ 1.0 ++ ++ +--- /dev/null ++++ b/Tools/iOSTestbed/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/Tools/iOSTestbed/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/Tools/iOSTestbed/iOSTestbed.xcodeproj/project.pbxproj +@@ -0,0 +1,569 @@ ++// !$*UTF8*$! ++{ ++ archiveVersion = 1; ++ classes = { ++ }; ++ objectVersion = 56; ++ objects = { ++ ++/* Begin PBXBuildFile section */ ++ 607A66172B0EFA380010BFC8 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66162B0EFA380010BFC8 /* AppDelegate.m */; }; ++ 607A66222B0EFA390010BFC8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607A66212B0EFA390010BFC8 /* Assets.xcassets */; }; ++ 607A66252B0EFA390010BFC8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607A66232B0EFA390010BFC8 /* LaunchScreen.storyboard */; }; ++ 607A66282B0EFA390010BFC8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66272B0EFA390010BFC8 /* main.m */; }; ++ 607A66322B0EFA3A0010BFC8 /* iOSTestbedTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66312B0EFA3A0010BFC8 /* iOSTestbedTests.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, ); }; }; ++ 607A66582B0F079F0010BFC8 /* dylib-Info-template.plist in Resources */ = {isa = PBXBuildFile; fileRef = 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */; }; ++/* 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 = ( ++ 607A664D2B0EFC080010BFC8 /* Python.xcframework in Embed Frameworks */, ++ ); ++ name = "Embed Frameworks"; ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++ 607A66522B0EFFE00010BFC8 /* Embed Frameworks */ = { ++ isa = PBXCopyFilesBuildPhase; ++ buildActionMask = 2147483647; ++ dstPath = ""; ++ dstSubfolderSpec = 10; ++ files = ( ++ 607A66512B0EFFE00010BFC8 /* Python.xcframework in Embed Frameworks */, ++ ); ++ name = "Embed Frameworks"; ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++/* End PBXCopyFilesBuildPhase section */ ++ ++/* Begin PBXFileReference section */ ++ 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 /* iOSTestbedTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iOSTestbedTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; ++ 607A66312B0EFA3A0010BFC8 /* iOSTestbedTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = iOSTestbedTests.m; sourceTree = ""; }; ++ 607A664A2B0EFB310010BFC8 /* Python.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Python.xcframework; sourceTree = ""; }; ++ 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "dylib-Info-template.plist"; sourceTree = ""; }; ++ 607A66592B0F08600010BFC8 /* iOSTestbed-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "iOSTestbed-Info.plist"; sourceTree = ""; }; ++/* End PBXFileReference section */ ++ ++/* Begin PBXFrameworksBuildPhase section */ ++ 607A660F2B0EFA380010BFC8 /* Frameworks */ = { ++ isa = PBXFrameworksBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ 607A664C2B0EFC080010BFC8 /* Python.xcframework in Frameworks */, ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++ 607A662A2B0EFA3A0010BFC8 /* Frameworks */ = { ++ isa = PBXFrameworksBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ 607A66502B0EFFE00010BFC8 /* Python.xcframework in Frameworks */, ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++/* End PBXFrameworksBuildPhase section */ ++ ++/* Begin PBXGroup section */ ++ 607A66092B0EFA380010BFC8 = { ++ isa = PBXGroup; ++ children = ( ++ 607A664A2B0EFB310010BFC8 /* Python.xcframework */, ++ 607A66142B0EFA380010BFC8 /* iOSTestbed */, ++ 607A66302B0EFA3A0010BFC8 /* iOSTestbedTests */, ++ 607A66132B0EFA380010BFC8 /* Products */, ++ 607A664F2B0EFFE00010BFC8 /* Frameworks */, ++ ); ++ sourceTree = ""; ++ }; ++ 607A66132B0EFA380010BFC8 /* Products */ = { ++ isa = PBXGroup; ++ children = ( ++ 607A66122B0EFA380010BFC8 /* iOSTestbed.app */, ++ 607A662D2B0EFA3A0010BFC8 /* iOSTestbedTests.xctest */, ++ ); ++ name = Products; ++ sourceTree = ""; ++ }; ++ 607A66142B0EFA380010BFC8 /* iOSTestbed */ = { ++ isa = PBXGroup; ++ children = ( ++ 607A66592B0F08600010BFC8 /* iOSTestbed-Info.plist */, ++ 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */, ++ 607A66152B0EFA380010BFC8 /* AppDelegate.h */, ++ 607A66162B0EFA380010BFC8 /* AppDelegate.m */, ++ 607A66212B0EFA390010BFC8 /* Assets.xcassets */, ++ 607A66232B0EFA390010BFC8 /* LaunchScreen.storyboard */, ++ 607A66272B0EFA390010BFC8 /* main.m */, ++ ); ++ path = iOSTestbed; ++ sourceTree = ""; ++ }; ++ 607A66302B0EFA3A0010BFC8 /* iOSTestbedTests */ = { ++ isa = PBXGroup; ++ children = ( ++ 607A66312B0EFA3A0010BFC8 /* iOSTestbedTests.m */, ++ ); ++ path = iOSTestbedTests; ++ sourceTree = ""; ++ }; ++ 607A664F2B0EFFE00010BFC8 /* Frameworks */ = { ++ isa = PBXGroup; ++ children = ( ++ ); ++ name = Frameworks; ++ sourceTree = ""; ++ }; ++/* End PBXGroup section */ ++ ++/* Begin PBXNativeTarget section */ ++ 607A66112B0EFA380010BFC8 /* iOSTestbed */ = { ++ isa = PBXNativeTarget; ++ 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 */, ++ 607A664E2B0EFC080010BFC8 /* Embed Frameworks */, ++ ); ++ buildRules = ( ++ ); ++ dependencies = ( ++ ); ++ name = iOSTestbed; ++ productName = iOSTestbed; ++ productReference = 607A66122B0EFA380010BFC8 /* iOSTestbed.app */; ++ productType = "com.apple.product-type.application"; ++ }; ++ 607A662C2B0EFA3A0010BFC8 /* iOSTestbedTests */ = { ++ isa = PBXNativeTarget; ++ buildConfigurationList = 607A66442B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "iOSTestbedTests" */; ++ buildPhases = ( ++ 607A66292B0EFA3A0010BFC8 /* Sources */, ++ 607A662A2B0EFA3A0010BFC8 /* Frameworks */, ++ 607A662B2B0EFA3A0010BFC8 /* Resources */, ++ 607A66522B0EFFE00010BFC8 /* Embed Frameworks */, ++ ); ++ buildRules = ( ++ ); ++ dependencies = ( ++ 607A662F2B0EFA3A0010BFC8 /* PBXTargetDependency */, ++ ); ++ name = iOSTestbedTests; ++ productName = iOSTestbedTests; ++ productReference = 607A662D2B0EFA3A0010BFC8 /* iOSTestbedTests.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 "iOSTestbed" */; ++ compatibilityVersion = "Xcode 14.0"; ++ developmentRegion = en; ++ hasScannedForEncodings = 0; ++ knownRegions = ( ++ en, ++ Base, ++ ); ++ mainGroup = 607A66092B0EFA380010BFC8; ++ productRefGroup = 607A66132B0EFA380010BFC8 /* Products */; ++ projectDirPath = ""; ++ projectRoot = ""; ++ targets = ( ++ 607A66112B0EFA380010BFC8 /* iOSTestbed */, ++ 607A662C2B0EFA3A0010BFC8 /* iOSTestbedTests */, ++ ); ++ }; ++/* End PBXProject section */ ++ ++/* Begin PBXResourcesBuildPhase section */ ++ 607A66102B0EFA380010BFC8 /* Resources */ = { ++ isa = PBXResourcesBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ 607A66252B0EFA390010BFC8 /* LaunchScreen.storyboard in Resources */, ++ 607A66582B0F079F0010BFC8 /* dylib-Info-template.plist in Resources */, ++ 607A66222B0EFA390010BFC8 /* Assets.xcassets in Resources */, ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++ 607A662B2B0EFA3A0010BFC8 /* Resources */ = { ++ isa = PBXResourcesBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++/* End PBXResourcesBuildPhase section */ ++ ++/* Begin PBXShellScriptBuildPhase section */ ++ 607A66552B0F061D0010BFC8 /* Install Target Specific Python Standard Library */ = { ++ isa = PBXShellScriptBuildPhase; ++ alwaysOutOfDate = 1; ++ buildActionMask = 2147483647; ++ files = ( ++ ); ++ inputFileListPaths = ( ++ ); ++ inputPaths = ( ++ ); ++ name = "Install Target Specific Python Standard Library"; ++ outputFileListPaths = ( ++ ); ++ outputPaths = ( ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ shellPath = /bin/sh; ++ shellScript = "set -e\n\nmkdir -p \"$CODESIGNING_FOLDER_PATH/python/lib\"\nif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-iphonesimulator\" ]; then\n echo \"Installing Python modules for iOS Simulator\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64_x86_64-simulator/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nelse\n echo \"Installing Python modules for iOS Device\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nfi\n"; ++ }; ++ 607A66562B0F06200010BFC8 /* Prepare Python Binary Modules */ = { ++ isa = PBXShellScriptBuildPhase; ++ alwaysOutOfDate = 1; ++ buildActionMask = 2147483647; ++ files = ( ++ ); ++ inputFileListPaths = ( ++ ); ++ inputPaths = ( ++ ); ++ name = "Prepare Python Binary Modules"; ++ outputFileListPaths = ( ++ ); ++ outputPaths = ( ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ shellPath = /bin/sh; ++ shellScript = "set -e\n\ninstall_dylib () {\n INSTALL_BASE=$1\n FULL_DYLIB=$2\n\n # The name of the .dylib file\n DYLIB=$(basename \"$FULL_DYLIB\")\n # The name of the .dylib file, relative to the install base\n RELATIVE_DYLIB=${FULL_DYLIB#$CODESIGNING_FOLDER_PATH/$INSTALL_BASE/}\n # The full dotted name of the binary module, constructed from the file path.\n FULL_MODULE_NAME=$(echo $RELATIVE_DYLIB | 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_DYLIB\" \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 defaults write \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\" CFBundleExecutable -string \"$DYLIB\"\n defaults write \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\" CFBundleIdentifier -string \"$FRAMEWORK_BUNDLE_ID\" \n fi\n \n echo \"Installing binary for $RELATIVE_DYLIB\" \n mv \"$FULL_DYLIB\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\"\n}\n\nPYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/python/lib\")\necho \"Install Python $PYTHON_VER standard library dylibs...\"\nfind \"$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload\" -name \"*.dylib\" | while read FULL_DYLIB; do\n install_dylib python/lib/$PYTHON_VER/lib-dynload \"$FULL_DYLIB\"\ndone\n\n# Clean up dylib template \nrm -f \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\"\necho \"Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)...\"\nfind \"$CODESIGNING_FOLDER_PATH/Frameworks\" -name \"*.framework\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der \"{}\" \\; \n"; ++ }; ++/* End PBXShellScriptBuildPhase section */ ++ ++/* Begin PBXSourcesBuildPhase section */ ++ 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 /* iOSTestbedTests.m in Sources */, ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++/* End PBXSourcesBuildPhase section */ ++ ++/* Begin PBXTargetDependency section */ ++ 607A662F2B0EFA3A0010BFC8 /* PBXTargetDependency */ = { ++ isa = PBXTargetDependency; ++ 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; ++ 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 = iphoneos; ++ }; ++ 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 = iphoneos; ++ 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 = 3HEZE76D99; ++ ENABLE_USER_SCRIPT_SANDBOXING = NO; ++ HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; ++ 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 = 12.0; ++ LD_RUNPATH_SEARCH_PATHS = ( ++ "$(inherited)", ++ "@executable_path/Frameworks", ++ ); ++ MARKETING_VERSION = 3.13.0a1; ++ PRODUCT_BUNDLE_IDENTIFIER = org.python.iOSTestbed; ++ PRODUCT_NAME = "$(TARGET_NAME)"; ++ SWIFT_EMIT_LOC_STRINGS = YES; ++ TARGETED_DEVICE_FAMILY = "1,2"; ++ }; ++ 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 = 3HEZE76D99; ++ ENABLE_USER_SCRIPT_SANDBOXING = NO; ++ HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; ++ 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 = 12.0; ++ LD_RUNPATH_SEARCH_PATHS = ( ++ "$(inherited)", ++ "@executable_path/Frameworks", ++ ); ++ MARKETING_VERSION = 3.13.0a1; ++ PRODUCT_BUNDLE_IDENTIFIER = org.python.iOSTestbed; ++ PRODUCT_NAME = "$(TARGET_NAME)"; ++ SWIFT_EMIT_LOC_STRINGS = YES; ++ TARGETED_DEVICE_FAMILY = "1,2"; ++ }; ++ 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.iOSTestbedTests; ++ PRODUCT_NAME = "$(TARGET_NAME)"; ++ SWIFT_EMIT_LOC_STRINGS = NO; ++ TARGETED_DEVICE_FAMILY = "1,2"; ++ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/iOSTestbed.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/iOSTestbed"; ++ }; ++ 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.iOSTestbedTests; ++ PRODUCT_NAME = "$(TARGET_NAME)"; ++ SWIFT_EMIT_LOC_STRINGS = NO; ++ 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 "iOSTestbed" */ = { ++ isa = XCConfigurationList; ++ buildConfigurations = ( ++ 607A663F2B0EFA3A0010BFC8 /* Debug */, ++ 607A66402B0EFA3A0010BFC8 /* Release */, ++ ); ++ defaultConfigurationIsVisible = 0; ++ defaultConfigurationName = Release; ++ }; ++ 607A66412B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "iOSTestbed" */ = { ++ isa = XCConfigurationList; ++ buildConfigurations = ( ++ 607A66422B0EFA3A0010BFC8 /* Debug */, ++ 607A66432B0EFA3A0010BFC8 /* Release */, ++ ); ++ defaultConfigurationIsVisible = 0; ++ defaultConfigurationName = Release; ++ }; ++ 607A66442B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "iOSTestbedTests" */ = { ++ isa = XCConfigurationList; ++ buildConfigurations = ( ++ 607A66452B0EFA3A0010BFC8 /* Debug */, ++ 607A66462B0EFA3A0010BFC8 /* Release */, ++ ); ++ defaultConfigurationIsVisible = 0; ++ defaultConfigurationName = Release; ++ }; ++/* End XCConfigurationList section */ ++ }; ++ rootObject = 607A660A2B0EFA380010BFC8 /* Project object */; ++} +--- /dev/null ++++ b/Tools/iOSTestbed/iOSTestbed/AppDelegate.h +@@ -0,0 +1,14 @@ ++// ++// AppDelegate.h ++// iOSTestbed ++// ++// Created by Russell Keith-Magee on 23/11/2023. ++// ++ ++#import ++ ++@interface AppDelegate : UIResponder ++ ++ ++@end ++ +--- /dev/null ++++ b/Tools/iOSTestbed/iOSTestbed/AppDelegate.m +@@ -0,0 +1,22 @@ ++// ++// AppDelegate.m ++// iOSTestbed ++// ++// Created by Russell Keith-Magee on 23/11/2023. ++// ++ ++#import "AppDelegate.h" ++ ++@interface AppDelegate () ++ ++@end ++ ++@implementation AppDelegate ++ ++ ++- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ++ // Override point for customization after application launch. ++ return YES; ++} ++ ++@end +--- /dev/null ++++ b/Tools/iOSTestbed/iOSTestbed/Assets.xcassets/AccentColor.colorset/Contents.json +@@ -0,0 +1,11 @@ ++{ ++ "colors" : [ ++ { ++ "idiom" : "universal" ++ } ++ ], ++ "info" : { ++ "author" : "xcode", ++ "version" : 1 ++ } ++} +--- /dev/null ++++ b/Tools/iOSTestbed/iOSTestbed/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/Tools/iOSTestbed/iOSTestbed/Assets.xcassets/Contents.json +@@ -0,0 +1,6 @@ ++{ ++ "info" : { ++ "author" : "xcode", ++ "version" : 1 ++ } ++} +--- /dev/null ++++ b/Tools/iOSTestbed/iOSTestbed/Base.lproj/LaunchScreen.storyboard +@@ -0,0 +1,9 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ +--- /dev/null ++++ b/Tools/iOSTestbed/iOSTestbed/dylib-Info-template.plist +@@ -0,0 +1,26 @@ ++ ++ ++ ++ ++ CFBundleDevelopmentRegion ++ en ++ CFBundleExecutable ++ ++ CFBundleIdentifier ++ ++ CFBundleInfoDictionaryVersion ++ 6.0 ++ CFBundlePackageType ++ APPL ++ CFBundleShortVersionString ++ 1.0 ++ CFBundleSupportedPlatforms ++ ++ iPhoneOS ++ ++ MinimumOSVersion ++ 12.0 ++ CFBundleVersion ++ 1 ++ ++ +--- /dev/null ++++ b/Tools/iOSTestbed/iOSTestbed/iOSTestbed-Info.plist.in +@@ -0,0 +1,54 @@ ++ ++ ++ ++ ++ CFBundleDevelopmentRegion ++ en ++ CFBundleDisplayName ++ ${PRODUCT_NAME} ++ CFBundleExecutable ++ ${EXECUTABLE_NAME} ++ CFBundleIdentifier ++ org.python.iOSTestbed ++ CFBundleInfoDictionaryVersion ++ 6.0 ++ CFBundleName ++ ${PRODUCT_NAME} ++ CFBundlePackageType ++ APPL ++ CFBundleShortVersionString ++ @VERSION@ ++ CFBundleSignature ++ ???? ++ CFBundleVersion ++ 1 ++ LSRequiresIPhoneOS ++ ++ UIRequiresFullScreen ++ ++ UILaunchStoryboardName ++ Launch Screen ++ UISupportedInterfaceOrientations ++ ++ UIInterfaceOrientationPortrait ++ UIInterfaceOrientationLandscapeLeft ++ UIInterfaceOrientationLandscapeRight ++ ++ UISupportedInterfaceOrientations~ipad ++ ++ UIInterfaceOrientationPortrait ++ UIInterfaceOrientationPortraitUpsideDown ++ UIInterfaceOrientationLandscapeLeft ++ UIInterfaceOrientationLandscapeRight ++ ++ MainModule ++ ios ++ UIApplicationSceneManifest ++ ++ UIApplicationSupportsMultipleScenes ++ ++ UISceneConfigurations ++ ++ ++ ++ +--- /dev/null ++++ b/Tools/iOSTestbed/iOSTestbed/main.m +@@ -0,0 +1,23 @@ ++// ++// main.m ++// iOSTestbed ++// ++// Created by Russell Keith-Magee on 23/11/2023. ++// ++ ++#import ++#import "AppDelegate.h" ++ ++int main(int argc, char * argv[]) { ++ NSString * appDelegateClassName; ++ @autoreleasepool { ++ // Setup code that might create autoreleased objects goes here. ++ appDelegateClassName = NSStringFromClass([AppDelegate class]); ++ } ++ ++ // iOS doesn't like uncaught signals. ++ signal(SIGPIPE, SIG_IGN); ++ signal(SIGXFSZ, SIG_IGN); ++ ++ return UIApplicationMain(argc, argv, nil, appDelegateClassName); ++} +--- /dev/null ++++ b/Tools/iOSTestbed/iOSTestbedTests/iOSTestbedTests.m +@@ -0,0 +1,188 @@ ++#import ++#import ++ ++@interface iOSTestbedTests : XCTestCase ++ ++@end ++ ++@implementation iOSTestbedTests ++ ++ ++- (void)testPython { ++ // 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` ++ const char *argv[] = { ++ "iOSTestbed", // argv[0] is the process that is running. ++ "-uall,-subprocess,-gui,-curses", // Enable most resources ++ "-v", // run in verbose mode so we get test failure information ++ // To run a subset of tests, add the test names below; e.g., ++ // "test_os", ++ // "test_sys", ++ }; ++ ++ // Start a Python interpreter. ++ int success = -1; ++ PyStatus status; ++ PyPreConfig preconfig; ++ PyConfig config; ++ NSString *python_home; ++ NSString *path; ++ wchar_t *wtmp_str; ++ ++ PyObject *app_module; ++ PyObject *module; ++ PyObject *module_attr; ++ PyObject *method_args; ++ PyObject *result; ++ PyObject *exc_type; ++ PyObject *exc_value; ++ PyObject *exc_traceback; ++ PyObject *systemExit_code; ++ ++ NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; ++ ++ // Extract Python version from bundle ++ NSString *py_version_string = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; ++ ++ // Generate an isolated Python configuration. ++ NSLog(@"Configuring isolated Python %@...", py_version_string); ++ 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; ++ // 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; ++ // Disable the user site module ++ config.site_import = 0; ++ // 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); ++ ++ // Set the stdlib location for the Python interpreter ++ path = [NSString stringWithFormat:@"%@/python/lib/python%@", resourcePath, py_version_string, nil]; ++ NSLog(@"Stdlib dir: %@", path); ++ wtmp_str = Py_DecodeLocale([path UTF8String], NULL); ++ status = PyConfig_SetString(&config, &config.stdlib_dir, wtmp_str); ++ if (PyStatus_Exception(status)) { ++ XCTFail(@"Unable to set stdlib dir: %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, sizeof(argv) / sizeof(char *), (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; ++ } ++ ++ // Start the test suite. ++ // ++ // From here to Py_ObjectCall(runmodule...) is effectively ++ // a copy of Py_RunMain() (and, more specifically, the ++ // pymain_run_module() method); we need to re-implement it ++ // because we need to be able to inspect the error state of ++ // the interpreter, not just the return code of the module. ++ NSLog(@"Running CPython test suite"); ++ module = PyImport_ImportModule("runpy"); ++ if (module == NULL) { ++ XCTFail(@"Could not import runpy module"); ++ } ++ ++ module_attr = PyObject_GetAttrString(module, "_run_module_as_main"); ++ if (module_attr == NULL) { ++ XCTFail(@"Could not access runpy._run_module_as_main"); ++ } ++ ++ app_module = PyUnicode_FromString("test"); ++ if (app_module == NULL) { ++ XCTFail(@"Could not convert module name to unicode"); ++ } ++ ++ method_args = Py_BuildValue("(Oi)", app_module, 0); ++ if (method_args == NULL) { ++ XCTFail(@"Could not create arguments for runpy._run_module_as_main"); ++ } ++ ++ // Print a separator to differentiate Python startup logs from app logs ++ NSLog(@"---------------------------------------------------------------------------"); ++ ++ // Invoke the app module ++ result = PyObject_Call(module_attr, method_args, NULL); ++ ++ NSLog(@"---------------------------------------------------------------------------"); ++ ++ // The test method doesn't return an object of any interest; ++ // but if the call returns NULL, there's been a problem. ++ if (result == NULL) { ++ // Retrieve the current error state of the interpreter. ++ PyErr_Fetch(&exc_type, &exc_value, &exc_traceback); ++ PyErr_NormalizeException(&exc_type, &exc_value, &exc_traceback); ++ ++ if (exc_traceback == NULL) { ++ XCTFail(@"Could not retrieve traceback"); ++ } ++ ++ if (PyErr_GivenExceptionMatches(exc_value, PyExc_SystemExit)) { ++ systemExit_code = PyObject_GetAttrString(exc_value, "code"); ++ if (systemExit_code == NULL) { ++ XCTFail(@"Could not determine exit code"); ++ } ++ else { ++ success = (int) PyLong_AsLong(systemExit_code); ++ XCTAssertEqual(success, 0, @"Python test suite did not pass"); ++ } ++ } else { ++ PyErr_DisplayException(exc_value); ++ XCTFail(@"Test suite generated exception"); ++ } ++ } ++ Py_Finalize(); ++} ++ ++ ++@end diff --git a/config.sub b/config.sub index d74fb6deac..09ebc4287c 100755 --- a/config.sub @@ -1842,7 +3323,7 @@ index d74fb6deac..09ebc4287c 100755 # Blank kernel with real OS is always fine. ;; diff --git a/configure b/configure -index c87f518382..ad0cd39350 100755 +index c87f518382..0e24298436 100755 --- a/configure +++ b/configure @@ -963,10 +963,13 @@ @@ -1980,7 +3461,7 @@ index c87f518382..ad0cd39350 100755 if test "x${prefix}" = "xNONE"; then FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}" else -@@ -4128,65 +4218,112 @@ +@@ -4128,65 +4218,114 @@ PYTHONFRAMEWORKINSTALLDIR=$PYTHONFRAMEWORKPREFIX/$PYTHONFRAMEWORKDIR FRAMEWORKINSTALLFIRST="frameworkinstallstructure" FRAMEWORKALTINSTALLFIRST="frameworkinstallstructure " @@ -2018,12 +3499,7 @@ index c87f518382..ad0cd39350 100755 - /Library*) - FRAMEWORKINSTALLAPPSPREFIX="/Applications" - ;; -+ ;; -+ tvOS) : -+ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKPYTHONW= -+ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ ac_config_files="$ac_config_files Tools/iOSTestbed/iOSTestbed/iOSTestbed-Info.plist" - */Library/Frameworks) - MDIR="`dirname "${enableval}"`" @@ -2040,27 +3516,34 @@ index c87f518382..ad0cd39350 100755 - FRAMEWORKUNIXTOOLSPREFIX="${MDIR}" - fi - ;; -+ prefix=$PYTHONFRAMEWORKPREFIX -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" -+ RESSRCDIR=tvOS/Resources ++ ;; ++ tvOS) : ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" - *) - FRAMEWORKINSTALLAPPSPREFIX="/Applications" - ;; - esac -+ ac_config_files="$ac_config_files tvOS/Resources/Info.plist" ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=tvOS/Resources - prefix=$PYTHONFRAMEWORKINSTALLDIR/Versions/$VERSION ++ ac_config_files="$ac_config_files tvOS/Resources/Info.plist" + +- # Add files for Mac specific code to the list of output +- # files: +- ac_config_files="$ac_config_files Mac/Makefile" + ;; + watchOS) : + FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" + FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" + FRAMEWORKPYTHONW= + INSTALLTARGETS="libinstall inclinstall sharedinstall" - -- # Add files for Mac specific code to the list of output -- # files: -- ac_config_files="$ac_config_files Mac/Makefile" ++ + prefix=$PYTHONFRAMEWORKPREFIX + PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" + RESSRCDIR=watchOS/Resources @@ -2140,7 +3623,7 @@ index c87f518382..ad0cd39350 100755 else $as_nop -@@ -4194,6 +4331,8 @@ +@@ -4194,6 +4333,8 @@ PYTHONFRAMEWORKDIR=no-framework PYTHONFRAMEWORKPREFIX= PYTHONFRAMEWORKINSTALLDIR= @@ -2149,15 +3632,15 @@ index c87f518382..ad0cd39350 100755 FRAMEWORKINSTALLFIRST= FRAMEWORKINSTALLLAST= FRAMEWORKALTINSTALLFIRST= -@@ -4223,79 +4362,11 @@ +@@ -4223,79 +4364,11 @@ -printf "%s\n" "#define _PYTHONFRAMEWORK \"${PYTHONFRAMEWORK}\"" >>confdefs.h - - +- +- -# Set name for machine-dependent library files - +- -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking MACHDEP" >&5 -printf %s "checking MACHDEP... " >&6; } -if test -z "$MACHDEP" @@ -2206,7 +3689,7 @@ index c87f518382..ad0cd39350 100755 - ac_md_release=`echo $ac_sys_release | - tr -d '/ ' | sed 's/^[A-Z]\.//' | sed 's/\..*//'` - MACHDEP="$ac_md_system$ac_md_release" -- + - case $MACHDEP in - aix*) MACHDEP="aix";; - linux*) MACHDEP="linux";; @@ -2214,12 +3697,12 @@ index c87f518382..ad0cd39350 100755 - darwin*) MACHDEP="darwin";; - '') MACHDEP="unknown";; - esac -- + - if test "$ac_sys_system" = "SunOS"; then - # For Solaris, there isn't an OS version specific macro defined - # in most compilers, so we define one here. - SUNOS_VERSION=`echo $ac_sys_release | sed -e 's!\.\(0-9\)$!.0\1!g' | tr -d '.'` -- + -printf "%s\n" "#define Py_SUNOS_VERSION $SUNOS_VERSION" >>confdefs.h +printf "%s\n" "#define _PYTHONFRAMEWORK \"${PYTHONFRAMEWORK}\"" >>confdefs.h @@ -2230,7 +3713,7 @@ index c87f518382..ad0cd39350 100755 if test "$cross_compiling" = yes; then -@@ -4303,27 +4374,102 @@ +@@ -4303,27 +4376,102 @@ *-*-linux*) case "$host_cpu" in arm*) @@ -2339,7 +3822,7 @@ index c87f518382..ad0cd39350 100755 fi # Some systems cannot stand _XOPEN_SOURCE being defined at all; they -@@ -4390,6 +4536,13 @@ +@@ -4390,6 +4538,13 @@ define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; @@ -2353,7 +3836,7 @@ index c87f518382..ad0cd39350 100755 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -4484,6 +4637,32 @@ +@@ -4484,6 +4639,32 @@ ;; esac @@ -2386,7 +3869,7 @@ index c87f518382..ad0cd39350 100755 if test "$ac_sys_system" = "Darwin" then # Extract the first word of "xcrun", so it can be a program name with args. -@@ -6746,6 +6925,12 @@ +@@ -6746,6 +6927,12 @@ case $ac_sys_system in #( Darwin*) : MULTIARCH="" ;; #( @@ -2399,7 +3882,7 @@ index c87f518382..ad0cd39350 100755 FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -6753,8 +6938,6 @@ +@@ -6753,8 +6940,6 @@ ;; esac @@ -2408,7 +3891,7 @@ index c87f518382..ad0cd39350 100755 if test x$PLATFORM_TRIPLET != x && test x$MULTIARCH != x; then if test x$PLATFORM_TRIPLET != x$MULTIARCH; then -@@ -6764,6 +6947,16 @@ +@@ -6764,6 +6949,16 @@ MULTIARCH=$PLATFORM_TRIPLET fi @@ -2425,7 +3908,7 @@ index c87f518382..ad0cd39350 100755 if test x$MULTIARCH != x; then MULTIARCH_CPPFLAGS="-DMULTIARCH=\\\"$MULTIARCH\\\"" -@@ -6807,6 +7000,12 @@ +@@ -6807,6 +7002,12 @@ PY_SUPPORT_TIER=3 ;; #( x86_64-*-freebsd*/clang) : PY_SUPPORT_TIER=3 ;; #( @@ -2438,7 +3921,7 @@ index c87f518382..ad0cd39350 100755 *) : PY_SUPPORT_TIER=0 ;; -@@ -7257,17 +7456,23 @@ +@@ -7257,17 +7458,23 @@ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking LDLIBRARY" >&5 printf %s "checking LDLIBRARY... " >&6; } @@ -2466,7 +3949,7 @@ index c87f518382..ad0cd39350 100755 else BLDLIBRARY='$(LDLIBRARY)' fi -@@ -7317,12 +7522,16 @@ +@@ -7317,12 +7524,16 @@ ;; Darwin*) LDLIBRARY='libpython$(LDVERSION).dylib' @@ -2487,7 +3970,7 @@ index c87f518382..ad0cd39350 100755 ;; esac -@@ -12515,6 +12724,7 @@ +@@ -12515,6 +12726,7 @@ esac ;; CYGWIN*) SHLIB_SUFFIX=.dll;; @@ -2495,7 +3978,7 @@ index c87f518382..ad0cd39350 100755 *) SHLIB_SUFFIX=.so;; esac fi -@@ -12597,6 +12807,11 @@ +@@ -12597,6 +12809,11 @@ BLDSHARED="$LDSHARED" fi ;; @@ -2507,7 +3990,7 @@ index c87f518382..ad0cd39350 100755 Emscripten|WASI) LDSHARED='$(CC) -shared' LDCXXSHARED='$(CXX) -shared';; -@@ -12750,6 +12965,24 @@ +@@ -12750,6 +12967,24 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED";; @@ -2532,7 +4015,7 @@ index c87f518382..ad0cd39350 100755 OpenUNIX*|UnixWare*) LINKFORSHARED="-Wl,-Bexport";; SCO_SV*) LINKFORSHARED="-Wl,-Bexport";; ReliantUNIX*) LINKFORSHARED="-W1 -Blargedynsym";; -@@ -14138,6 +14371,10 @@ +@@ -14138,6 +14373,10 @@ ctypes_malloc_closure=yes ;; #( @@ -2543,7 +4026,7 @@ index c87f518382..ad0cd39350 100755 sunos5) : as_fn_append LIBFFI_LIBS " -mimpure-text" ;; #( -@@ -23651,7 +23888,7 @@ +@@ -23651,7 +23890,7 @@ printf "%s\n" "$ABIFLAGS" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking SOABI" >&5 printf %s "checking SOABI... " >&6; } @@ -2552,7 +4035,7 @@ index c87f518382..ad0cd39350 100755 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $SOABI" >&5 printf "%s\n" "$SOABI" >&6; } -@@ -23660,7 +23897,7 @@ +@@ -23660,7 +23899,7 @@ if test "$Py_DEBUG" = 'true'; then # Similar to SOABI but remove "d" flag from ABIFLAGS @@ -2561,7 +4044,7 @@ index c87f518382..ad0cd39350 100755 printf "%s\n" "#define ALT_SOABI \"${ALT_SOABI}\"" >>confdefs.h -@@ -27949,6 +28186,28 @@ +@@ -27949,6 +28188,28 @@ ;; #( Darwin) : ;; #( @@ -2590,11 +4073,12 @@ index c87f518382..ad0cd39350 100755 CYGWIN*) : -@@ -31528,10 +31787,13 @@ +@@ -31528,10 +31789,14 @@ do case $ac_config_target in "pyconfig.h") CONFIG_HEADERS="$CONFIG_HEADERS pyconfig.h" ;; + "iOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES iOS/Resources/Info.plist" ;; ++ "Tools/iOSTestbed/iOSTestbed/iOSTestbed-Info.plist") CONFIG_FILES="$CONFIG_FILES Tools/iOSTestbed/iOSTestbed/iOSTestbed-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" ;; "Mac/Makefile") CONFIG_FILES="$CONFIG_FILES Mac/Makefile" ;; @@ -2606,7 +4090,7 @@ index c87f518382..ad0cd39350 100755 "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 cd69f0ede5..9306e1270b 100644 +index cd69f0ede5..0a3321d9f5 100644 --- a/configure.ac +++ b/configure.ac @@ -310,6 +310,83 @@ @@ -2709,7 +4193,7 @@ index cd69f0ede5..9306e1270b 100644 if test "x${prefix}" = "xNONE"; then FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}" else -@@ -444,66 +525,112 @@ +@@ -444,66 +525,113 @@ PYTHONFRAMEWORKINSTALLDIR=$PYTHONFRAMEWORKPREFIX/$PYTHONFRAMEWORKDIR FRAMEWORKINSTALLFIRST="frameworkinstallstructure" FRAMEWORKALTINSTALLFIRST="frameworkinstallstructure " @@ -2743,6 +4227,7 @@ index cd69f0ede5..9306e1270b 100644 - fi - ;; + AC_CONFIG_FILES([iOS/Resources/Info.plist]) ++ AC_CONFIG_FILES([Tools/iOSTestbed/iOSTestbed/iOSTestbed-Info.plist]) + ;; + tvOS) : + FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" @@ -2869,7 +4354,7 @@ index cd69f0ede5..9306e1270b 100644 FRAMEWORKINSTALLFIRST= FRAMEWORKINSTALLLAST= FRAMEWORKALTINSTALLFIRST= -@@ -522,6 +649,8 @@ +@@ -522,6 +650,8 @@ AC_SUBST([PYTHONFRAMEWORKDIR]) AC_SUBST([PYTHONFRAMEWORKPREFIX]) AC_SUBST([PYTHONFRAMEWORKINSTALLDIR]) @@ -2878,7 +4363,7 @@ index cd69f0ede5..9306e1270b 100644 AC_SUBST([FRAMEWORKINSTALLFIRST]) AC_SUBST([FRAMEWORKINSTALLLAST]) AC_SUBST([FRAMEWORKALTINSTALLFIRST]) -@@ -529,105 +658,113 @@ +@@ -529,105 +659,113 @@ AC_SUBST([FRAMEWORKPYTHONW]) AC_SUBST([FRAMEWORKUNIXTOOLSPREFIX]) AC_SUBST([FRAMEWORKINSTALLAPPSPREFIX]) @@ -3066,7 +4551,7 @@ index cd69f0ede5..9306e1270b 100644 fi # Some systems cannot stand _XOPEN_SOURCE being defined at all; they -@@ -693,6 +830,13 @@ +@@ -693,6 +831,13 @@ define_xopen_source=no;; Darwin/@<:@[12]@:>@@<:@0-9@:>@.*) define_xopen_source=no;; @@ -3080,7 +4565,7 @@ index cd69f0ede5..9306e1270b 100644 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -783,6 +927,26 @@ +@@ -783,6 +928,26 @@ ], ) @@ -3107,7 +4592,7 @@ index cd69f0ede5..9306e1270b 100644 if test "$ac_sys_system" = "Darwin" then dnl look for SDKROOT -@@ -941,11 +1105,13 @@ +@@ -941,11 +1106,13 @@ AC_MSG_CHECKING([for multiarch]) AS_CASE([$ac_sys_system], [Darwin*], [MULTIARCH=""], @@ -3122,7 +4607,7 @@ index cd69f0ede5..9306e1270b 100644 if test x$PLATFORM_TRIPLET != x && test x$MULTIARCH != x; then if test x$PLATFORM_TRIPLET != x$MULTIARCH; then -@@ -955,6 +1121,12 @@ +@@ -955,6 +1122,12 @@ MULTIARCH=$PLATFORM_TRIPLET fi AC_SUBST([PLATFORM_TRIPLET]) @@ -3135,7 +4620,7 @@ index cd69f0ede5..9306e1270b 100644 if test x$MULTIARCH != x; then MULTIARCH_CPPFLAGS="-DMULTIARCH=\\\"$MULTIARCH\\\"" -@@ -985,6 +1157,9 @@ +@@ -985,6 +1158,9 @@ [wasm32-unknown-emscripten/clang], [PY_SUPPORT_TIER=3], dnl WebAssembly Emscripten [wasm32-unknown-wasi/clang], [PY_SUPPORT_TIER=3], dnl WebAssembly System Interface [x86_64-*-freebsd*/clang], [PY_SUPPORT_TIER=3], dnl FreeBSD on AMD64 @@ -3145,7 +4630,7 @@ index cd69f0ede5..9306e1270b 100644 [PY_SUPPORT_TIER=0] ) -@@ -1298,17 +1473,23 @@ +@@ -1298,17 +1474,23 @@ AC_MSG_CHECKING([LDLIBRARY]) @@ -3173,7 +4658,7 @@ index cd69f0ede5..9306e1270b 100644 else BLDLIBRARY='$(LDLIBRARY)' fi -@@ -1357,12 +1538,16 @@ +@@ -1357,12 +1539,16 @@ ;; Darwin*) LDLIBRARY='libpython$(LDVERSION).dylib' @@ -3194,7 +4679,7 @@ index cd69f0ede5..9306e1270b 100644 ;; esac -@@ -3085,6 +3270,7 @@ +@@ -3085,6 +3271,7 @@ esac ;; CYGWIN*) SHLIB_SUFFIX=.dll;; @@ -3202,7 +4687,7 @@ index cd69f0ede5..9306e1270b 100644 *) SHLIB_SUFFIX=.so;; esac fi -@@ -3165,6 +3351,11 @@ +@@ -3165,6 +3352,11 @@ BLDSHARED="$LDSHARED" fi ;; @@ -3214,7 +4699,7 @@ index cd69f0ede5..9306e1270b 100644 Emscripten|WASI) LDSHARED='$(CC) -shared' LDCXXSHARED='$(CXX) -shared';; -@@ -3309,6 +3500,24 @@ +@@ -3309,6 +3501,24 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED";; @@ -3239,7 +4724,7 @@ index cd69f0ede5..9306e1270b 100644 OpenUNIX*|UnixWare*) LINKFORSHARED="-Wl,-Bexport";; SCO_SV*) LINKFORSHARED="-Wl,-Bexport";; ReliantUNIX*) LINKFORSHARED="-W1 -Blargedynsym";; -@@ -3682,6 +3891,9 @@ +@@ -3682,6 +3892,9 @@ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], @@ -3249,7 +4734,7 @@ index cd69f0ede5..9306e1270b 100644 [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] ) AS_VAR_IF([ctypes_malloc_closure], [yes], [ -@@ -5714,7 +5926,7 @@ +@@ -5714,7 +5927,7 @@ AC_MSG_CHECKING([ABIFLAGS]) AC_MSG_RESULT([$ABIFLAGS]) AC_MSG_CHECKING([SOABI]) @@ -3258,7 +4743,7 @@ index cd69f0ede5..9306e1270b 100644 AC_MSG_RESULT([$SOABI]) # Release build, debug build (Py_DEBUG), and trace refs build (Py_TRACE_REFS) -@@ -5722,7 +5934,7 @@ +@@ -5722,7 +5935,7 @@ if test "$Py_DEBUG" = 'true'; then # Similar to SOABI but remove "d" flag from ABIFLAGS AC_SUBST([ALT_SOABI]) @@ -3267,7 +4752,7 @@ index cd69f0ede5..9306e1270b 100644 AC_DEFINE_UNQUOTED([ALT_SOABI], ["${ALT_SOABI}"], [Alternative SOABI used in debug build to load C extensions built in release mode]) fi -@@ -7068,6 +7280,29 @@ +@@ -7068,6 +7281,29 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], @@ -3299,7 +4784,7 @@ index cd69f0ede5..9306e1270b 100644 [FreeBSD*], [PY_STDLIB_MOD_SET_NA([_scproxy])], --- /dev/null +++ b/iOS/README.rst -@@ -0,0 +1,107 @@ +@@ -0,0 +1,354 @@ +==================== +Python on iOS README +==================== @@ -3310,12 +4795,19 @@ index cd69f0ede5..9306e1270b 100644 +This document provides a quick overview of some iOS specific features in the +Python distribution. + ++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 on iOS, tools such as `BeeWare's ++Briefcase `__ and `Kivy's Builddozer ++`__ will provide a much more approachable user ++experience. ++ +Compilers for building on iOS +============================= + +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, on the -+most recently released macOS. ++recommended that you use the most recent stable release of Xcode, on the most ++recently released macOS. + +iOS specific arguments to configure +=================================== @@ -3329,9 +4821,8 @@ index cd69f0ede5..9306e1270b 100644 + + Specify the name for the python framework, defaults to ``Python``. + -+ -+Building and using Python on iOS -+================================ ++Building Python on iOS ++====================== + +ABIs and Architectures +---------------------- @@ -3339,46 +4830,79 @@ index cd69f0ede5..9306e1270b 100644 +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. ++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 support ed architecture on physical devices - ARM64. ++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.) ++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. ++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. + +How do I build Python for iOS? +------------------------------ + -+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 iOS -+project, you need to: ++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. + -+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. ++If you want to use Python in a real iOS 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. This can be done using the ``lipo`` tool, provide by Xcode: ++ ++ $ lipo -create -output module.dylib path/to/x86_64/module.dylib path/to/arm64/module.dylib ++ ++3. Merge the headers for each architecture. The header files will be identical on each platform, ++ except for ``pyconfig.h``. Copy all the headers from one platform (say, arm64), ++ rename ``pyconfig.h`` to ``pyconfig-arm64.h``, and copy the ``pyconfig.h`` for ++ the other architecture into the merged header folder as ``pyconfig-x86_64.h``. ++ Then copy the ``iOS/Resources/pyconfig.h`` file into the merged headers folder. ++ This will allow the two Python architectures to share header files. ++4. Merge the "fat" frameworks for each ABI into a single XCframework. + +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:: + -+The build also requires the use of cross-compilation. The commands for building -+Python for iOS will look somethign like:: -+ ++ $ export PATH=`pwd`/iOS/Resources/bin:$PATH + $ ./configure \ ++ AR=arm64-apple-ios-simulator-ar \ ++ CC=arm64-apple-ios-simulator-clang \ ++ CPP=arm64-apple-ios-simulator-cpp \ ++ CXX=arm64-apple-ios-simulator-clang \ + --enable-framework=/path/to/install \ -+ --host=aarch64-apple-ios \ ++ --host=aarch64-apple-ios-simulator \ + --build=aarch64-apple-darwin \ -+ --with-build-python=/path/to/python.exe ++ --with-build-python=/path/to/python.exe \ ++ ac_cv_file__dev_ptmx=no \ ++ ac_cv_file__dev_ptc=no + $ make + $ make install + +In this invocation: + ++* ``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; howver, ``xcrun`` embeds user- and ++ version-specific paths into the sysconfig data, which limits the portability ++ of the compiled Python. It also 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. ++ +* ``/path/to/install`` is the location where the final Python.framework will be + output. + @@ -3405,8 +4929,216 @@ index cd69f0ede5..9306e1270b 100644 + 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 iOS -+===================================== ++In practice, you will likely also need to specify the paths to iOS builds of the ++binary libraries that CPython depends on (XZ, BZip2, LibFFI and OpenSSL). ++ ++How do I test Python on iOS? ++---------------------------- ++ ++The ``Tools/iOSTestbed`` folder that contains an Xcode project that is able to run ++the iOS test suite. 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=aarch64-apple-ios-simulator`` or ``--host=x86_64-apple-ios-simulator`` ++), setting the framework location to the testbed project:: ++ ++ --enable-framework="./Tools/iOSTestbed/Python.xcframework/ios-arm64_x86_64-simulator" ++ ++Then run ``make all install testiOS``. This will build an iOS framework for your ++chosen architecture, install the Python iOS framework into the testbed project, ++and run the test suite on an "iPhone SE (3rd generation)" simulator. ++ ++While the test suite is running, Xcode does not display any console output. ++After showing some Xcode build commands, the console output will print ``Testing ++started``, and then appear to stop. It will remain in this state until the test ++suite completes. On a 2022 M1 MacBook Pro, the test suite takes approximately 12 ++minutes to run; a couple of extra minutes is required to boot and prepare the ++iOS simulator. ++ ++On success, the test suite will exit and report successful completion of the ++test suite. No output of the Python test suite will be displayed. ++ ++On failure, the output of the Python test suite *will* be displayed. This will ++show the details of the tests that failed. ++ ++How do I debug test failures? ++----------------------------- ++ ++The easiest way to diagnose a single test failure is to open the testbed project ++in Xcode and run the tests from there using the "Product > Test" menu item. ++ ++Running specific tests ++^^^^^^^^^^^^^^^^^^^^^^ ++ ++As the test suite is being executed on an iOS simulator, it is not possible to ++pass in command line arguments to configure test suite operation. To work around ++this limitation, the arguments that would normally be passed as command line ++arguments are configured as a static string at the start of the XCTest method ++``- (void)testPython`` in ``iOSTestbedTests.m``. To pass an argument to the test ++suite, add a a string to the ``argv`` defintion. These arguments will be passed ++to the test suite as if they had been passed to ``python -m test`` at the ++command line. ++ ++Disabling automated Breakpoints ++^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++ ++By default, Xcode will inserts an automatic breakpoint whenever a signal is ++raised. The Python test suite raises many of these signals as part of normal ++operation; unless you are trying to diagnose an issue with signals, the ++automatic breakpoints can be inconvenient. However, they can be disabled by ++creating a symbolic breakpoint that is triggered at the start of the test run. ++ ++Select "Debug > Breakpoints > Create Symbolic Breakpoint" from the Xcode menu, and ++populate the new brewpoint with the following details: ++ ++* **Name**: IgnoreSignals ++* **Symbol**: UIApplicationMain ++* **Action**: Add debugger commands for: ++ - ``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`` ++* Check the "Automatically continue after evaluating" box. ++ ++All other details can be left blank. When the process executes the ++``UIApplicationMain`` entry point, the breakpoint will trigger, run the debugger ++commands to disable the automatic breakpoints, and automatically resume. ++ ++Using Python on iOS ++=================== ++ ++To add Python to an iOS Xcode project: ++ ++1. Build Python for each architecture that you want to support. At a minimum, ++ you will need a build for `arm64-apple-ios`, plus one of either ++ `arm64-apple-ios-simulator` or `x86_64-apple-ios-simulator`. This will ++ produce a ``Python.framework``, plus a ``bin`` and ``lib`` folder in the same ++ directory as the ``Python.framework``. ++ ++2. Create an XCframework from the individual single-platform frameworks. The ++ basic structure can be compiled from the individual ``Python.framework`` ++ outputs:: ++ ++ xcodebuild -create-xcframework -output Python.xcframework -framework path/to/iphoneos/Python.framework -framework path/to/iphonesimulator/Python.framework ++ ++ Then, copy the ``bin`` and ``lib`` folders into the architecture-specific slices of ++ the XCframework:: ++ ++ cp path/to/iphoneos/bin Python.xcframework/ios-arm64 ++ cp path/to/iphoneos/lib Python.xcframework/ios-arm64 ++ ++ cp path/to/iphonesimulator/bin Python.xcframework/ios-arm64-simulator ++ cp path/to/iphonesimulator/lib Python.xcframework/ios-arm64-simulator ++ ++ Note that the name of the architecture-specific slice for the simulator will ++ depend on the CPU architecture that you build. ++ ++3. Add symbolic links to "common" platform names for each slice:: ++ ++ ln -si ios-arm64 Python.xcframework/iphoneos ++ ln -si ios-arm64-simulator Python.xcframework/iphonesimulator ++ ++4. Drag the XCframework into your iOS project. In the following instructions, ++ we'll assume you've dropped the XCframework into the root of your project; ++ however, you can use any other location that you want. ++ ++5. Drag the ``iOS/Resources/dylib-Info-template.plist`` file into your project, ++ and ensure it is associated with the app target. ++ ++6. Select the app target by selecting the root node of your Xcode project, then ++ the target name in the sidebar that appears. ++ ++7. In the "General" settings, under "Frameworks, Libraries and Embedded ++ Content", Add ``Python.xcframework``, with "Embed & Sign" selected. ++ ++8. In the "Build Settings" tab, modify the following: ++ ++ - Build Options ++ * User script sandboxing: No ++ - Search Paths ++ * Framework Search Paths: ``$(PROJECT_DIR)`` ++ * Header Search Paths: ``"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers"`` ++ - Apple Clang - Warnings - All languages ++ * Quoted Include in Framework Header: No ++ ++9. In the "Build Phases" tab, add a new "Run Script" build step *before* the ++ "Embed Frameworks" step. Name the step "Install Target Specific Python ++ Standard Library", disable the "Based on dependency analysis" checkbox, and ++ set the script content to:: ++ ++ 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/iphonesimulator/lib/" "$CODESIGNING_FOLDER_PATH/python/lib/" ++ else ++ echo "Installing Python modules for iOS Device" ++ rsync -au --delete "$PROJECT_DIR/Python.xcframework/iphoneos/lib/" "$CODESIGNING_FOLDER_PATH/python/lib/" ++ fi ++ ++10. Add a second "Run Script" build step *directly after* the step you just ++ added, named "Prepare Python Binary Modules". It should also have "Based on ++ dependency analysis" unchecked, with the following script content:: ++ ++ set -e ++ ++ install_dylib () { ++ INSTALL_BASE=$1 ++ FULL_DYLIB=$2 ++ ++ # The name of the .dylib file ++ DYLIB=$(basename "$FULL_DYLIB") ++ # The name of the .dylib file, relative to the install base ++ RELATIVE_DYLIB=${FULL_DYLIB#$CODESIGNING_FOLDER_PATH/$INSTALL_BASE/} ++ # The full dotted name of the binary module, constructed from the file path. ++ FULL_MODULE_NAME=$(echo $RELATIVE_DYLIB | 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_DYLIB" ++ mkdir -p "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER" ++ ++ cp "$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist" ++ defaults write "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist" CFBundleExecutable -string "$DYLIB" ++ defaults write "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist" CFBundleIdentifier -string "$FRAMEWORK_BUNDLE_ID" ++ fi ++ ++ echo "Installing binary for $RELATIVE_DYLIB" ++ mv "$FULL_DYLIB" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER" ++ } ++ ++ PYTHON_VER=$(ls "$CODESIGNING_FOLDER_PATH/python/lib") ++ echo "Install Python $PYTHON_VER standard library dylibs..." ++ find "$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload" -name "*.dylib" | while read FULL_DYLIB; do ++ install_dylib python/lib/$PYTHON_VER/lib-dynload "$FULL_DYLIB" ++ 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 "{}" \; ++ ++11. Add Objective C code to initialize and use a Python interpreter in embedded ++ mode. When configuring the interpreter, you can use: ++ ++ [NSString stringWithFormat:@"%@/python", [[NSBundle mainBundle] resourcePath], nil] ++ ++ as the value of ``PYTHONHOME``; the standard library will be installed as the ++ ``lib/python3.X`` subfolder of that ``PYTHONHOME``. ++ ++If you have third-party binary modules in your app, they will need to be: ++ ++* Compiled for both on-device and simulator platforms; ++* Copied into your project as part of the script in step 9; ++* Installed and signed as part of the script in step 10. --- /dev/null +++ b/iOS/Resources/Info.plist.in @@ -0,0 +1,34 @@ @@ -3435,7 +5167,7 @@ index cd69f0ede5..9306e1270b 100644 + CFBundleSignature + ???? + CFBundleVersion -+ %VERSION% ++ 1 + CFBundleSupportedPlatforms + + iPhoneOS @@ -3490,6 +5222,45 @@ index cd69f0ede5..9306e1270b 100644 +#!/bin/bash +xcrun --sdk iphonesimulator clang -target x86_64-apple-ios-simulator -E $@ --- /dev/null ++++ b/iOS/Resources/dylib-Info-template.plist +@@ -0,0 +1,26 @@ ++ ++ ++ ++ ++ CFBundleDevelopmentRegion ++ en ++ CFBundleExecutable ++ ++ CFBundleIdentifier ++ ++ CFBundleInfoDictionaryVersion ++ 6.0 ++ CFBundlePackageType ++ APPL ++ CFBundleShortVersionString ++ 1.0 ++ CFBundleSupportedPlatforms ++ ++ iPhoneOS ++ ++ MinimumOSVersion ++ 12.0 ++ CFBundleVersion ++ 1 ++ ++ +--- /dev/null ++++ b/iOS/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/tvOS/README.rst @@ -0,0 +1,108 @@ +===================== @@ -3683,6 +5454,45 @@ index cd69f0ede5..9306e1270b 100644 +#!/bin/bash +xcrun --sdk appletvsimulator clang -target x86_64-apple-tvos-simulator -E $@ --- /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 ++ ++ +--- /dev/null ++++ b/tvOS/Resources/pyconfig.h +@@ -0,0 +1,7 @@ ++#ifdef __arm64__ ++#include "pyconfig-arm64.h" ++#endif ++ ++#ifdef __x86_64__ ++#include "pyconfig-x86_64.h" ++#endif +--- /dev/null +++ b/watchOS/README.rst @@ -0,0 +1,108 @@ +======================== @@ -3875,3 +5685,46 @@ index cd69f0ede5..9306e1270b 100644 @@ -0,0 +1,2 @@ +#!/bin/bash +xcrun --sdk watchsimulator 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 ++ ++ watchOS ++ ++ MinimumOSVersion ++ 4.0 ++ CFBundleVersion ++ 1 ++ ++ +--- /dev/null ++++ b/watchOS/Resources/pyconfig.h +@@ -0,0 +1,11 @@ ++#ifdef __arm64__ ++# ifdef __LP64__ ++#include "pyconfig-arm64.h" ++# else ++#include "pyconfig-arm64_32.h" ++# endif ++#endif ++ ++#ifdef __x86_64__ ++#include "pyconfig-x86_64.h" ++#endif diff --git a/patch/Python/pyconfig-iOS.h b/patch/Python/pyconfig-iOS.h deleted file mode 100644 index 4acff2c6..00000000 --- a/patch/Python/pyconfig-iOS.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifdef __arm64__ -#include "pyconfig-arm64.h" -#endif - -#ifdef __x86_64__ -#include "pyconfig-x86_64.h" -#endif diff --git a/patch/Python/pyconfig-tvOS.h b/patch/Python/pyconfig-tvOS.h deleted file mode 100644 index d4afe05b..00000000 --- a/patch/Python/pyconfig-tvOS.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifdef __arm64__ -#include "pyconfig-arm64.h" -#endif - -#ifdef __x86_64__ -#include "pyconfig-x86_64.h" -#endif \ No newline at end of file diff --git a/patch/Python/pyconfig-watchOS.h b/patch/Python/pyconfig-watchOS.h deleted file mode 100644 index f842b987..00000000 --- a/patch/Python/pyconfig-watchOS.h +++ /dev/null @@ -1,11 +0,0 @@ -#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/patch/Python/test.exclude b/patch/Python/test.exclude deleted file mode 100644 index add994a7..00000000 --- a/patch/Python/test.exclude +++ /dev/null @@ -1,7 +0,0 @@ -# This is a list of Python standard library path patterns -# we exclude from the embedded device Python-Apple-support test tarballs. -# It is used by `tar -X` during the Makefile build. -# -# Remove pyc files. These take up space, but since most stdlib modules are -# never imported by user code, they mostly have no value. -*/__pycache__ \ No newline at end of file From ee70f59279028e6bbbeb02a39fc5811faf6060a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 10 Dec 2023 20:05:42 +0000 Subject: [PATCH 017/113] Bump actions/setup-python from 4.7.1 to 5.0.0 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4.7.1 to 5.0.0. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v4.7.1...v5.0.0) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yaml | 2 +- .github/workflows/release.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 6cb84f82..b9eeaa52 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python environment - uses: actions/setup-python@v4.7.1 + uses: actions/setup-python@v5.0.0 with: python-version: "3.X" diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 34448867..b767ced1 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -43,7 +43,7 @@ jobs: echo "BUILD_NUMBER=${BUILD_NUMBER}" >> ${GITHUB_OUTPUT} - name: Set up Python - uses: actions/setup-python@v4.7.1 + uses: actions/setup-python@v5.0.0 with: python-version: "${{ steps.build-vars.outputs.PYTHON_VER }}-dev" From a49a33f9ef146d4f26f3a78b9a84aac8bb190d28 Mon Sep 17 00:00:00 2001 From: Russell Martin Date: Wed, 3 Jan 2024 18:03:11 -0500 Subject: [PATCH 018/113] Upgrade `upload-artifact` and `download-artifact` actions to v4 --- .github/workflows/release.yaml | 50 +++++++++++++++------------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index b767ced1..7ba40a90 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -9,6 +9,7 @@ on: jobs: build: + name: Build runs-on: macOS-latest outputs: TAG: ${{ steps.build-vars.outputs.TAG }} @@ -21,26 +22,23 @@ jobs: OPENSSL_VERSION: ${{ steps.version-details.outputs.OPENSSL_VERSION }} strategy: matrix: - target: ['macOS', 'iOS', 'tvOS', 'watchOS'] + target: [ "macOS", "iOS", "tvOS", "watchOS" ] steps: - - uses: actions/checkout@v4 + - name: Checkout + uses: actions/checkout@v4.1.1 - - name: Set build variables + - name: Set Build Variables id: build-vars env: TAG_NAME: ${{ github.ref }} run: | export TAG=$(basename $TAG_NAME) - echo "TAG=${TAG}" export PYTHON_VER="${TAG%-*}" export BUILD_NUMBER="${TAG#*-}" - echo "PYTHON_VER=${PYTHON_VER}" - echo "BUILD_NUMBER=${BUILD_NUMBER}" - - echo "TAG=${TAG}" >> ${GITHUB_OUTPUT} - echo "PYTHON_VER=${PYTHON_VER}" >> ${GITHUB_OUTPUT} - echo "BUILD_NUMBER=${BUILD_NUMBER}" >> ${GITHUB_OUTPUT} + echo "TAG=${TAG}" | tee -a ${GITHUB_OUTPUT} + echo "PYTHON_VER=${PYTHON_VER}" | tee -a ${GITHUB_OUTPUT} + echo "BUILD_NUMBER=${BUILD_NUMBER}" | tee -a ${GITHUB_OUTPUT} - name: Set up Python uses: actions/setup-python@v5.0.0 @@ -52,7 +50,7 @@ jobs: # Do the build for the requested target. make ${{ matrix.target }} BUILD_NUMBER=${{ steps.build-vars.outputs.BUILD_NUMBER }} - - name: Extract version details + - name: Extract Version Details id: version-details run: | export PYTHON_VERSION=$(grep "Python version:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 3) @@ -61,34 +59,30 @@ jobs: export OPENSSL_VERSION=$(grep "OpenSSL:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) export LIBFFI_VERSION=$(grep "libFFI:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) - echo "PYTHON_VERSION=${PYTHON_VERSION}" - echo "BZIP2_VERSION=${BZIP2_VERSION}" - echo "XZ_VERSION=${XZ_VERSION}" - echo "OPENSSL_VERSION=${OPENSSL_VERSION}" - echo "LIBFFI_VERSION=${LIBFFI_VERSION}" - - echo "PYTHON_VERSION=${PYTHON_VERSION}" >> ${GITHUB_OUTPUT} - echo "BZIP2_VERSION=${BZIP2_VERSION}" >> ${GITHUB_OUTPUT} - echo "XZ_VERSION=${XZ_VERSION}" >> ${GITHUB_OUTPUT} - echo "OPENSSL_VERSION=${OPENSSL_VERSION}" >> ${GITHUB_OUTPUT} - echo "LIBFFI_VERSION=${LIBFFI_VERSION}" >> ${GITHUB_OUTPUT} + echo "PYTHON_VERSION=${PYTHON_VERSION}" | tee -a ${GITHUB_OUTPUT} + echo "BZIP2_VERSION=${BZIP2_VERSION}" | tee -a ${GITHUB_OUTPUT} + echo "XZ_VERSION=${XZ_VERSION}" | tee -a ${GITHUB_OUTPUT} + echo "OPENSSL_VERSION=${OPENSSL_VERSION}" | tee -a ${GITHUB_OUTPUT} + echo "LIBFFI_VERSION=${LIBFFI_VERSION}" | tee -a ${GITHUB_OUTPUT} - - name: Upload build artifact - uses: actions/upload-artifact@v3.1.3 + - name: Upload Build Artifact + uses: actions/upload-artifact@v4.0.0 with: - name: dist - path: "dist" + name: dist-${{ matrix.target }} + path: dist if-no-files-found: error make-release: + name: Make Release runs-on: ubuntu-latest needs: build steps: - name: Get build artifacts - uses: actions/download-artifact@v3.0.2 + uses: actions/download-artifact@v4.1.0 with: - name: dist + pattern: dist-* path: dist + merge-multiple: true - name: Create Release uses: ncipollo/release-action@v1.13.0 From 616ef71b281f2dd1554243bd11cd151f21f775f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Jan 2024 02:16:30 +0000 Subject: [PATCH 019/113] Bump actions/download-artifact from 4.1.0 to 4.1.1 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.1.0 to 4.1.1. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4.1.0...v4.1.1) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 7ba40a90..0f2f537c 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -78,7 +78,7 @@ jobs: needs: build steps: - name: Get build artifacts - uses: actions/download-artifact@v4.1.0 + uses: actions/download-artifact@v4.1.1 with: pattern: dist-* path: dist From 13b0ef2fe1336e07cd837a11118e359b79b71688 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 14 Jan 2024 20:07:08 +0000 Subject: [PATCH 020/113] Bump actions/upload-artifact from 4.0.0 to 4.1.0 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.0.0 to 4.1.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.0.0...v4.1.0) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 0f2f537c..c124787c 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -66,7 +66,7 @@ jobs: echo "LIBFFI_VERSION=${LIBFFI_VERSION}" | tee -a ${GITHUB_OUTPUT} - name: Upload Build Artifact - uses: actions/upload-artifact@v4.0.0 + uses: actions/upload-artifact@v4.1.0 with: name: dist-${{ matrix.target }} path: dist From 7021216ec8950d9731e1565fdc3fc351044b63aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Jan 2024 20:50:27 +0000 Subject: [PATCH 021/113] Bump actions/upload-artifact from 4.1.0 to 4.2.0 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.1.0 to 4.2.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.1.0...v4.2.0) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c124787c..81ac22d8 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -66,7 +66,7 @@ jobs: echo "LIBFFI_VERSION=${LIBFFI_VERSION}" | tee -a ${GITHUB_OUTPUT} - name: Upload Build Artifact - uses: actions/upload-artifact@v4.1.0 + uses: actions/upload-artifact@v4.2.0 with: name: dist-${{ matrix.target }} path: dist From eebe6e36783770abed943fc6b9075e19c326a52b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 28 Jan 2024 20:49:47 +0000 Subject: [PATCH 022/113] Bump actions/upload-artifact from 4.2.0 to 4.3.0 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.2.0 to 4.3.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.2.0...v4.3.0) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 81ac22d8..91563fa8 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -66,7 +66,7 @@ jobs: echo "LIBFFI_VERSION=${LIBFFI_VERSION}" | tee -a ${GITHUB_OUTPUT} - name: Upload Build Artifact - uses: actions/upload-artifact@v4.2.0 + uses: actions/upload-artifact@v4.3.0 with: name: dist-${{ matrix.target }} path: dist From 0200ce50e56febe3c08c830799973fa1e5a835e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 11 Feb 2024 20:30:26 +0000 Subject: [PATCH 023/113] Bump actions/download-artifact from 4.1.1 to 4.1.2 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.1.1 to 4.1.2. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4.1.1...v4.1.2) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 91563fa8..e659a6f6 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -78,7 +78,7 @@ jobs: needs: build steps: - name: Get build artifacts - uses: actions/download-artifact@v4.1.1 + uses: actions/download-artifact@v4.1.2 with: pattern: dist-* path: dist From 7cd7971421c2f88099da1a7a7574a1c8a7180cf2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 11 Feb 2024 20:30:29 +0000 Subject: [PATCH 024/113] Bump actions/upload-artifact from 4.3.0 to 4.3.1 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.0 to 4.3.1. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.3.0...v4.3.1) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 91563fa8..00b96c5d 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -66,7 +66,7 @@ jobs: echo "LIBFFI_VERSION=${LIBFFI_VERSION}" | tee -a ${GITHUB_OUTPUT} - name: Upload Build Artifact - uses: actions/upload-artifact@v4.3.0 + uses: actions/upload-artifact@v4.3.1 with: name: dist-${{ matrix.target }} path: dist From 09f628dcc202cdc1002d8378d25f439d9bb046de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 11 Feb 2024 20:30:31 +0000 Subject: [PATCH 025/113] Bump ncipollo/release-action from 1.13.0 to 1.14.0 Bumps [ncipollo/release-action](https://github.com/ncipollo/release-action) from 1.13.0 to 1.14.0. - [Release notes](https://github.com/ncipollo/release-action/releases) - [Commits](https://github.com/ncipollo/release-action/compare/v1.13.0...v1.14.0) --- updated-dependencies: - dependency-name: ncipollo/release-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 91563fa8..5b3761ba 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -85,7 +85,7 @@ jobs: merge-multiple: true - name: Create Release - uses: ncipollo/release-action@v1.13.0 + uses: ncipollo/release-action@v1.14.0 with: name: ${{ needs.build.outputs.PYTHON_VER }}-${{ needs.build.outputs.BUILD_NUMBER }} tag: ${{ needs.build.outputs.PYTHON_VER }}-${{ needs.build.outputs.BUILD_NUMBER }} From 1d482046f8ac097b266765ae925e37641ecad700 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 3 Mar 2024 20:59:35 +0000 Subject: [PATCH 026/113] Bump actions/download-artifact from 4.1.2 to 4.1.4 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.1.2 to 4.1.4. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4.1.2...v4.1.4) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 2c672f77..cf55f68d 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -78,7 +78,7 @@ jobs: needs: build steps: - name: Get build artifacts - uses: actions/download-artifact@v4.1.2 + uses: actions/download-artifact@v4.1.4 with: pattern: dist-* path: dist From 05aa1d0acb2a84cb6a83875c11a8aa4c13eca243 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 31 Mar 2024 20:19:43 +0000 Subject: [PATCH 027/113] Bump actions/setup-python from 5.0.0 to 5.1.0 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.0.0 to 5.1.0. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5.0.0...v5.1.0) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yaml | 2 +- .github/workflows/release.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index b9eeaa52..2c346d09 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python environment - uses: actions/setup-python@v5.0.0 + uses: actions/setup-python@v5.1.0 with: python-version: "3.X" diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index cf55f68d..36031d30 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -41,7 +41,7 @@ jobs: echo "BUILD_NUMBER=${BUILD_NUMBER}" | tee -a ${GITHUB_OUTPUT} - name: Set up Python - uses: actions/setup-python@v5.0.0 + uses: actions/setup-python@v5.1.0 with: python-version: "${{ steps.build-vars.outputs.PYTHON_VER }}-dev" From 85adfc862d7f56cbc4c885583ef32405a2b98f29 Mon Sep 17 00:00:00 2001 From: Tony Miller <2032667+tnymlr@users.noreply.github.com> Date: Wed, 10 Apr 2024 01:55:41 +1000 Subject: [PATCH 028/113] add disabling dead code stripping in linker Mention that embedding Python requires disabling dead code removal. --- USAGE.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/USAGE.md b/USAGE.md index 706fe058..8caa4ac2 100644 --- a/USAGE.md +++ b/USAGE.md @@ -145,6 +145,9 @@ echo "Signing as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY find "$CODESIGNING_FOLDER_PATH/Contents/Resources/python-stdlib/lib-dynload" -name "*.so" -exec /usr/bin/codesign --force --sign "$EXPANDED_CODE_SIGN_IDENTITY" -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der {} \; ``` +8. Disable dead code removal. As of March 2024 Xcode has "Dead Code Stripping" enabled by default. This _will_ remove functions from linked Python library that linker found unused. + Navigate to Build Settings -> Linkin - General. Find "Dead Code Stripping" and select "No" + You will now be able to access the Python runtime in your Python code. If you are on iOS, you will be able to deploy to an iOS simulator without specifying From e9bdc369e8dfe4d715598997a7de48cf7ef1fb41 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Apr 2024 20:50:02 +0000 Subject: [PATCH 029/113] Bump actions/upload-artifact from 4.3.1 to 4.3.2 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.1 to 4.3.2. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.3.1...v4.3.2) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 36031d30..5cda953b 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -66,7 +66,7 @@ jobs: echo "LIBFFI_VERSION=${LIBFFI_VERSION}" | tee -a ${GITHUB_OUTPUT} - name: Upload Build Artifact - uses: actions/upload-artifact@v4.3.1 + uses: actions/upload-artifact@v4.3.2 with: name: dist-${{ matrix.target }} path: dist From 1262ebd107c6874f2b72cb32ce95745ddbae5d85 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Apr 2024 20:50:05 +0000 Subject: [PATCH 030/113] Bump actions/download-artifact from 4.1.4 to 4.1.5 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.1.4 to 4.1.5. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4.1.4...v4.1.5) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 36031d30..8cc61f32 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -78,7 +78,7 @@ jobs: needs: build steps: - name: Get build artifacts - uses: actions/download-artifact@v4.1.4 + uses: actions/download-artifact@v4.1.5 with: pattern: dist-* path: dist From 995e9d7b8a3181cd679b5dc470ad5858873559e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 28 Apr 2024 20:46:21 +0000 Subject: [PATCH 031/113] Bump actions/download-artifact from 4.1.5 to 4.1.7 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.1.5 to 4.1.7. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4.1.5...v4.1.7) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 47d9c5f0..f18133e5 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -78,7 +78,7 @@ jobs: needs: build steps: - name: Get build artifacts - uses: actions/download-artifact@v4.1.5 + uses: actions/download-artifact@v4.1.7 with: pattern: dist-* path: dist From 7948bde59b89e0d4b4238f44855b7cc0d7295dff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 28 Apr 2024 20:46:23 +0000 Subject: [PATCH 032/113] Bump actions/upload-artifact from 4.3.2 to 4.3.3 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.2 to 4.3.3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.3.2...v4.3.3) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 47d9c5f0..e82f2596 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -66,7 +66,7 @@ jobs: echo "LIBFFI_VERSION=${LIBFFI_VERSION}" | tee -a ${GITHUB_OUTPUT} - name: Upload Build Artifact - uses: actions/upload-artifact@v4.3.2 + uses: actions/upload-artifact@v4.3.3 with: name: dist-${{ matrix.target }} path: dist From 580f39706a1f85ab13b840f252d68b85d56699eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 7 Jul 2024 20:05:20 +0000 Subject: [PATCH 033/113] Bump actions/upload-artifact from 4.3.3 to 4.3.4 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.3 to 4.3.4. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.3.3...v4.3.4) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 154a886a..18170534 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -66,7 +66,7 @@ jobs: echo "LIBFFI_VERSION=${LIBFFI_VERSION}" | tee -a ${GITHUB_OUTPUT} - name: Upload Build Artifact - uses: actions/upload-artifact@v4.3.3 + uses: actions/upload-artifact@v4.3.4 with: name: dist-${{ matrix.target }} path: dist From 0fadbb3a3f43520ceb4be249e74f5a90f0c2d9c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 7 Jul 2024 20:05:24 +0000 Subject: [PATCH 034/113] Bump actions/download-artifact from 4.1.7 to 4.1.8 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.1.7 to 4.1.8. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4.1.7...v4.1.8) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 154a886a..73b7d3ae 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -78,7 +78,7 @@ jobs: needs: build steps: - name: Get build artifacts - uses: actions/download-artifact@v4.1.7 + uses: actions/download-artifact@v4.1.8 with: pattern: dist-* path: dist From b3f9b1a7ff9368573eb62b4a3e1d76617cc72115 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 14 Jul 2024 20:12:50 +0000 Subject: [PATCH 035/113] Bump actions/setup-python from 5.1.0 to 5.1.1 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.1.0 to 5.1.1. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5.1.0...v5.1.1) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yaml | 2 +- .github/workflows/release.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 2c346d09..a4231bc0 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python environment - uses: actions/setup-python@v5.1.0 + uses: actions/setup-python@v5.1.1 with: python-version: "3.X" diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index f4191eab..c618d404 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -41,7 +41,7 @@ jobs: echo "BUILD_NUMBER=${BUILD_NUMBER}" | tee -a ${GITHUB_OUTPUT} - name: Set up Python - uses: actions/setup-python@v5.1.0 + uses: actions/setup-python@v5.1.1 with: python-version: "${{ steps.build-vars.outputs.PYTHON_VER }}-dev" From c53d7bd328256ed511c1fdbbc09641d042f86d95 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 26 Jul 2024 08:44:16 +0800 Subject: [PATCH 036/113] Update to final state of PEP730 patches. --- Makefile | 66 +- README.rst | 59 +- USAGE.md | 174 +- patch/Python/Python.patch | 6374 ++++++---------------------- patch/Python/release.macOS.exclude | 14 +- patch/make-macho-standalone.py | 10 - patch/make-relocatable.sh | 27 + patch/make-xcrun-alias | 10 - 8 files changed, 1436 insertions(+), 5298 deletions(-) delete mode 100644 patch/make-macho-standalone.py create mode 100755 patch/make-relocatable.sh delete mode 100755 patch/make-xcrun-alias diff --git a/Makefile b/Makefile index 59bea0aa..5d1c4b7c 100644 --- a/Makefile +++ b/Makefile @@ -15,16 +15,17 @@ BUILD_NUMBER=custom # PYTHON_VERSION is the full version number (e.g., 3.10.0b3) # 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.13.0a1 +PYTHON_VERSION=3.13.0b4 PYTHON_MICRO_VERSION=$(shell echo $(PYTHON_VERSION) | grep -Eo "\d+\.\d+\.\d+") PYTHON_VER=$(basename $(PYTHON_VERSION)) # The binary releases of dependencies, published at: # https://github.com/beeware/cpython-apple-source-deps/releases BZIP2_VERSION=1.0.8-1 -XZ_VERSION=5.4.4-1 -OPENSSL_VERSION=3.0.12-1 -LIBFFI_VERSION=3.4.4-1 +MPDECIMAL_VERSION=4.0.0-1 +OPENSSL_VERSION=3.0.14-1 +XZ_VERSION=5.4.7-1 +LIBFFI_VERSION=3.4.6-1 # Supported OS OS_LIST=macOS iOS tvOS watchOS @@ -37,17 +38,15 @@ VERSION_MIN-macOS=11.0 # iOS targets TARGETS-iOS=iphonesimulator.x86_64 iphonesimulator.arm64 iphoneos.arm64 -VERSION_MIN-iOS=12.0 +VERSION_MIN-iOS=13.0 # tvOS targets TARGETS-tvOS=appletvsimulator.x86_64 appletvsimulator.arm64 appletvos.arm64 -VERSION_MIN-tvOS=9.0 -PYTHON_CONFIGURE-tvOS=ac_cv_func_sigaltstack=no +VERSION_MIN-tvOS=12.0 # watchOS targets TARGETS-watchOS=watchsimulator.x86_64 watchsimulator.arm64 watchos.arm64_32 VERSION_MIN-watchOS=4.0 -PYTHON_CONFIGURE-watchOS=ac_cv_func_sigaltstack=no # The architecture of the machine doing the build HOST_ARCH=$(shell uname -m) @@ -82,7 +81,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) \ + git diff -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 @@ -122,8 +121,6 @@ OS_LOWER-$(target)=$(shell echo $(os) | tr '[:upper:]' '[:lower:]') SDK-$(target)=$$(basename $(target)) ARCH-$(target)=$$(subst .,,$$(suffix $(target))) -WHEEL_TAG-$(target)=py3-none-$$(shell echo $$(OS_LOWER-$(target))_$$(VERSION_MIN-$(os))_$(target) | sed "s/\./_/g") - ifneq ($(os),macOS) ifeq ($$(findstring simulator,$$(SDK-$(target))),) TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(OS_LOWER-$(target))$$(VERSION_MIN-$(os)) @@ -176,6 +173,26 @@ $$(XZ_LIB-$(target)): downloads/xz-$(XZ_VERSION)-$(target).tar.gz # Ensure the target is marked as clean. touch $$(XZ_LIB-$(target)) +########################################################################### +# Target: mpdecimal +########################################################################### + +MPDECIMAL_INSTALL-$(target)=$(PROJECT_DIR)/install/$(os)/$(target)/mpdecimal-$(MPDECIMAL_VERSION) +MPDECIMAL_LIB-$(target)=$$(MPDECIMAL_INSTALL-$(target))/lib/libmpdec.a + +downloads/mpdecimal-$(MPDECIMAL_VERSION)-$(target).tar.gz: + @echo ">>> Download mpdecimal for $(target)" + mkdir -p downloads + curl $(CURL_FLAGS) -o $$@ \ + https://github.com/beeware/cpython-apple-source-deps/releases/download/mpdecimal-$(MPDECIMAL_VERSION)/mpdecimal-$(MPDECIMAL_VERSION)-$(target).tar.gz + +$$(MPDECIMAL_LIB-$(target)): downloads/mpdecimal-$(MPDECIMAL_VERSION)-$(target).tar.gz + @echo ">>> Install mpdecimal for $(target)" + mkdir -p $$(MPDECIMAL_INSTALL-$(target)) + cd $$(MPDECIMAL_INSTALL-$(target)) && tar zxvf $(PROJECT_DIR)/downloads/mpdecimal-$(MPDECIMAL_VERSION)-$(target).tar.gz + # Ensure the target is marked as clean. + touch $$(MPDECIMAL_LIB-$(target)) + ########################################################################### # Target: OpenSSL ########################################################################### @@ -245,6 +262,7 @@ $$(PYTHON_SRCDIR-$(target))/configure: \ $$(BZIP2_LIB-$(target)) \ $$(XZ_LIB-$(target)) \ $$(OPENSSL_SSL_LIB-$(target)) \ + $$(MPDECIMAL_LIB-$(target)) \ $$(LIBFFI_LIB-$(target)) @echo ">>> Unpack and configure Python for $(target)" mkdir -p $$(PYTHON_SRCDIR-$(target)) @@ -270,6 +288,8 @@ $$(PYTHON_SRCDIR-$(target))/Makefile: \ LIBLZMA_LIBS="-L$$(XZ_INSTALL-$(target))/lib -llzma" \ BZIP2_CFLAGS="-I$$(BZIP2_INSTALL-$(target))/include" \ BZIP2_LIBS="-L$$(BZIP2_INSTALL-$(target))/lib -lbz2" \ + LIBMPDEC_CFLAGS="-I$$(MPDECIMAL_INSTALL-$(target))/include" \ + LIBMPDEC_LIBS="-L$$(MPDECIMAL_INSTALL-$(target))/lib -lmpdec" \ LIBFFI_CFLAGS="-I$$(LIBFFI_INSTALL-$(target))/include" \ LIBFFI_LIBS="-L$$(LIBFFI_INSTALL-$(target))/lib -lffi" \ --host=$$(TARGET_TRIPLE-$(target)) \ @@ -277,18 +297,15 @@ $$(PYTHON_SRCDIR-$(target))/Makefile: \ --with-build-python=$(HOST_PYTHON) \ --enable-ipv6 \ --with-openssl="$$(OPENSSL_INSTALL-$(target))" \ - --without-ensurepip \ --enable-framework="$$(PYTHON_INSTALL-$(target))" \ - ac_cv_file__dev_ptmx=no \ - ac_cv_file__dev_ptc=no \ - $$(PYTHON_CONFIGURE-$(os)) \ + --with-system-libmpdec \ 2>&1 | tee -a ../python-$(PYTHON_VERSION).config.log $$(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)" \ - make all \ + make -j8 all \ 2>&1 | tee -a ../python-$(PYTHON_VERSION).build.log $$(PYTHON_LIB-$(target)): $$(PYTHON_SRCDIR-$(target))/python.exe @@ -308,7 +325,7 @@ $$(PYTHON_SITECUSTOMIZE-$(target)): cat $(PROJECT_DIR)/patch/Python/sitecustomize.$(os).py \ | sed -e "s/{{os}}/$(os)/g" \ | sed -e "s/{{arch}}/$$(ARCH-$(target))/g" \ - | sed -e "s/{{tag}}/$$(OS_LOWER-$(target))-$$(VERSION_MIN-$(os))-$$(SDK-$(target))-$$(ARCH-$(target))/g" \ + | sed -e "s/{{tag}}/$$(OS_LOWER-$(target))-$$(VERSION_MIN-$(os))-$$(ARCH-$(target))-$$(SDK-$(target))/g" \ > $$(PYTHON_SITECUSTOMIZE-$(target)) $(target): $$(PYTHON_SITECUSTOMIZE-$(target)) $$(PYTHON_LIB-$(target)) @@ -329,6 +346,8 @@ vars-$(target): @echo "XZ_LIB-$(target): $$(XZ_LIB-$(target))" @echo "OPENSSL_INSTALL-$(target): $$(OPENSSL_INSTALL-$(target))" @echo "OPENSSL_SSL_LIB-$(target): $$(OPENSSL_SSL_LIB-$(target))" + @echo "MPDECIMAL_INSTALL-$(target): $$(MPDECIMAL_INSTALL-$(target))" + @echo "MPDECIMAL_LIB-$(target): $$(MPDECIMAL_LIB-$(target))" @echo "LIBFFI_INSTALL-$(target): $$(LIBFFI_INSTALL-$(target))" @echo "LIBFFI_LIB-$(target): $$(LIBFFI_LIB-$(target))" @echo "PYTHON_SRCDIR-$(target): $$(PYTHON_SRCDIR-$(target))" @@ -524,20 +543,14 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ tar zxf build/macOS/macosx/python-$(PYTHON_VERSION)/Python_Framework.pkgPython_Framework.pkg/PayloadPython_Framework.pkgPython_Framework.pkg/PayloadPython_Framework.pkgPython_Framework.pkg/Payload -C $$(PYTHON_FRAMEWORK-macosx) -X patch/Python/release.macOS.exclude # Rewrite the framework to make it standalone - python3 patch/make-macho-standalone.py $$(PYTHON_FRAMEWORK-macosx) \ - 2>&1 | tee $$(PYTHON_INSTALL-macosx)/python-$(PY_VERSION).make-macho-standalone.log - - # Remove the "development" versions of the libs - rm -f $$(PYTHON_INSTALL_VERSION-macosx)/lib/*.dylib - rm -f $$(PYTHON_INSTALL_VERSION-macosx)/lib/*.a - rm -rf $$(PYTHON_INSTALL_VERSION-macosx)/lib/python$(PY_VERSION)/config-* + patch/make-relocatable.sh $$(PYTHON_INSTALL_VERSION-macosx) 2>&1 > /dev/null # Re-apply the signature on the binaries. codesign -s - --preserve-metadata=identifier,entitlements,flags,runtime -f $$(PYTHON_LIB-macosx) \ 2>&1 | tee $$(PYTHON_INSTALL-macosx)/python-$(os).codesign.log - find install/macOS/macosx/python-3.13.0a1/Python.framework -name "*.dylib" -type f -exec codesign -s - --preserve-metadata=identifier,entitlements,flags,runtime -f {} \; \ + find $$(PYTHON_FRAMEWORK-macosx) -name "*.dylib" -type f -exec codesign -s - --preserve-metadata=identifier,entitlements,flags,runtime -f {} \; \ 2>&1 | tee -a $$(PYTHON_INSTALL-macosx)/python-$(os).codesign.log - find install/macOS/macosx/python-3.13.0a1/Python.framework -name "*.so" -type f -exec codesign -s - --preserve-metadata=identifier,entitlements,flags,runtime -f {} \; \ + find $$(PYTHON_FRAMEWORK-macosx) -name "*.so" -type f -exec codesign -s - --preserve-metadata=identifier,entitlements,flags,runtime -f {} \; \ 2>&1 | tee -a $$(PYTHON_INSTALL-macosx)/python-$(os).codesign.log codesign -s - --preserve-metadata=identifier,entitlements,flags,runtime -f $$(PYTHON_FRAMEWORK-macosx) \ 2>&1 | tee -a $$(PYTHON_INSTALL-macosx)/python-$(os).codesign.log @@ -588,6 +601,7 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ echo "libFFI: $(LIBFFI_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS echo "BZip2: $(BZIP2_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS echo "OpenSSL: $(OPENSSL_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS + echo "mpdecimal: $(MPDECIMAL_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS echo "XZ: $(XZ_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz: \ diff --git a/README.rst b/README.rst index ae9d2aeb..88b52ab9 100644 --- a/README.rst +++ b/README.rst @@ -8,39 +8,28 @@ into a macOS, iOS, tvOS or watchOS project. Other Python versions are available by cloning other branches of the main repository: -* `Python 3.8 `__ * `Python 3.9 `__ * `Python 3.10 `__ * `Python 3.11 `__ * `Python 3.12 `__ +* `Python 3.13 `__ -It works by downloading, patching, and building a fat binary of Python and selected -pre-requisites, and packaging them as static libraries that can be incorporated into an -XCode project. The binary modules in the Python standard library are statically -compiled, but are distributed as objects that can be dynamically loaded at runtime. +It works by downloading, patching, and building a fat binary of Python and +selected pre-requisites, and packaging them as frameworks that can be +incorporated into an XCode project. The binary modules in the Python standard +library are distributed as binaries that can be dynamically loaded at runtime. The macOS package is a re-bundling of the official macOS binary, modified so that -it is relocatable. - -The iOS, tvOS and watchOS packages are compiled by this project. They expose -*almost* all the modules in the Python standard library except for: - -* ``curses`` -* ``dbm.gnu`` -* ``grp`` -* ``multiprocessing`` -* ``nis`` (Deprecated by PEP594) -* ``ossaudiodev`` (Deprecated by PEP594) -* ``posixshmem`` -* ``posixsubprocess`` -* ``readline`` -* ``spwd`` (Deprecated by PEP594) -* ``syslog`` -* ``tkinter`` +it is relocatable, with the IDLE, Tkinter and turtle packages removed. + +The iOS, tvOS and watchOS packages compiled by this project use the official +`PEP 730 `__ code that is part of Python 3.13 +to provide iOS support; the relevant patches have been backported to 3.9-3.12. +Additional patches have been applied to add tvOS and watchOS support. The binaries support x86_64 and arm64 for macOS; arm64 for iOS and appleTV -devices; and arm64_32 for watchOS. It also supports device simulators on both -x86_64 and M1 hardware. This should enable the code to run on: +devices; and arm64_32 for watchOS devices. It also supports device simulators on +both x86_64 and M1 hardware. This should enable the code to run on: * macOS 11 (Big Sur) or later, on: * MacBook (including MacBooks using Apple Silicon) @@ -48,14 +37,14 @@ x86_64 and M1 hardware. This should enable the code to run on: * Mac Mini (including Apple Silicon Mac minis) * Mac Studio (all models) * Mac Pro (all models) -* iOS 12.0 or later, on: +* iOS 13.0 or later, on: * iPhone (6s or later) * iPad (5th gen or later) * iPad Air (all models) * iPad Mini (2 or later) * iPad Pro (all models) * iPod Touch (7th gen or later) -* tvOS 9.0 or later, on: +* tvOS 12.0 or later, on: * Apple TV (4th gen or later) * watchOS 4.0 or later, on: * Apple Watch (4th gen or later) @@ -121,18 +110,11 @@ see the `usage guide <./USAGE.md>`__ Building binary wheels ---------------------- -When building binary wheels, you may need to use the libraries built by this -project as inputs (e.g., the `cffi` module uses `libffi`). To support this, this -project is able to package these dependencies as "wheels" that can be added to -the ``dist`` directory of the `Mobile Forge -project `__. - -To build these wheels, run: - -* ``make wheels`` to make all wheels for all mobile platforms -* ``make wheels-iOS`` to build all the iOS wheels -* ``make wheels-tvOS`` to build all the tvOS wheels -* ``make wheels-watchOS`` to build all the watchOS wheels +This project packages the Python standard library, but does not address building +binary wheels. Binary wheels for macOS can be obtained from PyPI. `Mobile Forge +`__ is a project that provides the +tooling to build build binary wheels for iOS (and potentially for tvOS and +watchOS, although that hasn't been tested). Historical support ------------------ @@ -145,3 +127,4 @@ maintained: * `Python 3.5 `__ (EOL February 2021) * `Python 3.6 `__ (EOL December 2021) * `Python 3.7 `__ (EOL September 2022) +* `Python 3.8 `__ (EOL October 2024) diff --git a/USAGE.md b/USAGE.md index 8b6b9ace..12bc1dc7 100644 --- a/USAGE.md +++ b/USAGE.md @@ -9,179 +9,21 @@ pre-build stub application, in the case of macOS). ## The manual way -The Python support package *can* be manually added to any Xcode project; -however, you'll need to perform some steps manually (essentially reproducing what -Briefcase is doing) - **NOTE** Briefcase usage is the officially supported approach for using this support package. If you are experiencing diffculties, one approach for debugging is to generate a "Hello World" project with Briefcase, and compare the project that Briefcase has generated with your own project. -To add this support package to your own project: - -1. [Download a release tarball for your desired Python version and Apple - platform](https://github.com/beeware/Python-Apple-support/releases) - -2. Add `Python.xcframework` to your Xcode project. You can put it anywhere in your - project that you want; the following instructions assume it has been put in a - folder named "Support". - -3. In Xcode, select the root node of the project tree, and select the target you - want to build. - -4. Select "General" -> "Frameworks, Libraries and Embedded Content", and ensure - that `Python.xcframework` is on the list of frameworks. It should be marked - "Embed and sign". - -5. Select "General" -> "Build Settings", and set the following values: - - Linking - General: - - `@executable_path/Frameworks` - - Search paths: - - Framework Search paths: `"$(PROJECT_DIR)/Support"` - - Header Search paths: `"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers"` - -6. Add a new "Run script" build phase named "Install target specific Python - Modules". This script will install the standard library for your target. The - script should have the following content: - -```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/Support/Python.xcframework/iphonesimulator/lib/" "$CODESIGNING_FOLDER_PATH/python/lib/" - # Also install any user-provided modules - # rsync -au --delete "$PROJECT_DIR/Testbed/app_packages.iphonesimulator/" "$CODESIGNING_FOLDER_PATH/app_packages" -else - echo "Installing Python modules for iOS Device" - rsync -au --delete "$PROJECT_DIR/Support/Python.xcframework/iphoneos/lib/" "$CODESIGNING_FOLDER_PATH/python/lib" - # Also install any user-provided modules - # rsync -au --delete "$PROJECT_DIR/Testbed/app_packages.iphoneos/" "$CODESIGNING_FOLDER_PATH/app_packages" -fi -``` - -7. Add a new "Run script" build phase named "Sign Python Binary Modules". - - The iOS App Store requires that binary modules *must* be contained inside frameworks. - This script will move every `.dylib` file in the `lib-dynload` folder to a unique - framework in the `Frameworks` folder of your packaged binary, then sign the new - framework. The script should have the following content: - -```bash -set -e - -install_dylib () { - INSTALL_BASE=$1 - FULL_DYLIB=$2 - - # The name of the .dylib file - DYLIB=$(basename "$FULL_DYLIB") - # The name of the .dylib file, relative to the install base - RELATIVE_DYLIB=${FULL_DYLIB#$CODESIGNING_FOLDER_PATH/$INSTALL_BASE/} - # The full dotted name of the binary module, constructed from the file path. - FULL_MODULE_NAME=$(echo $RELATIVE_DYLIB | 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_DYLIB" - mkdir -p "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER" - - cp "$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist" - defaults write "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist" CFBundleExecutable -string "$DYLIB" - defaults write "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist" CFBundleIdentifier -string "$FRAMEWORK_BUNDLE_ID" - fi - - echo "Installing binary for $RELATIVE_DYLIB" - mv "$FULL_DYLIB" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER" -} - -# Make sure to update the Python version version reference here -echo "Install standard library dylibs..." -find "$CODESIGNING_FOLDER_PATH/python/lib/python3.13/lib-dynload" -name "*.dylib" | while read FULL_DYLIB; do - install_dylib python/lib/python3.13/lib-dynload "$FULL_DYLIB" -done -# Also install any user-provided dynamic modules; e.g., -# echo "Install app package dylibs..." -# find "$CODESIGNING_FOLDER_PATH/app_packages" -name "*.dylib" | while read FULL_DYLIB; do -# install_dylib app_packages "$FULL_DYLIB" -# 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 "{}" \; -``` - - Make sure that you update these scripts to update the references to the - Python version, and include any user-provided code that you want to bundle. - If you use the ``rsync`` approach above, user-provided code should *not* be - included as part of the "Copy Bundle Resources" step. - - You'll also need to add a file named `dylib-Info-template.plist` to your Xcode - project, and make it a member of any target that needs to use Python. The template - should have the following content: - -```xml - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - - CFBundleIdentifier - - CFBundleInfoDictionaryVersion - 6.0 - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSupportedPlatforms - - iPhoneOS - - MinimumOSVersion - 12.0 - CFBundleVersion - 1 - - -``` - - macOS projects don't require `.dylib` files be moved like this, so you can use a much - simpler signing script: - -```bash -set -e -echo "Signing as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)" -find "$CODESIGNING_FOLDER_PATH/Contents/Resources/python-stdlib/lib-dynload" -name "*.so" -exec /usr/bin/codesign --force --sign "$EXPANDED_CODE_SIGN_IDENTITY" -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der {} \; -``` - -8. Disable dead code removal. As of March 2024 Xcode has "Dead Code Stripping" enabled by default. This _will_ remove functions from linked Python library that linker found unused. - Navigate to Build Settings -> Linkin - General. Find "Dead Code Stripping" and select "No" - -You will now be able to access the Python runtime in your Python code. - -If you are on iOS, you will be able to deploy to an iOS simulator without specifying -development team; however, you will need to specify a valid development team to sign -the binaries for deployment onto a physical device (or for submission to the App Store). +The Python support package *can* be manually added to any Xcode project; +however, you'll need to perform some steps manually (essentially reproducing +what Briefcase is doing). The steps required are documented in the CPython usage +guides: -If you are on macOS, you will need to specify a valid development team to run -the app. If you don't want to specify a development team in your project, you -will also need to enable the "Disable Library Validation" entitlement under -"Signing & Capabilities" -> "Hardened Runtime" for your project. +* [macOS](https://docs.python.org/3/using/mac.html) +* [iOS](https://docs.python.org/3.14/using/ios.html) -If you have any third party dependencies with binary components, they'll also need to go -through the processing of the scripts in steps 6 and 7. +For tvOS and watchOS, you should be able to broadly follow the instructions in +the iOS guide. ## Accessing the Python runtime diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 24f7d218..aca98f65 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1,3834 +1,662 @@ -diff --git a/Include/internal/pycore_faulthandler.h b/Include/internal/pycore_faulthandler.h -index 6dd7d8d7ca..836c8a3fcf 100644 ---- a/Include/internal/pycore_faulthandler.h -+++ b/Include/internal/pycore_faulthandler.h -@@ -12,6 +12,14 @@ - # include // sigaction - #endif - -+#ifdef __APPLE__ -+# include "TargetConditionals.h" -+#endif /* __APPLE__ */ -+ -+// tvOS and watchOS don't provide a number of important POSIX functions. -+#if TARGET_OS_TV || TARGET_OS_WATCH -+# undef HAVE_SIGALTSTACK -+#endif /* TVOS || WATCHOS */ - - #ifndef MS_WINDOWS - /* register() is useless on Windows, because only SIGSEGV, SIGABRT and ---- /dev/null -+++ b/Lib/_ios_support.py -@@ -0,0 +1,46 @@ -+try: -+ from ctypes import cdll, c_void_p, c_char_p -+ from ctypes import util -+except ImportError: -+ # ctypes is an optional module. If it's not present, we're limited in what -+ # we can tell about the system, but we don't want to prevent the platform -+ # module from working. -+ cdll = None -+ -+ -+def get_platform_ios(): -+ if cdll: -+ objc = cdll.LoadLibrary(util.find_library(b'objc')) -+ -+ objc.objc_getClass.restype = c_void_p -+ objc.objc_getClass.argtypes = [c_char_p] -+ objc.objc_msgSend.restype = c_void_p -+ objc.objc_msgSend.argtypes = [c_void_p, c_void_p] -+ objc.sel_registerName.restype = c_void_p -+ objc.sel_registerName.argtypes = [c_char_p] -+ -+ UIDevice = c_void_p(objc.objc_getClass(b'UIDevice')) -+ SEL_currentDevice = c_void_p(objc.sel_registerName(b'currentDevice')) -+ device = c_void_p(objc.objc_msgSend(UIDevice, SEL_currentDevice)) -+ -+ SEL_systemVersion = c_void_p(objc.sel_registerName(b'systemVersion')) -+ systemVersion = c_void_p(objc.objc_msgSend(device, SEL_systemVersion)) -+ -+ SEL_systemName = c_void_p(objc.sel_registerName(b'systemName')) -+ systemName = c_void_p(objc.objc_msgSend(device, SEL_systemName)) -+ -+ SEL_model = c_void_p(objc.sel_registerName(b'model')) -+ systemModel = c_void_p(objc.objc_msgSend(device, SEL_model)) -+ -+ # UTF8String returns a const char*; -+ SEL_UTF8String = c_void_p(objc.sel_registerName(b'UTF8String')) -+ objc.objc_msgSend.restype = c_char_p -+ -+ system = objc.objc_msgSend(systemName, SEL_UTF8String).decode() -+ release = objc.objc_msgSend(systemVersion, SEL_UTF8String).decode() -+ model = objc.objc_msgSend(systemModel, SEL_UTF8String).decode() -+ -+ return system, release, model -+ else: -+ # Return dummy values if we can't call system APIs. -+ return "iOS", "?", "iPhone" -diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py -index 0c2510e161..5567080ba5 100644 ---- a/Lib/ctypes/util.py -+++ b/Lib/ctypes/util.py -@@ -67,7 +67,7 @@ - return fname - return None - --elif os.name == "posix" and sys.platform == "darwin": -+elif os.name == "posix" and sys.platform in {'darwin', 'ios', 'tvos', 'watchos'}: - from ctypes.macholib.dyld import dyld_find as _dyld_find - def find_library(name): - possible = ['lib%s.dylib' % name, -diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py -index 0019897c94..c11ead2b11 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' -+_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY = 'cygwin', 'darwin', 'ios', 'tvos', 'watchos' - _CASE_INSENSITIVE_PLATFORMS = (_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY - + _CASE_INSENSITIVE_PLATFORMS_STR_KEY) - -@@ -1704,6 +1704,65 @@ - return f'FileFinder({self.path!r})' - - -+class AppleFrameworkLoader(ExtensionFileLoader): -+ """A loader for modules that have been packaged as Apple Frameworks for -+ compatibility with Apple's App Store policies. -+ -+ For compatibility with the App Store, *all* binary modules must be .dylib -+ objects, contained in a Framework, stored in the ``Frameworks`` folder of -+ the packaged app. If you're trying to run "from foo import _bar", and _bar -+ is implemented with the binary module "foo/_bar.abi3.dylib" (or any other -+ .dylib extension), this loader will look for -+ "{sys.executable}/Frameworks/foo._bar.framework/_bar.abi3.dylib" (forming -+ the package name by taking the full path of the library, and replacing ``/`` -+ with ``.``). The app packaging tool is responsible for putting the library -+ in this location. -+ -+ However, the ``__file__`` attribute of the _bar module will report as the -+ original location inside the ``foo`` directory. This so that code that -+ depends on walking directory trees will continue to work as expected based -+ on the *original* file location. -+ """ -+ def __init__(self, fullname, dylib_file, path): -+ super().__init__(fullname, dylib_file) -+ self.parent_paths = path -+ -+ def create_module(self, spec): -+ mod = super().create_module(spec) -+ if self.parent_paths: -+ for parent_path in self.parent_paths: -+ if _path_isdir(parent_path): -+ mod.__file__ = _path_join(parent_path, _path_split(self.path)[-1]) -+ continue -+ return mod -+ -+ -+class AppleFrameworkFinder: -+ """A finder for modules that have been packaged as Apple Frameworks -+ for compatibility with Apple's App Store policies. +diff --git a/Doc/library/urllib.parse.rst b/Doc/library/urllib.parse.rst +index 27909b763e9..fb5353e1895 100644 +--- a/Doc/library/urllib.parse.rst ++++ b/Doc/library/urllib.parse.rst +@@ -22,11 +22,19 @@ + + The module has been designed to match the internet RFC on Relative Uniform + Resource Locators. It supports the following URL schemes: ``file``, ``ftp``, +-``gopher``, ``hdl``, ``http``, ``https``, ``imap``, ``mailto``, ``mms``, ++``gopher``, ``hdl``, ``http``, ``https``, ``imap``, ``itms-services``, ``mailto``, ``mms``, + ``news``, ``nntp``, ``prospero``, ``rsync``, ``rtsp``, ``rtsps``, ``rtspu``, + ``sftp``, ``shttp``, ``sip``, ``sips``, ``snews``, ``svn``, ``svn+ssh``, + ``telnet``, ``wais``, ``ws``, ``wss``. + ++.. impl-detail:: ++ ++ The inclusion of the ``itms-services`` URL scheme can prevent an app from ++ passing Apple's App Store review process for the macOS and iOS App Stores. ++ Handling for the ``itms-services`` scheme is always removed on iOS; on ++ macOS, it *may* be removed if CPython has been built with the ++ :option:`--with-app-store-compliance` option. ++ + The :mod:`urllib.parse` module defines functions that fall into two broad + categories: URL parsing and URL quoting. These are covered in detail in + the following sections. +diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst +index 8eaba84e159..2c73c224e4e 100644 +--- a/Doc/using/configure.rst ++++ b/Doc/using/configure.rst +@@ -945,6 +945,17 @@ + Specify the name for the python framework on macOS only valid when + :option:`--enable-framework` is set (default: ``Python``). + ++.. option:: --with-app-store-compliance ++.. option:: --with-app-store-compliance=PATCH-FILE ++ ++ The Python standard library contains strings that are known to trigger ++ automated inspection tool errors when submitted for distribution by ++ the macOS and iOS App Stores. If enabled, this option will apply the list of ++ patches that are known to correct app store compliance. A custom patch ++ file can also be specified. This option is disabled by default. ++ ++ .. versionadded:: 3.13 ++ + iOS Options + ----------- + +diff --git a/Doc/using/ios.rst b/Doc/using/ios.rst +index 70a81fd8a53..5d0924be8d9 100644 +--- a/Doc/using/ios.rst ++++ b/Doc/using/ios.rst +@@ -323,3 +323,21 @@ + + * If you're using a separate folder for third-party packages, ensure that folder + is included as part of the ``PYTHONPATH`` configuration in step 10. ++ ++App Store Compliance ++==================== + -+ See AppleFrameworkLoader for details. -+ """ -+ def __init__(self, path): -+ self.frameworks_path = path -+ -+ def find_spec(self, fullname, path, target=None): -+ name = fullname.split(".")[-1] -+ -+ for extension in EXTENSION_SUFFIXES: -+ dylib_file = _path_join(self.frameworks_path, f"{fullname}.framework", f"{name}{extension}") -+ _bootstrap._verbose_message('Looking for Apple Framework dylib {}', dylib_file) -+ try: -+ dylib_exists = _path_isfile(dylib_file) -+ except ValueError: -+ pass -+ else: -+ if dylib_exists: -+ loader = AppleFrameworkLoader(fullname, dylib_file, path) -+ return _bootstrap.spec_from_loader(fullname, loader) -+ -+ return None -+ - # Import setup ############################################################### - - def _fix_up_module(ns, name, pathname, cpathname=None): -@@ -1753,3 +1812,7 @@ - supported_loaders = _get_supported_file_loaders() - sys.path_hooks.extend([FileFinder.path_hook(*supported_loaders)]) - sys.meta_path.append(PathFinder) -+ if sys.platform in {"ios", "tvos", "watchos"}: -+ frameworks_folder = _path_join(_path_split(sys.executable)[0], "Frameworks") -+ _bootstrap._verbose_message('Adding Apple Framework dylib finder at {}', frameworks_folder) -+ sys.meta_path.append(AppleFrameworkFinder(frameworks_folder)) -diff --git a/Lib/importlib/machinery.py b/Lib/importlib/machinery.py -index d9a19a13f7..fbd30b159f 100644 ---- a/Lib/importlib/machinery.py -+++ b/Lib/importlib/machinery.py -@@ -12,6 +12,7 @@ - from ._bootstrap_external import SourceFileLoader - from ._bootstrap_external import SourcelessFileLoader - from ._bootstrap_external import ExtensionFileLoader -+from ._bootstrap_external import AppleFrameworkLoader - from ._bootstrap_external import NamespaceLoader - ++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. ++ ++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 ++review rules cannot be challenged; so, it is necessary to modify the Python ++standard library for an app to pass App Store review. ++ ++The Python source tree contains ++: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. +diff --git a/Doc/using/mac.rst b/Doc/using/mac.rst +index 31d37aad2a7..44fb00de373 100644 +--- a/Doc/using/mac.rst ++++ b/Doc/using/mac.rst +@@ -188,6 +188,28 @@ + * `PyInstaller `__: A cross-platform packaging tool that creates + a single file or folder as a distributable artifact. + ++App Store Compliance ++-------------------- ++ ++Apps submitted for distribution through the macOS App Store 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. ++ ++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 ++review rules cannot be challenged. Therefore, it is necessary to modify the ++Python standard library for an app to pass App Store review. ++ ++The Python source tree contains ++: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 CPython is configured with the ++:option:`--with-app-store-compliance` option. ++ ++This patch is not normally required to use CPython on a Mac; nor is it required ++if you are distributing an app *outside* the macOS App Store. It is *only* ++required if you are using the macOS App Store as a distribution channel. ++ + Other Resources + =============== diff --git a/Lib/platform.py b/Lib/platform.py -index 7bb222088d..b4db9749da 100755 +index 5958382276e..5db5eb276a2 100755 --- a/Lib/platform.py +++ b/Lib/platform.py -@@ -496,6 +496,26 @@ - # If that also doesn't work return the default values - return release, versioninfo, machine +@@ -521,6 +521,54 @@ + return IOSVersionInfo(system, release, model, is_simulator) -+def ios_ver(): -+ """Get iOS/tvOS version information, and return it as a -+ tuple (system, release, model). All tuple entries are strings. + ++# 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. + """ -+ import _ios_support -+ return _ios_support.get_platform_ios() ++ 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) + -+def is_simulator(): -+ """Determine if the current platform is a device simulator. ++ return TVOSVersionInfo(system, release, model, is_simulator) + -+ Only useful when working with iOS, tvOS or watchOS, because -+ Apple provides simulator platforms for those devices. + -+ If the platform is actual hardware, returns False. Will also -+ return False for device *emulators*, which are indistinguishable -+ from actual devices because they are reproducing actual device -+ properties. ++# 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. + """ -+ return getattr(sys.implementation, "_simulator", False) ++ if sys.platform == "watchos": ++ # TODO: Can the iOS implementation be used here? ++ import _ios_support ++ result = _ios_support.get_platform_ios() ++ if result is not None: ++ return WatchOSVersionInfo(*result) ++ ++ return WatchOSVersionInfo(system, release, model, is_simulator) ++ + def _java_getprop(name, default): - + """This private helper is deprecated in 3.13 and will be removed in 3.15""" from java.lang import System -@@ -652,7 +672,7 @@ - default in case the command should fail. - - """ -- if sys.platform in ('dos', 'win32', 'win16'): -+ if sys.platform in {'dos', 'win32', 'win16', 'ios', 'tvos', 'watchos'}: - # XXX Others too ? - return default - -@@ -814,6 +834,24 @@ +@@ -884,14 +932,25 @@ csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0) return 'Alpha' if cpu_number >= 128 else 'VAX' -+ # On iOS, tvOS and watchOS, os.uname returns the architecture -+ # as uname.machine. On device it doesn't; but there's only -+ # on CPU architecture on device -+ def get_ios(): -+ if getattr(sys.implementation, "_simulator", False): -+ return os.uname().machine -+ return 'arm64' -+ +- # On the iOS simulator, os.uname returns the architecture as uname.machine. +- # On device it returns the model name for some reason; but there's only one +- # CPU architecture for iOS devices, so we know the right answer. ++ # On the iOS/tvOS/watchOS simulator, os.uname returns the architecture as ++ # uname.machine. On device it returns the model name for some reason; but ++ # there's only one CPU architecture for devices, so we know the right ++ # answer. + def get_ios(): + if sys.implementation._multiarch.endswith("simulator"): + return os.uname().machine + return 'arm64' + + def get_tvos(): -+ if getattr(sys.implementation, "_simulator", False): ++ if sys.implementation._multiarch.endswith("simulator"): + return os.uname().machine + return 'arm64' + + def get_watchos(): -+ if getattr(sys.implementation, "_simulator", False): ++ if sys.implementation._multiarch.endswith("simulator"): + return os.uname().machine + return 'arm64_32' + def from_subprocess(): """ Fall back to `uname -p` -@@ -968,6 +1006,15 @@ - system = 'Windows' - release = 'Vista' +@@ -1051,9 +1110,13 @@ + system = 'Android' + release = android_ver().release +- # Normalize responses on iOS + # Normalize responses on Apple mobile platforms -+ if sys.platform in {'ios', 'tvos'}: -+ system, release, model = ios_ver() -+ -+ # On iOS/tvOS simulators, os.uname() reports the machine as something -+ # like "arm64" or "x86_64". -+ if getattr(sys.implementation, "_simulator", False): -+ machine = f'{model}Simulator' -+ + if sys.platform == 'ios': + system, release, _, _ = ios_ver() ++ if sys.platform == 'tvos': ++ system, release, _, _ = tvos_ver() ++ if sys.platform == 'watchos': ++ system, release, _, _ = watchos_ver() + vals = system, node, release, version, machine # Replace 'unknown' values with the more portable '' - _uname_cache = uname_result(*map(_unknown_as_blank, vals)) -@@ -1247,11 +1294,13 @@ - system, release, version = system_alias(system, release, version) - - if system == 'Darwin': -- # macOS (darwin kernel) -- macos_release = mac_ver()[0] -- if macos_release: -- system = 'macOS' -- release = macos_release -+ if sys.platform in {'ios', 'tvos'}: -+ system, release, _ = ios_ver() -+ else: -+ macos_release = mac_ver()[0] -+ if macos_release: -+ system = 'macOS' -+ release = macos_release - - if system == 'Windows': - # MS platforms -diff --git a/Lib/site.py b/Lib/site.py -index 672fa7b000..9fd399e990 100644 ---- a/Lib/site.py -+++ b/Lib/site.py -@@ -294,6 +294,9 @@ - - if sys.platform == 'darwin' and sys._framework: - return f'{userbase}/lib/python/site-packages' -+ elif sys.platform in ('ios', 'tvos', 'watchos'): -+ from sysconfig import get_path -+ return get_path('purelib', sys.platform) - - return f'{userbase}/lib/python{version[0]}.{version[1]}/site-packages' - -diff --git a/Lib/subprocess.py b/Lib/subprocess.py -index 6df5dd551e..597da09643 100644 ---- a/Lib/subprocess.py -+++ b/Lib/subprocess.py -@@ -74,8 +74,8 @@ - else: - _mswindows = True - --# wasm32-emscripten and wasm32-wasi do not support processes --_can_fork_exec = sys.platform not in {"emscripten", "wasi"} -+# some platforms do not support processes -+_can_fork_exec = sys.platform not in {"emscripten", "wasi", "ios", "tvos", "watchos"} - - if _mswindows: - import _winapi -@@ -103,18 +103,22 @@ - if _can_fork_exec: - from _posixsubprocess import fork_exec as _fork_exec - # used in methods that are called by __del__ -- _waitpid = os.waitpid -- _waitstatus_to_exitcode = os.waitstatus_to_exitcode -- _WIFSTOPPED = os.WIFSTOPPED -- _WSTOPSIG = os.WSTOPSIG -- _WNOHANG = os.WNOHANG -+ class _del_safe: -+ waitpid = os.waitpid -+ waitstatus_to_exitcode = os.waitstatus_to_exitcode -+ WIFSTOPPED = os.WIFSTOPPED -+ WSTOPSIG = os.WSTOPSIG -+ WNOHANG = os.WNOHANG -+ ECHILD = errno.ECHILD - else: -- _fork_exec = None -- _waitpid = None -- _waitstatus_to_exitcode = None -- _WIFSTOPPED = None -- _WSTOPSIG = None -- _WNOHANG = None -+ class _del_safe: -+ waitpid = None -+ waitstatus_to_exitcode = None -+ WIFSTOPPED = None -+ WSTOPSIG = None -+ WNOHANG = None -+ ECHILD = errno.ECHILD -+ - import select - import selectors - -@@ -1951,20 +1955,16 @@ - raise child_exception_type(err_msg) - - -- def _handle_exitstatus(self, sts, -- _waitstatus_to_exitcode=_waitstatus_to_exitcode, -- _WIFSTOPPED=_WIFSTOPPED, -- _WSTOPSIG=_WSTOPSIG): -+ def _handle_exitstatus(self, sts, _del_safe=_del_safe): - """All callers to this function MUST hold self._waitpid_lock.""" - # This method is called (indirectly) by __del__, so it cannot - # refer to anything outside of its local scope. -- if _WIFSTOPPED(sts): -- self.returncode = -_WSTOPSIG(sts) -+ if _del_safe.WIFSTOPPED(sts): -+ self.returncode = -_del_safe.WSTOPSIG(sts) - else: -- self.returncode = _waitstatus_to_exitcode(sts) -+ self.returncode = _del_safe.waitstatus_to_exitcode(sts) - -- def _internal_poll(self, _deadstate=None, _waitpid=_waitpid, -- _WNOHANG=_WNOHANG, _ECHILD=errno.ECHILD): -+ def _internal_poll(self, _deadstate=None, _del_safe=_del_safe): - """Check if child process has terminated. Returns returncode - attribute. - -@@ -1980,13 +1980,13 @@ - try: - if self.returncode is not None: - return self.returncode # Another thread waited. -- pid, sts = _waitpid(self.pid, _WNOHANG) -+ pid, sts = _del_safe.waitpid(self.pid, _del_safe.WNOHANG) - if pid == self.pid: - self._handle_exitstatus(sts) - except OSError as e: - if _deadstate is not None: - self.returncode = _deadstate -- elif e.errno == _ECHILD: -+ elif e.errno == _del_safe.ECHILD: - # This happens if SIGCLD is set to be ignored or - # waiting for child processes has otherwise been - # disabled for our process. This child is dead, we +@@ -1343,6 +1406,10 @@ + # macOS and iOS both report as a "Darwin" kernel + if sys.platform == "ios": + system, release, _, _ = ios_ver() ++ elif sys.platform == "tvos": ++ system, release, _, _ = tvos_ver() ++ elif sys.platform == "watchos": ++ system, release, _, _ = watchos_ver() + else: + macos_release = mac_ver()[0] + if macos_release: diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py -index 68d30c0f9e..4a8a27b6d0 100644 +index 83e057c177f..d18a7564866 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py -@@ -96,6 +96,33 @@ - 'scripts': '{base}/Scripts', - 'data': '{base}', - }, -+ 'ios': { -+ 'stdlib': '{installed_base}/lib/python{py_version_short}', -+ 'platstdlib': '{installed_base}/lib/python{py_version_short}', -+ 'purelib': '{installed_base}/lib/python{py_version_short}/site-packages', -+ 'platlib': '{installed_base}/lib/python{py_version_short}/site-packages', -+ 'include': '{installed_base}/include', -+ 'scripts': '{installed_base}/bin', -+ 'data': '{installed_base}/Resources', -+ }, -+ 'tvos': { -+ 'stdlib': '{installed_base}/lib/python{py_version_short}', -+ 'platstdlib': '{installed_base}/lib/python{py_version_short}', -+ 'purelib': '{installed_base}/lib/python{py_version_short}/site-packages', -+ 'platlib': '{installed_base}/lib/python{py_version_short}/site-packages', -+ 'include': '{installed_base}/include', -+ 'scripts': '{installed_base}/bin', -+ 'data': '{installed_base}/Resources', -+ }, -+ 'watchos': { -+ 'stdlib': '{installed_base}/lib/python{py_version_short}', -+ 'platstdlib': '{installed_base}/lib/python{py_version_short}', -+ 'purelib': '{installed_base}/lib/python{py_version_short}/site-packages', -+ 'platlib': '{installed_base}/lib/python{py_version_short}/site-packages', -+ 'include': '{installed_base}/include', -+ 'scripts': '{installed_base}/bin', -+ 'data': '{installed_base}/Resources', -+ }, - } - - # For the OS-native venv scheme, we essentially provide an alias: -@@ -282,12 +309,19 @@ - 'home': 'posix_home', - 'user': 'nt_user', - } -+ if sys.platform in ('ios', 'tvos', 'watchos'): -+ return { -+ 'prefix': sys.platform, -+ 'home': sys.platform, -+ 'user': sys.platform, -+ } - if sys.platform == 'darwin' and sys._framework: - return { - 'prefix': 'posix_prefix', - 'home': 'posix_home', - 'user': 'osx_framework_user', - } -+ - return { - 'prefix': 'posix_prefix', - 'home': 'posix_home', -@@ -619,10 +653,16 @@ - if m: +@@ -642,7 +642,15 @@ release = m.group() elif osname[:6] == "darwin": -- import _osx_support -- osname, release, machine = _osx_support.get_platform_osx( -- get_config_vars(), -- osname, release, machine) -+ if sys.platform in ("ios", "tvos", "watchos"): -+ import _ios_support -+ _, release, _ = _ios_support.get_platform_ios() + if sys.platform == "ios": +- release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "12.0") ++ release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0") + osname = sys.platform + machine = sys.implementation._multiarch -+ else: -+ import _osx_support -+ osname, release, machine = _osx_support.get_platform_osx( -+ get_config_vars(), -+ osname, release, machine) - - return f"{osname}-{release}-{machine}" - -diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py -index 21e8770ab3..67958d247c 100644 ---- a/Lib/test/support/__init__.py -+++ b/Lib/test/support/__init__.py -@@ -46,7 +46,7 @@ - "check_disallow_instantiation", "check_sanitizer", "skip_if_sanitizer", - "requires_limited_api", "requires_specialization", - # sys -- "MS_WINDOWS", "is_jython", "is_android", "is_emscripten", "is_wasi", -+ "MS_WINDOWS", "is_jython", "is_android", "is_emscripten", "is_wasi", "is_apple_mobile", - "check_impl_detail", "unix_shell", "setswitchinterval", - # os - "get_pagesize", -@@ -520,7 +520,7 @@ - - is_android = hasattr(sys, 'getandroidapilevel') - --if sys.platform not in ('win32', 'vxworks'): -+if sys.platform not in ('win32', 'vxworks', 'ios', 'tvos', 'watchos'): - unix_shell = '/system/bin/sh' if is_android else '/bin/sh' - else: - unix_shell = None -@@ -530,12 +530,25 @@ - is_emscripten = sys.platform == "emscripten" - is_wasi = sys.platform == "wasi" - --has_fork_support = hasattr(os, "fork") and not is_emscripten and not is_wasi -+# Apple mobile platforms (iOS/tvOS/watchOS) are POSIX-like but do not -+# have subprocess or fork support. -+is_apple_mobile = sys.platform in ('ios', 'tvos', 'watchos') -+ -+has_fork_support = ( -+ hasattr(os, "fork") -+ and not is_emscripten -+ and not is_wasi -+ and not is_apple_mobile -+) - - def requires_fork(): - return unittest.skipUnless(has_fork_support, "requires working os.fork()") - --has_subprocess_support = not is_emscripten and not is_wasi -+has_subprocess_support = ( -+ not is_emscripten -+ and not is_wasi -+ and not is_apple_mobile -+) - - def requires_subprocess(): - """Used for subprocess, os.spawn calls, fd inheritance""" -diff --git a/Lib/test/support/os_helper.py b/Lib/test/support/os_helper.py -index 821a4b1ffd..517f7b57f4 100644 ---- a/Lib/test/support/os_helper.py -+++ b/Lib/test/support/os_helper.py -@@ -20,7 +20,7 @@ - - # TESTFN_UNICODE is a non-ascii filename - TESTFN_UNICODE = TESTFN_ASCII + "-\xe0\xf2\u0258\u0141\u011f" --if sys.platform == 'darwin': -+if sys.platform in ('darwin', 'ios', 'tvos', 'watchos'): - # In Mac OS X's VFS API file names are, by definition, canonically - # decomposed Unicode, encoded using UTF-8. See QA1173: - # http://developer.apple.com/mac/library/qa/qa2001/qa1173.html -@@ -46,8 +46,8 @@ - 'encoding (%s). Unicode filename tests may not be effective' - % (TESTFN_UNENCODABLE, sys.getfilesystemencoding())) - TESTFN_UNENCODABLE = None --# macOS and Emscripten deny unencodable filenames (invalid utf-8) --elif sys.platform not in {'darwin', 'emscripten', 'wasi'}: -+# Apple and Emscripten deny unencodable filenames (invalid utf-8) -+elif sys.platform not in {'darwin', 'ios', 'tvos', 'watchos', 'emscripten', 'wasi'}: - try: - # ascii and utf-8 cannot encode the byte 0xff - b'\xff'.decode(sys.getfilesystemencoding()) -diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py -index b25c097573..7491782c7f 100644 ---- a/Lib/test/test_asyncio/test_events.py -+++ b/Lib/test/test_asyncio/test_events.py -@@ -33,6 +33,7 @@ - from multiprocessing.util import _cleanup_tests as multiprocessing_cleanup_tests - from test.test_asyncio import utils as test_utils - from test import support -+from test.support import is_apple_mobile - from test.support import socket_helper - from test.support import threading_helper - from test.support import ALWAYS_EQ, LARGEST, SMALLEST -@@ -1799,6 +1800,7 @@ - next(it) - - -+@support.requires_subprocess() - class SubprocessTestsMixin: - - def check_terminated(self, returncode): -diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py -index 9c92e75886..65ee41dbd1 100644 ---- a/Lib/test/test_asyncio/test_streams.py -+++ b/Lib/test/test_asyncio/test_streams.py -@@ -18,6 +18,7 @@ - - import asyncio - from test.test_asyncio import utils as test_utils -+from test.support import requires_subprocess - - - def tearDownModule(): -@@ -771,6 +772,7 @@ - self.assertEqual(msg2, b"hello world 2!\n") - - @unittest.skipIf(sys.platform == 'win32', "Don't have pipes") -+ @requires_subprocess() - def test_read_all_from_pipe_reader(self): - # See asyncio issue 168. This test is derived from the example - # subprocess_attach_read_pipe.py, but we configure the -diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py -index 179c8cb8cc..c8659f6ed6 100644 ---- a/Lib/test/test_asyncio/test_subprocess.py -+++ b/Lib/test/test_asyncio/test_subprocess.py -@@ -11,6 +11,7 @@ - from asyncio import subprocess - from test.test_asyncio import utils as test_utils - from test import support -+from test.support import is_apple_mobile - from test.support import os_helper - - -@@ -47,6 +48,8 @@ - self._proc.pid = -1 - - -+@unittest.skipIf(is_apple_mobile, -+ f"{sys.platform} doesn't support subprocesses.") - class SubprocessTransportTests(test_utils.TestCase): - def setUp(self): - super().setUp() -@@ -110,6 +113,8 @@ - transport.close() - - -+@unittest.skipIf(is_apple_mobile, -+ f"{sys.platform} doesn't support subprocesses.") - class SubprocessMixin: - - def test_stdin_stdout(self): -diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py -index d2c8cba6ac..c3795dede9 100644 ---- a/Lib/test/test_asyncio/test_unix_events.py -+++ b/Lib/test/test_asyncio/test_unix_events.py -@@ -1875,6 +1875,7 @@ - - - @unittest.skipUnless(hasattr(os, 'fork'), 'requires os.fork()') -+@support.requires_subprocess() - class TestFork(unittest.IsolatedAsyncioTestCase): - - async def test_fork_not_share_event_loop(self): -diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py -index 1b58882601..7ae237e6c1 100644 ---- a/Lib/test/test_cmd_line_script.py -+++ b/Lib/test/test_cmd_line_script.py -@@ -560,7 +560,9 @@ - # Python cannot a undecodable bytes argument to a subprocess. - # WASI does not permit invalid UTF-8 names. - if (os_helper.TESTFN_UNDECODABLE -- and sys.platform not in ('win32', 'darwin', 'emscripten', 'wasi')): -+ and sys.platform not in ( -+ 'win32', 'darwin', 'ios', 'tvos', 'watchos', 'emscripten', 'wasi' -+ )): - name = os.fsdecode(os_helper.TESTFN_UNDECODABLE) - elif os_helper.TESTFN_NONASCII: - name = os_helper.TESTFN_NONASCII -diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py -index 203dd6fe57..a4e7099da3 100644 ---- a/Lib/test/test_fcntl.py -+++ b/Lib/test/test_fcntl.py -@@ -6,7 +6,7 @@ - import struct - import sys - import unittest --from test.support import verbose, cpython_only, get_pagesize -+from test.support import cpython_only, get_pagesize, is_apple_mobile, requires_subprocess, verbose - from test.support.import_helper import import_module - from test.support.os_helper import TESTFN, unlink - -@@ -57,7 +57,9 @@ - start_len = "qq" - - if (sys.platform.startswith(('netbsd', 'freebsd', 'openbsd')) -- or sys.platform == 'darwin'): -+ or sys.platform == 'darwin' -+ or is_apple_mobile -+ ): - if struct.calcsize('l') == 8: - off_t = 'l' - pid_t = 'i' -@@ -157,6 +159,7 @@ - self.assertRaises(TypeError, fcntl.flock, 'spam', fcntl.LOCK_SH) - - @unittest.skipIf(platform.system() == "AIX", "AIX returns PermissionError") -+ @requires_subprocess() - def test_lockf_exclusive(self): - self.f = open(TESTFN, 'wb+') - cmd = fcntl.LOCK_EX | fcntl.LOCK_NB -@@ -169,6 +172,7 @@ - self.assertEqual(p.exitcode, 0) - - @unittest.skipIf(platform.system() == "AIX", "AIX returns PermissionError") -+ @requires_subprocess() - def test_lockf_share(self): - self.f = open(TESTFN, 'wb+') - cmd = fcntl.LOCK_SH | fcntl.LOCK_NB -diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py -index 2f191ea7a4..81115e9db8 100644 ---- a/Lib/test/test_ftplib.py -+++ b/Lib/test/test_ftplib.py -@@ -18,6 +18,7 @@ - - from unittest import TestCase, skipUnless - from test import support -+from test.support import requires_subprocess - from test.support import threading_helper - from test.support import socket_helper - from test.support import warnings_helper -@@ -900,6 +901,7 @@ - - - @skipUnless(ssl, "SSL not available") -+@requires_subprocess() - class TestTLS_FTPClassMixin(TestFTPClass): - """Repeat TestFTPClass tests starting the TLS layer for both control - and data connections first. -@@ -916,6 +918,7 @@ - - - @skipUnless(ssl, "SSL not available") -+@requires_subprocess() - class TestTLS_FTPClass(TestCase): - """Specific TLS_FTP class tests.""" - -diff --git a/Lib/test/test_genericpath.py b/Lib/test/test_genericpath.py -index 4f311c2d49..c0a6a48dea 100644 ---- a/Lib/test/test_genericpath.py -+++ b/Lib/test/test_genericpath.py -@@ -488,7 +488,9 @@ - # invalid UTF-8 name. Windows allows creating a directory with an - # arbitrary bytes name, but fails to enter this directory - # (when the bytes name is used). -- and sys.platform not in ('win32', 'darwin', 'emscripten', 'wasi')): -+ and sys.platform not in ( -+ 'win32', 'darwin', 'ios', 'tvos', 'watchos', 'emscripten', 'wasi' -+ )): - name = os_helper.TESTFN_UNDECODABLE - elif os_helper.TESTFN_NONASCII: - name = os_helper.TESTFN_NONASCII -diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py -index 9fa6ecf9c0..01dc990b1e 100644 ---- a/Lib/test/test_httpservers.py -+++ b/Lib/test/test_httpservers.py -@@ -30,7 +30,9 @@ - - import unittest - from test import support -+from test.support import is_apple_mobile - from test.support import os_helper -+from test.support import requires_subprocess - from test.support import threading_helper - - support.requires_working_socket(module=True) -@@ -410,8 +412,8 @@ - reader.close() - return body - -- @unittest.skipIf(sys.platform == 'darwin', -- 'undecodable name cannot always be decoded on macOS') -+ @unittest.skipIf(sys.platform == 'darwin' or is_apple_mobile, -+ 'undecodable name cannot always be decoded on Apple platforms') - @unittest.skipIf(sys.platform == 'win32', - 'undecodable name cannot be decoded on win32') - @unittest.skipUnless(os_helper.TESTFN_UNDECODABLE, -@@ -422,9 +424,9 @@ - with open(os.path.join(self.tempdir, filename), 'wb') as f: - f.write(os_helper.TESTFN_UNDECODABLE) - response = self.request(self.base_url + '/') -- if sys.platform == 'darwin': -- # On Mac OS the HFS+ filesystem replaces bytes that aren't valid -- # UTF-8 into a percent-encoded value. -+ if sys.platform == 'darwin' or is_apple_mobile: -+ # On Apple platforms the HFS+ filesystem replaces bytes that -+ # aren't valid UTF-8 into a percent-encoded value. - for name in os.listdir(self.tempdir): - if name != 'test': # Ignore a filename created in setUp(). - filename = name -@@ -697,6 +699,7 @@ - - @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0, - "This test can't be run reliably as root (issue #13308).") -+@requires_subprocess() - class CGIHTTPServerTestCase(BaseTestCase): - class request_handler(NoLogRequestHandler, CGIHTTPRequestHandler): - _test_case_self = None # populated by each setUp() method call. -diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py -index aa465c70df..16337bb12b 100644 ---- a/Lib/test/test_import/__init__.py -+++ b/Lib/test/test_import/__init__.py -@@ -5,7 +5,7 @@ - import importlib.util - from importlib._bootstrap_external import _get_sourcefile - from importlib.machinery import ( -- BuiltinImporter, ExtensionFileLoader, FrozenImporter, SourceFileLoader, -+ AppleFrameworkLoader, BuiltinImporter, ExtensionFileLoader, FrozenImporter, SourceFileLoader, - ) - import marshal - import os -@@ -25,7 +25,7 @@ - - from test.support import os_helper - from test.support import ( -- STDLIB_DIR, swap_attr, swap_item, cpython_only, is_emscripten, -+ STDLIB_DIR, swap_attr, swap_item, cpython_only, is_apple_mobile, is_emscripten, - is_wasi, run_in_subinterp, run_in_subinterp_with_config, Py_TRACE_REFS) - from test.support.import_helper import ( - forget, make_legacy_pyc, unlink, unload, ready_to_import, -@@ -66,6 +66,7 @@ - MODULE_KINDS = { - BuiltinImporter: 'built-in', - ExtensionFileLoader: 'extension', -+ AppleFrameworkLoader: 'framework extension', - FrozenImporter: 'frozen', - SourceFileLoader: 'pure Python', - } -@@ -91,7 +92,10 @@ - assert module.__spec__.origin == 'built-in', module.__spec__ - - def require_extension(module, *, skip=False): -- _require_loader(module, ExtensionFileLoader, skip) -+ if is_apple_mobile: -+ _require_loader(module, AppleFrameworkLoader, skip) -+ else: -+ _require_loader(module, ExtensionFileLoader, skip) - - def require_frozen(module, *, skip=True): - module = _require_loader(module, FrozenImporter, skip) -@@ -360,7 +364,7 @@ - self.assertEqual(cm.exception.path, _testcapi.__file__) - self.assertRegex( - str(cm.exception), -- r"cannot import name 'i_dont_exist' from '_testcapi' \(.*\.(so|pyd)\)" -+ r"cannot import name 'i_dont_exist' from '_testcapi' \(.*\.(so|dylib|pyd)\)" - ) - else: - self.assertEqual( -@@ -1678,6 +1682,12 @@ - os.set_blocking(r, False) - return (r, w) - -+ def create_extension_loader(self, modname, filename): -+ if is_apple_mobile: -+ return AppleFrameworkLoader(modname, filename, None) -+ else: -+ return ExtensionFileLoader(modname, filename) -+ - def import_script(self, name, fd, filename=None, check_override=None): - override_text = '' - if check_override is not None: -@@ -1872,7 +1882,7 @@ - def test_multi_init_extension_non_isolated_compat(self): - modname = '_test_non_isolated' - filename = _testmultiphase.__file__ -- loader = ExtensionFileLoader(modname, filename) -+ loader = self.create_extension_loader(modname, filename) - spec = importlib.util.spec_from_loader(modname, loader) - module = importlib.util.module_from_spec(spec) - loader.exec_module(module) -@@ -1890,7 +1900,7 @@ - def test_multi_init_extension_per_interpreter_gil_compat(self): - modname = '_test_shared_gil_only' - filename = _testmultiphase.__file__ -- loader = ExtensionFileLoader(modname, filename) -+ loader = self.create_extension_loader(modname, filename) - spec = importlib.util.spec_from_loader(modname, loader) - module = importlib.util.module_from_spec(spec) - loader.exec_module(module) -@@ -2020,10 +2030,13 @@ - @classmethod - def setUpClass(cls): - spec = importlib.util.find_spec(cls.NAME) -- from importlib.machinery import ExtensionFileLoader -+ from importlib.machinery import AppleFrameworkLoader, ExtensionFileLoader - cls.FILE = spec.origin - cls.LOADER = type(spec.loader) -- assert cls.LOADER is ExtensionFileLoader -+ if is_apple_mobile: -+ assert cls.LOADER is AppleFrameworkLoader -+ else: -+ assert cls.LOADER is ExtensionFileLoader - - # Start fresh. - cls.clean_up() -@@ -2063,7 +2076,10 @@ - """ - # This is essentially copied from the old imp module. - from importlib._bootstrap import _load -- loader = self.LOADER(name, path) -+ if is_apple_mobile: -+ loader = self.LOADER(name, path, None) -+ else: -+ loader = self.LOADER(name, path) - - # Issue bpo-24748: Skip the sys.modules check in _load_module_shim; - # always load new extension. -diff --git a/Lib/test/test_importlib/extension/test_finder.py b/Lib/test/test_importlib/extension/test_finder.py -index 1d5b6e7a5d..519a1c536b 100644 ---- a/Lib/test/test_importlib/extension/test_finder.py -+++ b/Lib/test/test_importlib/extension/test_finder.py -@@ -1,3 +1,4 @@ -+from test.support import is_apple_mobile - from test.test_importlib import abc, util - - machinery = util.import_importlib('importlib.machinery') -@@ -11,6 +12,8 @@ - """Test the finder for extension modules.""" - - def setUp(self): -+ if is_apple_mobile: -+ raise unittest.SkipTest(f"{sys.platform} uses a custom finder") - if not self.machinery.EXTENSION_SUFFIXES: - raise unittest.SkipTest("Requires dynamic loading support.") - if util.EXTENSIONS.name in sys.builtin_module_names: -diff --git a/Lib/test/test_importlib/extension/test_loader.py b/Lib/test/test_importlib/extension/test_loader.py -index 64c8a54851..fe3219c885 100644 ---- a/Lib/test/test_importlib/extension/test_loader.py -+++ b/Lib/test/test_importlib/extension/test_loader.py -@@ -1,3 +1,4 @@ -+from test.support import is_apple_mobile - from test.test_importlib import abc, util - - machinery = util.import_importlib('importlib.machinery') -@@ -16,6 +17,8 @@ - """Test ExtensionFileLoader.""" - - def setUp(self): -+ if is_apple_mobile: -+ raise unittest.SkipTest(f"{sys.platform} uses a custom loader") - if not self.machinery.EXTENSION_SUFFIXES: - raise unittest.SkipTest("Requires dynamic loading support.") - if util.EXTENSIONS.name in sys.builtin_module_names: -diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py -index 022cf21a47..67f484d40e 100644 ---- a/Lib/test/test_io.py -+++ b/Lib/test/test_io.py -@@ -40,6 +40,7 @@ - from test.support.script_helper import ( - assert_python_ok, assert_python_failure, run_python_until_end) - from test.support import import_helper -+from test.support import is_apple_mobile - from test.support import os_helper - from test.support import threading_helper - from test.support import warnings_helper -@@ -605,7 +606,7 @@ - # On Windows and Mac OSX this test consumes large resources; It takes - # a long time to build the >2 GiB file and takes >2 GiB of disk space - # therefore the resource must be enabled to run this test. -- if sys.platform[:3] == 'win' or sys.platform == 'darwin': -+ if sys.platform[:3] == 'win' or sys.platform == 'darwin' or is_apple_mobile: - support.requires( - 'largefile', - 'test requires %s bytes and a long time to run' % self.LARGE) -diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py -index ab969ce26a..e6aee6c7de 100644 ---- a/Lib/test/test_logging.py -+++ b/Lib/test/test_logging.py -@@ -43,6 +43,7 @@ - import tempfile - from test.support.script_helper import assert_python_ok, assert_python_failure - from test import support -+from test.support import is_apple_mobile - from test.support import os_helper - from test.support import socket_helper - from test.support import threading_helper -diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py -index 3d9d6d5d0a..dfb1d6f84d 100644 ---- a/Lib/test/test_marshal.py -+++ b/Lib/test/test_marshal.py -@@ -1,5 +1,5 @@ - from test import support --from test.support import os_helper, requires_debug_ranges -+from test.support import os_helper, requires_debug_ranges, is_apple_mobile - from test.support.script_helper import assert_python_ok - import array - import io -@@ -263,7 +263,10 @@ - elif sys.platform == 'wasi': - MAX_MARSHAL_STACK_DEPTH = 1500 - else: -- MAX_MARSHAL_STACK_DEPTH = 2000 -+ if is_apple_mobile: -+ MAX_MARSHAL_STACK_DEPTH = 1500 -+ else: -+ MAX_MARSHAL_STACK_DEPTH = 2000 - for i in range(MAX_MARSHAL_STACK_DEPTH - 2): - last.append([0]) - last = last[-1] -diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py -index dfcf303942..5aabfac885 100644 ---- a/Lib/test/test_mmap.py -+++ b/Lib/test/test_mmap.py -@@ -1,5 +1,5 @@ - from test.support import ( -- requires, _2G, _4G, gc_collect, cpython_only, is_emscripten -+ requires, _2G, _4G, gc_collect, cpython_only, is_emscripten, is_apple_mobile - ) - from test.support.import_helper import import_module - from test.support.os_helper import TESTFN, unlink -@@ -245,7 +245,7 @@ - with open(TESTFN, "r+b") as f: - self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, access=4) - -- if os.name == "posix": -+ if os.name == "posix" and not is_apple_mobile: - # Try incompatible flags, prot and access parameters. - with open(TESTFN, "r+b") as f: - self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, -@@ -1007,7 +1007,7 @@ - unlink(TESTFN) - - def _make_test_file(self, num_zeroes, tail): -- if sys.platform[:3] == 'win' or sys.platform == 'darwin': -+ if sys.platform[:3] == 'win' or sys.platform == 'darwin' or is_apple_mobile: - requires('largefile', - 'test requires %s bytes and a long time to run' % str(0x180000000)) - f = open(TESTFN, 'w+b') -diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py -index 398393b233..52b0a2ac23 100644 ---- a/Lib/test/test_os.py -+++ b/Lib/test/test_os.py -@@ -3785,6 +3785,7 @@ - self.assertGreaterEqual(size.columns, 0) - self.assertGreaterEqual(size.lines, 0) - -+ @support.requires_subprocess() - def test_stty_match(self): - """Check if stty returns the same results - -diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py -index 2169733503..842fed6a67 100644 ---- a/Lib/test/test_platform.py -+++ b/Lib/test/test_platform.py -@@ -8,7 +8,7 @@ - from unittest import mock - - from test import support --from test.support import os_helper -+from test.support import os_helper, is_apple_mobile - - FEDORA_OS_RELEASE = """\ - NAME=Fedora -@@ -328,7 +328,7 @@ - def test_mac_ver(self): - res = platform.mac_ver() - -- if platform.uname().system == 'Darwin': -+ if platform.uname().system == 'Darwin' and not is_apple_mobile: - # We are on a macOS system, check that the right version - # information is returned - output = subprocess.check_output(['sw_vers'], text=True) -@@ -360,6 +360,9 @@ - else: - self.assertEqual(res[2], 'PowerPC') - -+ @unittest.skipUnless(is_apple_mobile, 'iOS/tvOS/watchOS only test') -+ def test_ios_ver(self): -+ res = platform.ios_ver() - - @unittest.skipUnless(sys.platform == 'darwin', "OSX only test") - def test_mac_ver_with_fork(self): -@@ -472,7 +475,8 @@ - 'root:xnu-4570.71.2~1/RELEASE_X86_64'), - 'x86_64', 'i386') - arch = ('64bit', '') -- with mock.patch.object(platform, 'uname', return_value=uname), \ -+ with mock.patch.object(sys, "platform", "darwin"), \ -+ mock.patch.object(platform, 'uname', return_value=uname), \ - mock.patch.object(platform, 'architecture', return_value=arch): - for mac_ver, expected_terse, expected in [ - # darwin: mac_ver() returns empty strings -diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py -index 9d72dba159..a8b50606ac 100644 ---- a/Lib/test/test_posix.py -+++ b/Lib/test/test_posix.py -@@ -2,6 +2,7 @@ - - from test import support - from test.support import import_helper -+from test.support import is_apple_mobile - from test.support import os_helper - from test.support import warnings_helper - from test.support.script_helper import assert_python_ok -@@ -69,12 +70,19 @@ - "getpid", "getpgrp", "getppid", "getuid", "sync", - ] - -+ # getgroups can't be invoked on iOS/tvOS/watchOS. -+ if is_apple_mobile: -+ NO_ARG_FUNCTIONS.append("getgroups") -+ - for name in NO_ARG_FUNCTIONS: - posix_func = getattr(posix, name, None) - if posix_func is not None: - with self.subTest(name): -- posix_func() -- self.assertRaises(TypeError, posix_func, 1) -+ try: -+ posix_func() -+ self.assertRaises(TypeError, posix_func, 1) -+ except Exception as e: -+ self.fail('Problem invoking %s: %s' % (name, e)) - - @unittest.skipUnless(hasattr(posix, 'getresuid'), - 'test needs posix.getresuid()') -@@ -779,9 +787,10 @@ - check_stat(uid, gid) - self.assertRaises(OSError, chown_func, first_param, 0, -1) - check_stat(uid, gid) -- if 0 not in os.getgroups(): -- self.assertRaises(OSError, chown_func, first_param, -1, 0) -- check_stat(uid, gid) -+ if hasattr(os, 'getgroups') and not is_apple_mobile: -+ if 0 not in os.getgroups(): -+ self.assertRaises(OSError, chown_func, first_param, -1, 0) -+ check_stat(uid, gid) - # test illegal types - for t in str, float: - self.assertRaises(TypeError, chown_func, first_param, t(uid), gid) -@@ -1129,7 +1138,7 @@ - self.assertIsInstance(hi, int) - self.assertGreaterEqual(hi, lo) - # OSX evidently just returns 15 without checking the argument. -- if sys.platform != "darwin": -+ if sys.platform != 'darwin' and not is_apple_mobile: - self.assertRaises(OSError, posix.sched_get_priority_min, -23) - self.assertRaises(OSError, posix.sched_get_priority_max, -23) - -@@ -1899,11 +1908,13 @@ - - - @unittest.skipUnless(hasattr(os, 'posix_spawn'), "test needs os.posix_spawn") -+@support.requires_subprocess() - class TestPosixSpawn(unittest.TestCase, _PosixSpawnMixin): - spawn_func = getattr(posix, 'posix_spawn', None) - - - @unittest.skipUnless(hasattr(os, 'posix_spawnp'), "test needs os.posix_spawnp") -+@support.requires_subprocess() - class TestPosixSpawnP(unittest.TestCase, _PosixSpawnMixin): - spawn_func = getattr(posix, 'posix_spawnp', None) - -diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py -index f31a68c5d8..062a7524dd 100644 ---- a/Lib/test/test_pty.py -+++ b/Lib/test/test_pty.py -@@ -1,11 +1,15 @@ -+import sys -+import unittest - from test.support import verbose, reap_children -+from test.support import is_apple_mobile, is_emscripten, is_wasi - from test.support.import_helper import import_module - - # Skip these tests if termios or fcntl are not available - import_module('termios') --# fcntl is a proxy for not being one of the wasm32 platforms even though we --# don't use this module... a proper check for what crashes those is needed. --import_module("fcntl") -+ -+# Skip tests on WASM platforms, plus iOS/tvOS/watchOS -+if is_apple_mobile or is_emscripten or is_wasi: -+ raise unittest.SkipTest(f"pty tests not required on {sys.platform}") - - import errno - import os -@@ -16,7 +20,6 @@ - import signal - import socket - import io # readline --import unittest - import warnings - - TEST_STRING_1 = b"I wish to buy a fish license.\n" -diff --git a/Lib/test/test_selectors.py b/Lib/test/test_selectors.py -index 677349c2bf..f4471bdf8c 100644 ---- a/Lib/test/test_selectors.py -+++ b/Lib/test/test_selectors.py -@@ -526,7 +526,7 @@ - try: - fds = s.select() - except OSError as e: -- if e.errno == errno.EINVAL and sys.platform == 'darwin': -+ if e.errno == errno.EINVAL and (sys.platform == 'darwin' or support.is_apple_mobile): - # unexplainable errors on macOS don't need to fail the test - self.skipTest("Invalid argument error calling poll()") - raise -diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py -index d231e66b7b..182eeaaca4 100644 ---- a/Lib/test/test_shutil.py -+++ b/Lib/test/test_shutil.py -@@ -2051,6 +2051,7 @@ - check_chown(dirname, uid, gid) - - -+@support.requires_subprocess() - class TestWhich(BaseTest, unittest.TestCase): - - def setUp(self): -@@ -3055,6 +3056,7 @@ - self.assertGreaterEqual(size.lines, 0) - - @unittest.skipUnless(os.isatty(sys.__stdout__.fileno()), "not on tty") -+ @support.requires_subprocess() - @unittest.skipUnless(hasattr(os, 'get_terminal_size'), - 'need os.get_terminal_size()') - def test_stty_match(self): -diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py -index f2ae28c38d..f1f5c56eb8 100644 ---- a/Lib/test/test_signal.py -+++ b/Lib/test/test_signal.py -@@ -12,6 +12,7 @@ - import time - import unittest - from test import support -+from test.support import is_apple_mobile - from test.support import os_helper - from test.support.script_helper import assert_python_ok, spawn_python - from test.support import threading_helper -@@ -806,7 +807,7 @@ - self.assertEqual(self.hndl_called, True) - - # Issue 3864, unknown if this affects earlier versions of freebsd also -- @unittest.skipIf(sys.platform in ('netbsd5',), -+ @unittest.skipIf(sys.platform in ('netbsd5',) or is_apple_mobile, - 'itimer not reliable (does not mix well with threading) on some BSDs.') - def test_itimer_virtual(self): - self.itimer = signal.ITIMER_VIRTUAL -@@ -1242,6 +1243,8 @@ - - @unittest.skipUnless(hasattr(signal, "setitimer"), - "test needs setitimer()") -+ @unittest.skipIf(is_apple_mobile, -+ f"{sys.platform} doesn't support setitimer().") - def test_stress_delivery_dependent(self): - """ - This test uses dependent signal handlers. -@@ -1288,6 +1291,8 @@ - - @unittest.skipUnless(hasattr(signal, "setitimer"), - "test needs setitimer()") -+ @unittest.skipIf(is_apple_mobile, -+ f"{sys.platform} doesn't support setitimer().") - def test_stress_delivery_simultaneous(self): - """ - This test uses simultaneous signal handlers. -@@ -1321,6 +1326,7 @@ - @unittest.skipUnless(hasattr(signal, "SIGUSR1"), - "test needs SIGUSR1") - @threading_helper.requires_working_threading() -+ @unittest.skipIf(is_apple_mobile, "too stressful for iOS") - def test_stress_modifying_handlers(self): - # bpo-43406: race condition between trip_signal() and signal.signal - signum = signal.SIGUSR1 -diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py -index 86701caf05..155c4c458c 100644 ---- a/Lib/test/test_socket.py -+++ b/Lib/test/test_socket.py -@@ -1,5 +1,6 @@ - import unittest - from test import support -+from test.support import is_apple_mobile - from test.support import os_helper - from test.support import socket_helper - from test.support import threading_helper -@@ -1154,7 +1155,7 @@ - # I've ordered this by protocols that have both a tcp and udp - # protocol, at least for modern Linuxes. - if (sys.platform.startswith(('freebsd', 'netbsd', 'gnukfreebsd')) -- or sys.platform in ('linux', 'darwin')): -+ or sys.platform in ('linux', 'darwin') or is_apple_mobile): - # avoid the 'echo' service on this platform, as there is an - # assumption breaking non-standard port/protocol entry - services = ('daytime', 'qotd', 'domain') -@@ -3665,7 +3666,7 @@ - def _testFDPassCMSG_LEN(self): - self.createAndSendFDs(1) - -- @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") -+ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") - @unittest.skipIf(AIX, "skipping, see issue #22397") - @requireAttrs(socket, "CMSG_SPACE") - def testFDPassSeparate(self): -@@ -3676,7 +3677,7 @@ - maxcmsgs=2) - - @testFDPassSeparate.client_skip -- @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") -+ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") - @unittest.skipIf(AIX, "skipping, see issue #22397") - def _testFDPassSeparate(self): - fd0, fd1 = self.newFDs(2) -@@ -3689,7 +3690,7 @@ - array.array("i", [fd1]))]), - len(MSG)) - -- @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") -+ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") - @unittest.skipIf(AIX, "skipping, see issue #22397") - @requireAttrs(socket, "CMSG_SPACE") - def testFDPassSeparateMinSpace(self): -@@ -3703,7 +3704,7 @@ - maxcmsgs=2, ignoreflags=socket.MSG_CTRUNC) - - @testFDPassSeparateMinSpace.client_skip -- @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") -+ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") - @unittest.skipIf(AIX, "skipping, see issue #22397") - def _testFDPassSeparateMinSpace(self): - fd0, fd1 = self.newFDs(2) -@@ -3727,7 +3728,7 @@ - nbytes = self.sendmsgToServer([msg]) - self.assertEqual(nbytes, len(msg)) - -- @unittest.skipIf(sys.platform == "darwin", "see issue #24725") -+ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") - def testFDPassEmpty(self): - # Try to pass an empty FD array. Can receive either no array - # or an empty array. -diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py -index 0f62f9eb20..d2daf044d6 100644 ---- a/Lib/test/test_socketserver.py -+++ b/Lib/test/test_socketserver.py -@@ -8,12 +8,13 @@ - import select - import signal - import socket -+import sys - import threading - import unittest - import socketserver - - import test.support --from test.support import reap_children, verbose -+from test.support import is_apple_mobile, reap_children, verbose - from test.support import os_helper - from test.support import socket_helper - from test.support import threading_helper -diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py -index f3efe0f52f..ff29b98a80 100644 ---- a/Lib/test/test_sqlite3/test_dbapi.py -+++ b/Lib/test/test_sqlite3/test_dbapi.py -@@ -31,7 +31,7 @@ - - from test.support import ( - SHORT_TIMEOUT, check_disallow_instantiation, requires_subprocess, -- is_emscripten, is_wasi -+ is_apple_mobile, is_emscripten, is_wasi - ) - from test.support import gc_collect - from test.support import threading_helper -@@ -667,7 +667,7 @@ - cx.execute(self._sql) - - @unittest.skipIf(sys.platform == "win32", "skipped on Windows") -- @unittest.skipIf(sys.platform == "darwin", "skipped on macOS") -+ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipped on Apple platforms") - @unittest.skipIf(is_emscripten or is_wasi, "not supported on Emscripten/WASI") - @unittest.skipUnless(TESTFN_UNDECODABLE, "only works if there are undecodable paths") - def test_open_with_undecodable_path(self): -@@ -713,7 +713,7 @@ - cx.execute(self._sql) - - @unittest.skipIf(sys.platform == "win32", "skipped on Windows") -- @unittest.skipIf(sys.platform == "darwin", "skipped on macOS") -+ @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipped on Apple platforms") - @unittest.skipIf(is_emscripten or is_wasi, "not supported on Emscripten/WASI") - @unittest.skipUnless(TESTFN_UNDECODABLE, "only works if there are undecodable paths") - def test_open_undecodable_uri(self): -diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py -index 2a6813f00b..1a65e8f44d 100644 ---- a/Lib/test/test_sysconfig.py -+++ b/Lib/test/test_sysconfig.py -@@ -6,7 +6,7 @@ - from copy import copy - - from test.support import ( -- captured_stdout, PythonSymlink, requires_subprocess, is_wasi -+ captured_stdout, PythonSymlink, requires_subprocess, is_apple_mobile, is_wasi - ) - from test.support.import_helper import import_module - from test.support.os_helper import (TESTFN, unlink, skip_unless_symlink, -@@ -333,12 +333,14 @@ - # XXX more platforms to tests here - - @unittest.skipIf(is_wasi, "Incompatible with WASI mapdir and OOT builds") -+ @unittest.skipIf(is_apple_mobile, -+ f"{sys.platform} doesn't distribute config.h in the runtime environment") - def test_get_config_h_filename(self): - config_h = sysconfig.get_config_h_filename() - self.assertTrue(os.path.isfile(config_h), config_h) - - def test_get_scheme_names(self): -- wanted = ['nt', 'posix_home', 'posix_prefix', 'posix_venv', 'nt_venv', 'venv'] -+ wanted = ['ios', 'nt', 'posix_home', 'posix_prefix', 'posix_venv', 'nt_venv', 'venv', 'tvos', 'watchos'] - if HAS_USER_BASE: - wanted.extend(['nt_user', 'osx_framework_user', 'posix_user']) - self.assertEqual(get_scheme_names(), tuple(sorted(wanted))) -@@ -410,6 +412,8 @@ - self.assertTrue(library.startswith(f'python{major}{minor}')) - self.assertTrue(library.endswith('.dll')) - self.assertEqual(library, ldlibrary) -+ elif is_apple_mobile: -+ self.assertEqual(ldlibrary, "Python.framework/Python") ++ elif sys.platform == "tvos": ++ release = get_config_vars().get("TVOS_DEPLOYMENT_TARGET", "9.0") ++ osname = sys.platform ++ machine = sys.implementation._multiarch ++ elif sys.platform == "watchos": ++ release = get_config_vars().get("WATCHOS_DEPLOYMENT_TARGET", "4.0") + osname = sys.platform + machine = sys.implementation._multiarch else: - self.assertTrue(library.startswith(f'libpython{major}.{minor}')) - self.assertTrue(library.endswith('.a')) -@@ -460,6 +464,8 @@ - self.assertEqual(my_platform, test_platform) - - @unittest.skipIf(is_wasi, "Incompatible with WASI mapdir and OOT builds") -+ @unittest.skipIf(is_apple_mobile, -+ f"{sys.platform} doesn't distribute config.h in the runtime environment") - def test_srcdir(self): - # See Issues #15322, #15364. - srcdir = sysconfig.get_config_var('srcdir') -@@ -526,6 +532,8 @@ - @unittest.skipIf(sys.platform.startswith('win'), - 'Test is not Windows compatible') - @unittest.skipIf(is_wasi, "Incompatible with WASI mapdir and OOT builds") -+ @unittest.skipIf(is_apple_mobile, -+ f"{sys.platform} doesn't distribute config.h in the runtime environment") - def test_get_makefile_filename(self): - makefile = sysconfig.get_makefile_filename() - self.assertTrue(os.path.isfile(makefile), makefile) -diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py -index 00a64372b3..539db5d7d7 100644 ---- a/Lib/test/test_threading.py -+++ b/Lib/test/test_threading.py -@@ -3,7 +3,7 @@ - """ - - import test.support --from test.support import threading_helper, requires_subprocess -+from test.support import threading_helper, requires_subprocess, is_apple_mobile - from test.support import verbose, cpython_only, os_helper - from test.support.import_helper import import_module - from test.support.script_helper import assert_python_ok, assert_python_failure -@@ -1250,6 +1250,7 @@ - os.set_blocking(r, False) - return (r, w) - -+ @unittest.skipIf(is_apple_mobile, "%s doesn't have os.pipe" % sys.platform) - def test_threads_join(self): - # Non-daemon threads should be joined at subinterpreter shutdown - # (issue #18808) -@@ -1278,6 +1279,7 @@ - # The thread was joined properly. - self.assertEqual(os.read(r, 1), b"x") - -+ @unittest.skipIf(is_apple_mobile, "%s doesn't have os.pipe" % sys.platform) - def test_threads_join_2(self): - # Same as above, but a delay gets introduced after the thread's - # Python code returned but before the thread state is deleted. -diff --git a/Lib/test/test_unicode_file_functions.py b/Lib/test/test_unicode_file_functions.py -index 47619c8807..3f36be1a49 100644 ---- a/Lib/test/test_unicode_file_functions.py -+++ b/Lib/test/test_unicode_file_functions.py -@@ -5,7 +5,7 @@ - import unittest - import warnings - from unicodedata import normalize --from test.support import os_helper -+from test.support import is_apple_mobile, os_helper - from test import support - - -@@ -23,13 +23,13 @@ - '10_\u1fee\u1ffd', - ] - --# Mac OS X decomposes Unicode names, using Normal Form D. -+# Apple platforms decompose Unicode names, using Normal Form D. - # http://developer.apple.com/mac/library/qa/qa2001/qa1173.html - # "However, most volume formats do not follow the exact specification for - # these normal forms. For example, HFS Plus uses a variant of Normal Form D - # in which U+2000 through U+2FFF, U+F900 through U+FAFF, and U+2F800 through - # U+2FAFF are not decomposed." --if sys.platform != 'darwin': -+if sys.platform != 'darwin' and not is_apple_mobile: - filenames.extend([ - # Specific code points: NFC(fn), NFD(fn), NFKC(fn) and NFKD(fn) all different - '11_\u0385\u03d3\u03d4', -@@ -123,7 +123,7 @@ - # NFD (a variant of Unicode NFD form). Normalize the filename to NFC, NFKC, - # NFKD in Python is useless, because darwin will normalize it later and so - # open(), os.stat(), etc. don't raise any exception. -- @unittest.skipIf(sys.platform == 'darwin', 'irrelevant test on Mac OS X') -+ @unittest.skipIf(sys.platform == 'darwin' or is_apple_mobile, 'irrelevant test on Apple platforms') - @unittest.skipIf( - support.is_emscripten or support.is_wasi, - "test fails on Emscripten/WASI when host platform is macOS." -@@ -145,7 +145,7 @@ - # Skip the test on darwin, because darwin uses a normalization different - # than Python NFD normalization: filenames are different even if we use - # Python NFD normalization. -- @unittest.skipIf(sys.platform == 'darwin', 'irrelevant test on Mac OS X') -+ @unittest.skipIf(sys.platform == 'darwin' or is_apple_mobile, 'irrelevant test on Apple platforms') - def test_listdir(self): - sf0 = set(self.files) - with warnings.catch_warnings(): -diff --git a/Lib/test/test_unittest/test_break.py b/Lib/test/test_unittest/test_break.py -index 1da98af3e7..d413d5a478 100644 ---- a/Lib/test/test_unittest/test_break.py -+++ b/Lib/test/test_unittest/test_break.py -@@ -11,6 +11,8 @@ - - @unittest.skipUnless(hasattr(os, 'kill'), "Test requires os.kill") - @unittest.skipIf(sys.platform =="win32", "Test cannot run on Windows") -+@unittest.skipIf(support.is_apple_mobile, -+ f"{sys.platform} doesn't support SIGINT.") - class TestBreak(unittest.TestCase): - int_handler = None - # This number was smart-guessed, previously tests were failing -diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py -index 99c9e24994..fa528a6758 100644 ---- a/Lib/test/test_urllib2.py -+++ b/Lib/test/test_urllib2.py -@@ -1,6 +1,7 @@ - import unittest - from test import support - from test.support import os_helper -+from test.support import requires_subprocess - from test.support import warnings_helper - from test import test_urllib - from unittest import mock -@@ -998,6 +999,7 @@ - - file_obj.close() - -+ @requires_subprocess() - def test_http_body_pipe(self): - # A file reading from a pipe. - # A pipe cannot be seek'ed. There is no way to determine the -diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py -index 890672c5d2..dce68e1e40 100644 ---- a/Lib/test/test_venv.py -+++ b/Lib/test/test_venv.py -@@ -19,7 +19,7 @@ - import tempfile - from test.support import (captured_stdout, captured_stderr, - skip_if_broken_multiprocessing_synchronize, verbose, -- requires_subprocess, is_emscripten, is_wasi, -+ requires_subprocess, is_apple_mobile, is_emscripten, is_wasi, - requires_venv_with_pip, TEST_HOME_DIR, - requires_resource, copy_python_src_ignore) - from test.support.os_helper import (can_symlink, EnvironmentVarGuard, rmtree) -@@ -41,6 +41,8 @@ - - if is_emscripten or is_wasi: - raise unittest.SkipTest("venv is not available on Emscripten/WASI.") -+if is_apple_mobile: -+ raise unittest.SkipTest("venv is not available on mobile Apple platforms.") - - @requires_subprocess() - def check_output(cmd, encoding=None): -diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py -index 8b0628745c..2d8de8aecb 100755 ---- a/Lib/webbrowser.py -+++ b/Lib/webbrowser.py -@@ -541,6 +541,57 @@ - - # what to do if _tryorder is now empty? - -+# -+# Platform support for iOS -+# -+if sys.platform == 'ios': -+ class MobileSafari(BaseBrowser): -+ def open(self, url, new=0, autoraise=True): -+ # This code is the equivalent of: -+ # NSURL *nsurl = [NSURL URLWithString:url]; -+ # [[UIApplication sharedApplication] openURL:nsurl]; -+ from ctypes import cdll, c_void_p, c_char_p, c_uint32 -+ from ctypes import util -+ objc = cdll.LoadLibrary(util.find_library(b'objc')) -+ cf = cdll.LoadLibrary(util.find_library(b'CoreFoundation')) -+ objc.objc_getClass.restype = c_void_p -+ objc.objc_getClass.argtypes = [c_char_p] -+ objc.sel_registerName.restype = c_void_p -+ objc.sel_registerName.argtypes = [c_char_p] -+ cf.CFStringCreateWithCString.restype = c_void_p -+ cf.CFStringCreateWithCString.argtypes = [c_void_p, c_char_p, c_uint32] -+ -+ # Get an NSString describing the URL -+ kCFStringEncodingUTF8 = 0x08000100 -+ url = c_void_p(cf.CFStringCreateWithCString(None, url.encode('utf-8'), kCFStringEncodingUTF8)) -+ autorelease = c_void_p(objc.sel_registerName(b'autorelease')) -+ objc.objc_msgSend.argtypes = [c_void_p, c_void_p] -+ objc.objc_msgSend.restype = c_void_p -+ objc.objc_msgSend(url, autorelease) -+ -+ # Get an NSURL object representing the URL -+ NSURL = c_void_p(objc.objc_getClass(b'NSURL')) -+ urlWithString_ = c_void_p(objc.sel_registerName(b'URLWithString:')) -+ objc.objc_msgSend.restype = c_void_p -+ objc.objc_msgSend.argtypes = [c_void_p, c_void_p, c_void_p] -+ nsurl = c_void_p(objc.objc_msgSend(NSURL, urlWithString_, url)) -+ -+ # Get the shared UIApplication instance -+ UIApplication = c_void_p(objc.objc_getClass(b'UIApplication')) -+ sharedApplication = c_void_p(objc.sel_registerName(b'sharedApplication')) -+ objc.objc_msgSend.argtypes = [c_void_p, c_void_p] -+ objc.objc_msgSend.restype = c_void_p -+ shared_app = c_void_p(objc.objc_msgSend(UIApplication, sharedApplication)) -+ -+ # Open the URL on the shared application -+ openURL_ = c_void_p(objc.sel_registerName(b'openURL:')) -+ objc.objc_msgSend.argtypes = [c_void_p, c_void_p, c_void_p] -+ objc.objc_msgSend.restype = None -+ objc.objc_msgSend(shared_app, openURL_, nsurl) -+ -+ return True -+ -+ register("mobilesafari", None, MobileSafari(), preferred=True) - - # - # Platform support for Windows +--- /dev/null ++++ b/Mac/Resources/app-store-compliance.patch +@@ -0,0 +1,29 @@ ++diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py ++index d6c83a75c1c..19ed4e01091 100644 ++--- a/Lib/test/test_urlparse.py +++++ b/Lib/test/test_urlparse.py ++@@ -237,11 +237,6 @@ def test_roundtrips(self): ++ '','',''), ++ ('git+ssh', 'git@github.com','/user/project.git', ++ '', '')), ++- ('itms-services://?action=download-manifest&url=https://example.com/app', ++- ('itms-services', '', '', '', ++- 'action=download-manifest&url=https://example.com/app', ''), ++- ('itms-services', '', '', ++- 'action=download-manifest&url=https://example.com/app', '')), ++ ('+scheme:path/to/file', ++ ('', '', '+scheme:path/to/file', '', '', ''), ++ ('', '', '+scheme:path/to/file', '', '')), ++diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py ++index 8f724f907d4..148caf742c9 100644 ++--- a/Lib/urllib/parse.py +++++ b/Lib/urllib/parse.py ++@@ -59,7 +59,7 @@ ++ 'imap', 'wais', 'file', 'mms', 'https', 'shttp', ++ 'snews', 'prospero', 'rtsp', 'rtsps', 'rtspu', 'rsync', ++ 'svn', 'svn+ssh', 'sftp', 'nfs', 'git', 'git+ssh', ++- 'ws', 'wss', 'itms-services'] +++ 'ws', 'wss'] ++ ++ uses_params = ['', 'ftp', 'hdl', 'prospero', 'http', 'imap', ++ 'https', 'shttp', 'rtsp', 'rtsps', 'rtspu', 'sip', diff --git a/Makefile.pre.in b/Makefile.pre.in -index 4996c5c309..7571c5f4ad 100644 +index 76653f399f4..ab2a67040d1 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in -@@ -184,6 +184,8 @@ - PYTHONFRAMEWORKDIR= @PYTHONFRAMEWORKDIR@ - PYTHONFRAMEWORKPREFIX= @PYTHONFRAMEWORKPREFIX@ - PYTHONFRAMEWORKINSTALLDIR= @PYTHONFRAMEWORKINSTALLDIR@ -+PYTHONFRAMEWORKINSTALLNAMEPREFIX= @PYTHONFRAMEWORKINSTALLNAMEPREFIX@ -+RESSRCDIR= @RESSRCDIR@ - # Deployment target selected during configure, to be checked - # by distutils. The export statement is needed to ensure that the - # deployment target is active during build. -@@ -843,7 +845,7 @@ - $(BLDSHARED) $(NO_AS_NEEDED) -o $@ -Wl,-h$@ $^ - - libpython$(LDVERSION).dylib: $(LIBRARY_OBJS) -- $(CC) -dynamiclib -Wl,-single_module $(PY_CORE_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(DTRACE_OBJS) $(SHLIBS) $(LIBC) $(LIBM); \ -+ $(CC) -dynamiclib $(PY_CORE_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(PYTHONFRAMEWORKINSTALLNAMEPREFIX)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(DTRACE_OBJS) $(SHLIBS) $(LIBC) $(LIBM); \ - - - libpython$(VERSION).sl: $(LIBRARY_OBJS) -@@ -868,14 +870,13 @@ - # This rule is here for OPENSTEP/Rhapsody/MacOSX. It builds a temporary - # minimal framework (not including the Lib directory and such) in the current - # directory. --RESSRCDIR=Mac/Resources/framework - $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK): \ - $(LIBRARY) \ - $(RESSRCDIR)/Info.plist - $(INSTALL) -d -m $(DIRMODE) $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION) - $(CC) -o $(LDLIBRARY) $(PY_CORE_LDFLAGS) -dynamiclib \ -- -all_load $(LIBRARY) -Wl,-single_module \ -- -install_name $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK) \ -+ -all_load $(LIBRARY) \ -+ -install_name $(PYTHONFRAMEWORKINSTALLNAMEPREFIX)/Versions/$(VERSION)/$(PYTHONFRAMEWORK) \ - -compatibility_version $(VERSION) \ - -current_version $(VERSION) \ - -framework CoreFoundation $(LIBS); -@@ -887,6 +888,21 @@ - $(LN) -fsn Versions/Current/$(PYTHONFRAMEWORK) $(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK) - $(LN) -fsn Versions/Current/Resources $(PYTHONFRAMEWORKDIR)/Resources - -+# This rule is for iOS, which requires an annoyingly just slighly different -+# format for frameworks to macOS. It *doesn't* use a versioned framework, and -+# the Info.plist must be in the root of the framework. -+$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK): \ -+ $(LIBRARY) \ -+ $(RESSRCDIR)/Info.plist -+ $(INSTALL) -d -m $(DIRMODE) $(PYTHONFRAMEWORKDIR) -+ $(CC) -o $(LDLIBRARY) $(PY_CORE_LDFLAGS) -dynamiclib \ -+ -all_load $(LIBRARY) \ -+ -install_name $(PYTHONFRAMEWORKINSTALLNAMEPREFIX)/$(PYTHONFRAMEWORK) \ -+ -compatibility_version $(VERSION) \ -+ -current_version $(VERSION) \ -+ -framework CoreFoundation $(LIBS); -+ $(INSTALL_DATA) $(RESSRCDIR)/Info.plist $(PYTHONFRAMEWORKDIR)/Info.plist -+ - # This rule builds the Cygwin Python DLL and import library if configured - # for a shared core library; otherwise, this rule is a noop. - $(DLLLIBRARY) libpython$(LDVERSION).dll.a: $(LIBRARY_OBJS) -@@ -1885,6 +1901,21 @@ - $(RUNSHARED) /usr/libexec/oah/translate \ - ./$(BUILDPYTHON) -E -m test -j 0 -u all $(TESTOPTS) - -+# Run the test suite on the iOS simulator. -+# Must be run on a macOS machine with a full Xcode install that has an iPhone SE -+# (3rd edition) simulator available, after running `make install` on a configuration -+# with --enable-framework="./Tools/iOSTestbed/Python.xcframework/ios-arm64_x86_64-simulator" -+XCRESULT=./build/$(MULTIARCH).$(shell date +%s).xcresult -+.PHONY: testiOS -+testiOS: -+ # Run the test suite for the Xcode project, targeting the iOS simulator. -+ # If the suite fails, extract and print the console output, then re-raise the failure -+ if ! xcodebuild test -project ./Tools/iOSTestbed/iOSTestbed.xcodeproj -scheme "iOSTestbed" -destination "platform=iOS Simulator,name=iPhone SE (3rd Generation)" -resultBundlePath $(XCRESULT) ; then \ -+ xcrun xcresulttool get --path $(XCRESULT) --id $$(xcrun xcresulttool get --path $(XCRESULT) --format json | $(PYTHON_FOR_BUILD) -c "import sys, json; result = json.load(sys.stdin); print(result['actions']['_values'][0]['actionResult']['logRef']['id']['_value'])"); \ -+ echo ; \ -+ exit 1; \ -+ fi -+ - # Like test, but using --slow-ci which enables all test resources and use - # longer timeout. Run an optional pybuildbot.identify script to include - # information about the build environment. -@@ -1924,7 +1955,7 @@ - # which can lead to two parallel `./python setup.py build` processes that - # step on each others toes. - .PHONY: install --install: @FRAMEWORKINSTALLFIRST@ commoninstall bininstall maninstall @FRAMEWORKINSTALLLAST@ -+install: @FRAMEWORKINSTALLFIRST@ @INSTALLTARGETS@ @FRAMEWORKINSTALLLAST@ - if test "x$(ENSUREPIP)" != "xno" ; then \ - case $(ENSUREPIP) in \ - upgrade) ensurepip="--upgrade" ;; \ -@@ -2507,20 +2538,36 @@ +@@ -179,6 +179,9 @@ + EXE= @EXEEXT@ + BUILDEXE= @BUILDEXEEXT@ + ++# Name of the patch file to apply for app store compliance ++APP_STORE_COMPLIANCE_PATCH=@APP_STORE_COMPLIANCE_PATCH@ ++ + # Short name and location for Mac OS X Python framework + UNIVERSALSDK=@UNIVERSALSDK@ + PYTHONFRAMEWORK= @PYTHONFRAMEWORK@ +@@ -692,7 +695,7 @@ + @grep -E '^[A-Za-z][-A-Za-z0-9]+:' Makefile | awk -F : '{print $$1}' + + .PHONY: build_all +-build_all: check-clean-src $(BUILDPYTHON) platform sharedmods \ ++build_all: check-clean-src check-app-store-compliance $(BUILDPYTHON) platform sharedmods \ + gdbhooks Programs/_testembed scripts checksharedmods rundsymutil + + .PHONY: build_wasm +@@ -715,6 +718,16 @@ exit 1; \ - else true; \ fi -- @for i in $(prefix)/Resources/English.lproj $(prefix)/lib; do\ -- if test ! -d $(DESTDIR)$$i; then \ -- echo "Creating directory $(DESTDIR)$$i"; \ -- $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$$i; \ -- else true; \ -+ # iOS/tvOS/watchOS uses a non-versioned framework with Info.plist in the -+ # framework root, no .lproj data, and binaries -+ @if test "$(MACHDEP)" = ios -o "$(MACHDEP)" = tvos -o "$(MACHDEP)" = watchos; then \ -+ if test -d $(PYTHONFRAMEWORKPREFIX)/include; then \ -+ echo "Clearing stale header symlink directory"; \ -+ rm -rf $(PYTHONFRAMEWORKPREFIX)/include; \ - fi; \ -- done -- $(LN) -fsn include/python$(LDVERSION) $(DESTDIR)$(prefix)/Headers -- sed 's/%VERSION%/'"`$(RUNSHARED) ./$(BUILDPYTHON) -c 'import platform; print(platform.python_version())'`"'/g' < $(RESSRCDIR)/Info.plist > $(DESTDIR)$(prefix)/Resources/Info.plist -- $(LN) -fsn $(VERSION) $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Versions/Current -- $(LN) -fsn Versions/Current/$(PYTHONFRAMEWORK) $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/$(PYTHONFRAMEWORK) -- $(LN) -fsn Versions/Current/Headers $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Headers -- $(LN) -fsn Versions/Current/Resources $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Resources -- $(INSTALL_SHARED) $(LDLIBRARY) $(DESTDIR)$(PYTHONFRAMEWORKPREFIX)/$(LDLIBRARY) -+ $(INSTALL) -d -m $(DIRMODE) $(PYTHONFRAMEWORKINSTALLDIR); \ -+ sed 's/%VERSION%/'"`$(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import platform; print(platform.python_version())'`"'/g' < $(RESSRCDIR)/Info.plist > $(PYTHONFRAMEWORKINSTALLDIR)/Info.plist; \ -+ $(INSTALL_SHARED) $(LDLIBRARY) $(PYTHONFRAMEWORKPREFIX)/$(LDLIBRARY); \ -+ $(INSTALL) -d -m $(DIRMODE) $(BINDIR); \ -+ for file in $(RESSRCDIR)/bin/* ; do \ -+ $(INSTALL) -m $(EXEMODE) $$file $(BINDIR); \ -+ done; \ -+ else \ -+ for i in $(prefix)/Resources/English.lproj $(prefix)/lib; do \ -+ if test ! -d $(DESTDIR)$$i; then \ -+ echo "Creating directory $(DESTDIR)$$i"; \ -+ $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$$i; \ -+ else true; \ -+ fi; \ -+ done; \ -+ $(LN) -fsn include/python$(LDVERSION) $(DESTDIR)$(prefix)/Headers; \ -+ sed 's/%VERSION%/'"`$(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import platform; print(platform.python_version())'`"'/g' < $(RESSRCDIR)/Info.plist > $(DESTDIR)$(prefix)/Resources/Info.plist; \ -+ $(LN) -fsn $(VERSION) $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Versions/Current; \ -+ $(LN) -fsn Versions/Current/$(PYTHONFRAMEWORK) $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/$(PYTHONFRAMEWORK); \ -+ $(LN) -fsn Versions/Current/Headers $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Headers; \ -+ $(LN) -fsn Versions/Current/Resources $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Resources; \ -+ $(INSTALL_SHARED) $(LDLIBRARY) $(DESTDIR)$(PYTHONFRAMEWORKPREFIX)/$(LDLIBRARY); \ -+ fi - - # This installs Mac/Lib into the framework - # Install a number of symlinks to keep software that expects a normal unix -@@ -2562,6 +2609,19 @@ - frameworkinstallextras: - cd Mac && $(MAKE) installextras DESTDIR="$(DESTDIR)" -+# On iOS, bin/lib can't live inside the framework; include needs to be called -+# "Headers", but *must* be in the framework, and *not* include the `python3.X` -+# subdirectory. The install has put these folders in the same folder as -+# Python.framework; Move the headers to their final framework-compatible home. -+.PHONY: frameworkinstallmobileheaders -+frameworkinstallmobileheaders: -+ if test -d $(PYTHONFRAMEWORKINSTALLDIR)/Headers; then \ -+ echo "Removing old framework headers"; \ -+ rm -rf $(PYTHONFRAMEWORKINSTALLDIR)/Headers; \ ++# Check that the app store compliance patch can be applied (if configured). ++# This is checked as a dry-run against the original library sources; ++# the patch will be actually applied during the install phase. ++.PHONY: check-app-store-compliance ++check-app-store-compliance: ++ @if [ "$(APP_STORE_COMPLIANCE_PATCH)" != "" ]; then \ ++ patch --dry-run --quiet --force --strip 1 --directory "$(abs_srcdir)" --input "$(abs_srcdir)/$(APP_STORE_COMPLIANCE_PATCH)"; \ ++ echo "App store compliance patch can be applied."; \ + fi -+ mv "$(PYTHONFRAMEWORKPREFIX)/include/python$(VERSION)" "$(PYTHONFRAMEWORKINSTALLDIR)/Headers" -+ $(LN) -fs "$(PYTHONFRAMEWORKDIR)" "$(PYTHONFRAMEWORKPREFIX)/include/python$(VERSION)" + - # Build the toplevel Makefile - Makefile.pre: $(srcdir)/Makefile.pre.in config.status - CONFIG_FILES=Makefile.pre CONFIG_HEADERS= ./config.status + # Profile generation build must start from a clean tree. + profile-clean-stamp: + $(MAKE) clean +@@ -2568,6 +2581,14 @@ + $(INSTALL_DATA) `cat pybuilddir.txt`/_sysconfigdata_$(ABIFLAGS)_$(MACHDEP)_$(MULTIARCH).py \ + $(DESTDIR)$(LIBDEST); \ + $(INSTALL_DATA) $(srcdir)/LICENSE $(DESTDIR)$(LIBDEST)/LICENSE.txt ++ @ # If app store compliance has been configured, apply the patch to the ++ @ # installed library code. The patch has been previously validated against ++ @ # the original source tree, so we can ignore any errors that are raised ++ @ # due to files that are missing because of --disable-test-modules etc. ++ @if [ "$(APP_STORE_COMPLIANCE_PATCH)" != "" ]; then \ ++ echo "Applying app store compliance patch"; \ ++ patch --force --reject-file "$(abs_builddir)/app-store-compliance.rej" --strip 2 --directory "$(DESTDIR)$(LIBDEST)" --input "$(abs_srcdir)/$(APP_STORE_COMPLIANCE_PATCH)" || true ; \ ++ fi + @ # Build PYC files for the 3 optimization levels (0, 1, 2) + -PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ + $(PYTHON_FOR_BUILD) -Wi $(DESTDIR)$(LIBDEST)/compileall.py \ diff --git a/Misc/platform_triplet.c b/Misc/platform_triplet.c -index 3307260544..b5db9e8a80 100644 +index ec0857a4a99..2350e9dc821 100644 --- a/Misc/platform_triplet.c +++ b/Misc/platform_triplet.c -@@ -233,7 +233,42 @@ - # error unknown platform triplet - # endif - #elif defined(__APPLE__) -+# include "TargetConditionals.h" -+# if TARGET_OS_IOS -+# if TARGET_OS_SIMULATOR +@@ -257,6 +257,26 @@ + # 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=iphonesimulator-x86_64 ++PLATFORM_TRIPLET=x86_64-appletvsimulator +# else -+PLATFORM_TRIPLET=iphonesimulator-arm64 ++PLATFORM_TRIPLET=arm64-appletvsimulator +# endif +# else -+PLATFORM_TRIPLET=iphoneos-arm64 ++PLATFORM_TRIPLET=arm64-appletvos +# endif -+# elif TARGET_OS_TV -+# if TARGET_OS_SIMULATOR ++# elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH ++# if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR +# if __x86_64__ -+PLATFORM_TRIPLET=appletvsimulator-x86_64 ++PLATFORM_TRIPLET=x86_64-watchsimulator +# else -+PLATFORM_TRIPLET=appletvsimulator-arm64 ++PLATFORM_TRIPLET=arm64-watchsimulator +# endif +# else -+PLATFORM_TRIPLET=appletvos-arm64 ++PLATFORM_TRIPLET=arm64_32-watchos +# endif -+# elif TARGET_OS_WATCH -+# if TARGET_OS_SIMULATOR -+# if __x86_64__ -+PLATFORM_TRIPLET=watchsimulator-x86_64 -+# else -+PLATFORM_TRIPLET=watchsimulator-arm64 -+# endif -+# else -+PLATFORM_TRIPLET=watchos-arm64_32 -+# endif -+# elif TARGET_OS_OSX + // Older macOS SDKs do not define TARGET_OS_OSX + # elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX PLATFORM_TRIPLET=darwin -+# else -+# error unknown Apple platform -+# endif - #elif defined(__VXWORKS__) - PLATFORM_TRIPLET=vxworks - #elif defined(__wasm32__) -diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c -index 2898eedc3e..b48a143c34 100644 ---- a/Modules/_posixsubprocess.c -+++ b/Modules/_posixsubprocess.c -@@ -33,10 +33,20 @@ - - #include "posixmodule.h" - -+#if defined(__APPLE__) -+#include "TargetConditionals.h" -+#endif -+ - #ifdef _Py_MEMORY_SANITIZER - # include - #endif - -+// iOS/tvOS/watchOS *define* a number of POSIX functions, but you can't use them -+// because they aren't conventional multiprocess environments. -+#if TARGET_OS_IPHONE -+# undef HAVE_FORK -+#endif -+ - #if defined(__ANDROID__) && __ANDROID_API__ < 21 && !defined(SYS_getdents64) - # include - # define SYS_getdents64 __NR_getdents64 -@@ -810,11 +820,16 @@ - saved_errno = 0; - for (i = 0; exec_array[i] != NULL; ++i) { - const char *executable = exec_array[i]; -+ -+#if TARGET_OS_TV || TARGET_OS_WATCH -+ errno = ENOTSUP; -+#else - if (envp) { - execve(executable, argv, envp); - } else { - execv(executable, argv); - } -+#endif /* TARGET_OS_TV || TARGET_OS_WATCH */ - if (errno != ENOENT && errno != ENOTDIR && saved_errno == 0) { - saved_errno = errno; - } -@@ -880,7 +895,9 @@ - PyObject *preexec_fn, - PyObject *preexec_fn_args_tuple) - { -- -+/* iOS/tvOS/watchOS define the existence of fork, but it cannot be invoked; -+ * so fail fast if any attempt is made to invoke fork_exec */ -+#ifdef HAVE_FORK - pid_t pid; - - #ifdef VFORK_USABLE -@@ -915,7 +932,7 @@ - pid = fork(); - } - } else --#endif -+#endif /* VFORK_USABLE */ - { - pid = fork(); - } -@@ -948,6 +965,9 @@ - preexec_fn, preexec_fn_args_tuple); - _exit(255); - return 0; /* Dead code to avoid a potential compiler warning. */ -+#else /* HAVE_FORK */ -+ return -1; -+#endif /* HAVE_FORK */ - } - - /*[clinic input] -@@ -1028,6 +1048,10 @@ - int *c_fds_to_keep = NULL; - Py_ssize_t fds_to_keep_len = PyTuple_GET_SIZE(py_fds_to_keep); - -+/* iOS/tvOS/watchOS define the existence of fork, but it cannot be invoked; -+ * so fail fast if any attempt is made to invoke fork_exec */ -+#ifdef HAVE_FORK -+ - PyInterpreterState *interp = _PyInterpreterState_GET(); - if ((preexec_fn != Py_None) && interp->finalizing) { - PyErr_SetString(PyExc_RuntimeError, -@@ -1225,7 +1249,7 @@ - } - old_sigmask = &old_sigs; - } --#endif -+#endif /* VFORK_USABLE */ - - pid = do_fork_exec(exec_array, argv, envp, cwd, - p2cread, p2cwrite, c2pread, c2pwrite, -@@ -1258,7 +1282,7 @@ - * the thread signal mask. */ - (void) pthread_sigmask(SIG_SETMASK, old_sigmask, NULL); - } --#endif -+#endif /* VFORK_USABLE */ - - if (need_after_fork) - PyOS_AfterFork_Parent(); -@@ -1292,6 +1316,10 @@ - } - - return pid == -1 ? NULL : PyLong_FromPid(pid); -+ -+#else /* HAVE_FORK */ -+ return NULL; -+#endif - } - - /* module level code ********************************************************/ -diff --git a/Modules/getpath.c b/Modules/getpath.c -index 6f76a84e78..e91272f833 100644 ---- a/Modules/getpath.c -+++ b/Modules/getpath.c -@@ -758,7 +758,7 @@ - return winmodule_to_dict(dict, key, PyWin_DLLhModule); - } - #endif --#elif defined(WITH_NEXT_FRAMEWORK) -+#elif defined(WITH_NEXT_FRAMEWORK) && !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR - static char modPath[MAXPATHLEN + 1]; - static int modPathInitialized = -1; - if (modPathInitialized < 0) { -diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c -index a4d9466559..8f51bef22d 100644 ---- a/Modules/mathmodule.c -+++ b/Modules/mathmodule.c -@@ -199,6 +199,10 @@ - } - - -+#ifdef __APPLE__ -+# include "TargetConditionals.h" -+#endif /* __APPLE__ */ -+ - /* - sin(pi*x), giving accurate results for all finite x (especially x - integral or close to an integer). This is here for use in the -diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c -index 650ae4bbd6..f1d90ea23b 100644 ---- a/Modules/posixmodule.c -+++ b/Modules/posixmodule.c -@@ -92,6 +92,8 @@ - - #include - -+#include "TargetConditionals.h" -+ - #if defined(__has_builtin) - #if __has_builtin(__builtin_available) - #define HAVE_BUILTIN_AVAILABLE 1 -@@ -369,6 +371,11 @@ - # define fsync _commit - #endif /* ! __WATCOMC__ || __QNX__ */ - -+// iOS/tvOS/watchOS *define* getgroups, but it returns an error if used. -+#if TARGET_OS_IPHONE -+# undef HAVE_GETGROUPS -+#endif -+ - /*[clinic input] - # one of the few times we lie about this name! - module os -@@ -1548,7 +1555,9 @@ - */ - #include - #elif !defined(_MSC_VER) && (!defined(__WATCOMC__) || defined(__QNX__) || defined(__VXWORKS__)) -+# if !TARGET_OS_TV && !TARGET_OS_WATCH - extern char **environ; -+# endif - #endif /* !_MSC_VER */ - - static PyObject * -@@ -1564,6 +1573,7 @@ - d = PyDict_New(); - if (d == NULL) - return NULL; -+#if !TARGET_OS_TV && !TARGET_OS_WATCH - #ifdef MS_WINDOWS - /* _wenviron must be initialized in this way if the program is started - through main() instead of wmain(). */ -@@ -1617,6 +1627,7 @@ - Py_DECREF(k); - Py_DECREF(v); - } -+#endif /* !TARGET_OS_TV && !TARGET_OS_WATCH */ - return d; - } - -@@ -5750,6 +5761,9 @@ - /*[clinic end generated code: output=290fc437dd4f33a0 input=86a58554ba6094af]*/ - { - long result; -+#if TARGET_OS_IPHONE -+ result = -1; -+#else - const char *bytes = PyBytes_AsString(command); - - if (PySys_Audit("os.system", "(O)", command) < 0) { -@@ -5759,6 +5773,7 @@ - Py_BEGIN_ALLOW_THREADS - result = system(bytes); - Py_END_ALLOW_THREADS -+#endif /* TARGET_OS_IPHONE */ - return result; - } - #endif -@@ -15000,6 +15015,7 @@ - int is_symlink; - int need_stat; - #endif -+#if !TARGET_OS_TV && !TARGET_OS_WATCH - #ifdef MS_WINDOWS - unsigned long dir_bits; - #endif -@@ -15060,6 +15076,7 @@ - #endif - - return result; -+#endif /* !TARGET_OS_TV && !TARGET_OS_WATCH */ - - error: - Py_XDECREF(st_mode); -diff --git a/Modules/pwdmodule.c b/Modules/pwdmodule.c -index b7034369c4..a7d63abe5d 100644 ---- a/Modules/pwdmodule.c -+++ b/Modules/pwdmodule.c -@@ -1,6 +1,10 @@ - - /* UNIX password file access module */ - -+#ifdef __APPLE__ -+# include "TargetConditionals.h" -+#endif /* __APPLE__ */ -+ - #include "Python.h" - #include "posixmodule.h" - -@@ -183,6 +187,22 @@ - if (nomem == 1) { - return PyErr_NoMemory(); - } -+ -+// iPhone has a "user" with UID 501, username "mobile"; but the simulator -+// doesn't reflect this. Generate a simulated response. -+#if TARGET_IPHONE_SIMULATOR -+ if (uid == 501) { -+ struct passwd mp; -+ mp.pw_name = "mobile"; -+ mp.pw_passwd = "/smx7MYTQIi2M"; -+ mp.pw_uid = 501; -+ mp.pw_gid = 501; -+ mp.pw_gecos = "Mobile User"; -+ mp.pw_dir = "/var/mobile"; -+ mp.pw_shell = "/bin/sh"; -+ return mkpwent(module, &mp); -+ } -+#endif - PyObject *uid_obj = _PyLong_FromUid(uid); - if (uid_obj == NULL) - return NULL; -@@ -266,6 +286,22 @@ - PyErr_NoMemory(); - } - else { -+// iPhone has a "user" with UID 501, username "mobile"; but the simulator -+// doesn't reflect this. Generate a simulated response. -+#if TARGET_IPHONE_SIMULATOR -+ if (strcmp(name, "mobile") == 0) { -+ struct passwd mp; -+ mp.pw_name = "mobile"; -+ mp.pw_passwd = "/smx7MYTQIi2M"; -+ mp.pw_uid = 501; -+ mp.pw_gid = 501; -+ mp.pw_gecos = "Mobile User"; -+ mp.pw_dir = "/var/mobile"; -+ mp.pw_shell = "/bin/sh"; -+ retval = mkpwent(module, &mp); -+ goto out; -+ } -+#endif - PyErr_Format(PyExc_KeyError, - "getpwnam(): name not found: %R", name); - } -diff --git a/Modules/timemodule.c b/Modules/timemodule.c -index 6a872a285d..197eadf55f 100644 ---- a/Modules/timemodule.c -+++ b/Modules/timemodule.c -@@ -59,6 +59,10 @@ - # define HAVE_CLOCK_GETTIME_RUNTIME 1 - #endif - -+// iOS/tvOS/watchOS *define* clock_settime, but it can't be used -+#if TARGET_OS_IPHONE -+# undef HAVE_CLOCK_SETTIME -+#endif - - #define SEC_TO_NS (1000 * 1000 * 1000) - -@@ -113,6 +117,11 @@ - } - - -+#ifdef __APPLE__ -+# include "TargetConditionals.h" -+#endif /* __APPLE__ */ -+ -+ - /* Forward declarations */ - static int pysleep(_PyTime_t timeout); - -@@ -337,11 +346,13 @@ - return NULL; - } - -+#if !TARGET_OS_IPHONE - ret = clock_settime((clockid_t)clk_id, &ts); - if (ret != 0) { - PyErr_SetFromErrno(PyExc_OSError); - return NULL; - } -+#endif - Py_RETURN_NONE; - } - -diff --git a/Python/bootstrap_hash.c b/Python/bootstrap_hash.c -index 92f2301a01..ef6db7a9bb 100644 ---- a/Python/bootstrap_hash.c -+++ b/Python/bootstrap_hash.c -@@ -40,6 +40,10 @@ - #endif - - -+#ifdef __APPLE__ -+# include "TargetConditionals.h" -+#endif /* __APPLE__ */ -+ - #ifdef Py_DEBUG - int _Py_HashSecret_Initialized = 0; - #else -@@ -185,6 +189,9 @@ - } - - #elif defined(HAVE_GETENTROPY) -+// iOS, tvOS and watchOS have an incomplete definitions of getentropy -+// so it is *found* by configure, but doesn't actually exist. -+#elif defined(HAVE_GETENTROPY) && !TARGET_OS_IPHONE - #define PY_GETENTROPY 1 - - /* Fill buffer with size pseudo-random bytes generated by getentropy(): -diff --git a/Python/dynload_shlib.c b/Python/dynload_shlib.c -index 5a37a83805..92b632af22 100644 ---- a/Python/dynload_shlib.c -+++ b/Python/dynload_shlib.c -@@ -28,6 +28,10 @@ - #define LEAD_UNDERSCORE "" - #endif - -+#ifdef __APPLE__ -+# include "TargetConditionals.h" -+#endif /* __APPLE__ */ -+ - /* The .so extension module ABI tag, supplied by the Makefile via - Makefile.pre.in and configure. This is used to discriminate between - incompatible .so files so that extensions for different Python builds can -@@ -38,12 +42,21 @@ - #ifdef __CYGWIN__ - ".dll", - #else /* !__CYGWIN__ */ -- "." SOABI ".so", --#ifdef ALT_SOABI -- "." ALT_SOABI ".so", --#endif -- ".abi" PYTHON_ABI_STRING ".so", -- ".so", -+# ifdef __APPLE__ -+# if TARGET_OS_IPHONE -+# define SHLIB_SUFFIX ".dylib" -+# else -+# define SHLIB_SUFFIX ".so" -+# endif -+# else -+# define SHLIB_SUFFIX ".so" -+# endif -+ "." SOABI SHLIB_SUFFIX, -+# ifdef ALT_SOABI -+ "." ALT_SOABI SHLIB_SUFFIX, -+# endif -+ ".abi" PYTHON_ABI_STRING SHLIB_SUFFIX, -+ SHLIB_SUFFIX, - #endif /* __CYGWIN__ */ - NULL, - }; -diff --git a/Python/marshal.c b/Python/marshal.c -index 8940582c7f..3f2d77b307 100644 ---- a/Python/marshal.c -+++ b/Python/marshal.c -@@ -14,6 +14,10 @@ - #include "pycore_setobject.h" // _PySet_NextEntry() - #include "marshal.h" // Py_MARSHAL_VERSION - -+#ifdef __APPLE__ -+# include "TargetConditionals.h" -+#endif /* __APPLE__ */ -+ - /*[clinic input] - module marshal - [clinic start generated code]*/ -@@ -33,11 +37,15 @@ - * #if defined(MS_WINDOWS) && defined(_DEBUG) - */ - #if defined(MS_WINDOWS) --#define MAX_MARSHAL_STACK_DEPTH 1000 -+# define MAX_MARSHAL_STACK_DEPTH 1000 - #elif defined(__wasi__) --#define MAX_MARSHAL_STACK_DEPTH 1500 -+# define MAX_MARSHAL_STACK_DEPTH 1500 - #else --#define MAX_MARSHAL_STACK_DEPTH 2000 -+# if TARGET_OS_IPHONE -+# define MAX_MARSHAL_STACK_DEPTH 1500 -+# else -+# define MAX_MARSHAL_STACK_DEPTH 2000 -+# endif - #endif - - #define TYPE_NULL '0' -diff --git a/Python/sysmodule.c b/Python/sysmodule.c -index 3debe7f7c1..612ba30da1 100644 ---- a/Python/sysmodule.c -+++ b/Python/sysmodule.c -@@ -55,6 +55,10 @@ - extern const char *PyWin_DLLVersionString; - #endif - -+#if defined(__APPLE__) -+#include "TargetConditionals.h" -+#endif -+ - #ifdef __EMSCRIPTEN__ - # include - #endif -@@ -3152,6 +3156,15 @@ - goto error; - #endif - -+#if TARGET_OS_IPHONE -+# if TARGET_OS_SIMULATOR -+ res = PyDict_SetItemString(impl_info, "_simulator", Py_True); -+# else -+ res = PyDict_SetItemString(impl_info, "_simulator", Py_False); -+# endif -+ if (res < 0) -+ goto error; -+#endif - /* dict ready */ - - ns = _PyNamespace_New(impl_info); ---- /dev/null -+++ b/Tools/iOSTestbed/Python.xcframework/Info.plist -@@ -0,0 +1,44 @@ -+ -+ -+ -+ -+ 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 -+ -+ -+ CFBundlePackageType -+ XFWK -+ XCFrameworkFormatVersion -+ 1.0 -+ -+ ---- /dev/null -+++ b/Tools/iOSTestbed/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/Tools/iOSTestbed/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/Tools/iOSTestbed/iOSTestbed.xcodeproj/project.pbxproj -@@ -0,0 +1,569 @@ -+// !$*UTF8*$! -+{ -+ archiveVersion = 1; -+ classes = { -+ }; -+ objectVersion = 56; -+ objects = { -+ -+/* Begin PBXBuildFile section */ -+ 607A66172B0EFA380010BFC8 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66162B0EFA380010BFC8 /* AppDelegate.m */; }; -+ 607A66222B0EFA390010BFC8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607A66212B0EFA390010BFC8 /* Assets.xcassets */; }; -+ 607A66252B0EFA390010BFC8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607A66232B0EFA390010BFC8 /* LaunchScreen.storyboard */; }; -+ 607A66282B0EFA390010BFC8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66272B0EFA390010BFC8 /* main.m */; }; -+ 607A66322B0EFA3A0010BFC8 /* iOSTestbedTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66312B0EFA3A0010BFC8 /* iOSTestbedTests.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, ); }; }; -+ 607A66582B0F079F0010BFC8 /* dylib-Info-template.plist in Resources */ = {isa = PBXBuildFile; fileRef = 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */; }; -+/* 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 = ( -+ 607A664D2B0EFC080010BFC8 /* Python.xcframework in Embed Frameworks */, -+ ); -+ name = "Embed Frameworks"; -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+ 607A66522B0EFFE00010BFC8 /* Embed Frameworks */ = { -+ isa = PBXCopyFilesBuildPhase; -+ buildActionMask = 2147483647; -+ dstPath = ""; -+ dstSubfolderSpec = 10; -+ files = ( -+ 607A66512B0EFFE00010BFC8 /* Python.xcframework in Embed Frameworks */, -+ ); -+ name = "Embed Frameworks"; -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXCopyFilesBuildPhase section */ -+ -+/* Begin PBXFileReference section */ -+ 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 /* iOSTestbedTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iOSTestbedTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; -+ 607A66312B0EFA3A0010BFC8 /* iOSTestbedTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = iOSTestbedTests.m; sourceTree = ""; }; -+ 607A664A2B0EFB310010BFC8 /* Python.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Python.xcframework; sourceTree = ""; }; -+ 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "dylib-Info-template.plist"; sourceTree = ""; }; -+ 607A66592B0F08600010BFC8 /* iOSTestbed-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "iOSTestbed-Info.plist"; sourceTree = ""; }; -+/* End PBXFileReference section */ -+ -+/* Begin PBXFrameworksBuildPhase section */ -+ 607A660F2B0EFA380010BFC8 /* Frameworks */ = { -+ isa = PBXFrameworksBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 607A664C2B0EFC080010BFC8 /* Python.xcframework in Frameworks */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+ 607A662A2B0EFA3A0010BFC8 /* Frameworks */ = { -+ isa = PBXFrameworksBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 607A66502B0EFFE00010BFC8 /* Python.xcframework in Frameworks */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXFrameworksBuildPhase section */ -+ -+/* Begin PBXGroup section */ -+ 607A66092B0EFA380010BFC8 = { -+ isa = PBXGroup; -+ children = ( -+ 607A664A2B0EFB310010BFC8 /* Python.xcframework */, -+ 607A66142B0EFA380010BFC8 /* iOSTestbed */, -+ 607A66302B0EFA3A0010BFC8 /* iOSTestbedTests */, -+ 607A66132B0EFA380010BFC8 /* Products */, -+ 607A664F2B0EFFE00010BFC8 /* Frameworks */, -+ ); -+ sourceTree = ""; -+ }; -+ 607A66132B0EFA380010BFC8 /* Products */ = { -+ isa = PBXGroup; -+ children = ( -+ 607A66122B0EFA380010BFC8 /* iOSTestbed.app */, -+ 607A662D2B0EFA3A0010BFC8 /* iOSTestbedTests.xctest */, -+ ); -+ name = Products; -+ sourceTree = ""; -+ }; -+ 607A66142B0EFA380010BFC8 /* iOSTestbed */ = { -+ isa = PBXGroup; -+ children = ( -+ 607A66592B0F08600010BFC8 /* iOSTestbed-Info.plist */, -+ 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */, -+ 607A66152B0EFA380010BFC8 /* AppDelegate.h */, -+ 607A66162B0EFA380010BFC8 /* AppDelegate.m */, -+ 607A66212B0EFA390010BFC8 /* Assets.xcassets */, -+ 607A66232B0EFA390010BFC8 /* LaunchScreen.storyboard */, -+ 607A66272B0EFA390010BFC8 /* main.m */, -+ ); -+ path = iOSTestbed; -+ sourceTree = ""; -+ }; -+ 607A66302B0EFA3A0010BFC8 /* iOSTestbedTests */ = { -+ isa = PBXGroup; -+ children = ( -+ 607A66312B0EFA3A0010BFC8 /* iOSTestbedTests.m */, -+ ); -+ path = iOSTestbedTests; -+ sourceTree = ""; -+ }; -+ 607A664F2B0EFFE00010BFC8 /* Frameworks */ = { -+ isa = PBXGroup; -+ children = ( -+ ); -+ name = Frameworks; -+ sourceTree = ""; -+ }; -+/* End PBXGroup section */ -+ -+/* Begin PBXNativeTarget section */ -+ 607A66112B0EFA380010BFC8 /* iOSTestbed */ = { -+ isa = PBXNativeTarget; -+ 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 */, -+ 607A664E2B0EFC080010BFC8 /* Embed Frameworks */, -+ ); -+ buildRules = ( -+ ); -+ dependencies = ( -+ ); -+ name = iOSTestbed; -+ productName = iOSTestbed; -+ productReference = 607A66122B0EFA380010BFC8 /* iOSTestbed.app */; -+ productType = "com.apple.product-type.application"; -+ }; -+ 607A662C2B0EFA3A0010BFC8 /* iOSTestbedTests */ = { -+ isa = PBXNativeTarget; -+ buildConfigurationList = 607A66442B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "iOSTestbedTests" */; -+ buildPhases = ( -+ 607A66292B0EFA3A0010BFC8 /* Sources */, -+ 607A662A2B0EFA3A0010BFC8 /* Frameworks */, -+ 607A662B2B0EFA3A0010BFC8 /* Resources */, -+ 607A66522B0EFFE00010BFC8 /* Embed Frameworks */, -+ ); -+ buildRules = ( -+ ); -+ dependencies = ( -+ 607A662F2B0EFA3A0010BFC8 /* PBXTargetDependency */, -+ ); -+ name = iOSTestbedTests; -+ productName = iOSTestbedTests; -+ productReference = 607A662D2B0EFA3A0010BFC8 /* iOSTestbedTests.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 "iOSTestbed" */; -+ compatibilityVersion = "Xcode 14.0"; -+ developmentRegion = en; -+ hasScannedForEncodings = 0; -+ knownRegions = ( -+ en, -+ Base, -+ ); -+ mainGroup = 607A66092B0EFA380010BFC8; -+ productRefGroup = 607A66132B0EFA380010BFC8 /* Products */; -+ projectDirPath = ""; -+ projectRoot = ""; -+ targets = ( -+ 607A66112B0EFA380010BFC8 /* iOSTestbed */, -+ 607A662C2B0EFA3A0010BFC8 /* iOSTestbedTests */, -+ ); -+ }; -+/* End PBXProject section */ -+ -+/* Begin PBXResourcesBuildPhase section */ -+ 607A66102B0EFA380010BFC8 /* Resources */ = { -+ isa = PBXResourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 607A66252B0EFA390010BFC8 /* LaunchScreen.storyboard in Resources */, -+ 607A66582B0F079F0010BFC8 /* dylib-Info-template.plist in Resources */, -+ 607A66222B0EFA390010BFC8 /* Assets.xcassets in Resources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+ 607A662B2B0EFA3A0010BFC8 /* Resources */ = { -+ isa = PBXResourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXResourcesBuildPhase section */ -+ -+/* Begin PBXShellScriptBuildPhase section */ -+ 607A66552B0F061D0010BFC8 /* Install Target Specific Python Standard Library */ = { -+ isa = PBXShellScriptBuildPhase; -+ alwaysOutOfDate = 1; -+ buildActionMask = 2147483647; -+ files = ( -+ ); -+ inputFileListPaths = ( -+ ); -+ inputPaths = ( -+ ); -+ name = "Install Target Specific Python Standard Library"; -+ outputFileListPaths = ( -+ ); -+ outputPaths = ( -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ shellPath = /bin/sh; -+ shellScript = "set -e\n\nmkdir -p \"$CODESIGNING_FOLDER_PATH/python/lib\"\nif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-iphonesimulator\" ]; then\n echo \"Installing Python modules for iOS Simulator\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64_x86_64-simulator/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nelse\n echo \"Installing Python modules for iOS Device\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nfi\n"; -+ }; -+ 607A66562B0F06200010BFC8 /* Prepare Python Binary Modules */ = { -+ isa = PBXShellScriptBuildPhase; -+ alwaysOutOfDate = 1; -+ buildActionMask = 2147483647; -+ files = ( -+ ); -+ inputFileListPaths = ( -+ ); -+ inputPaths = ( -+ ); -+ name = "Prepare Python Binary Modules"; -+ outputFileListPaths = ( -+ ); -+ outputPaths = ( -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ shellPath = /bin/sh; -+ shellScript = "set -e\n\ninstall_dylib () {\n INSTALL_BASE=$1\n FULL_DYLIB=$2\n\n # The name of the .dylib file\n DYLIB=$(basename \"$FULL_DYLIB\")\n # The name of the .dylib file, relative to the install base\n RELATIVE_DYLIB=${FULL_DYLIB#$CODESIGNING_FOLDER_PATH/$INSTALL_BASE/}\n # The full dotted name of the binary module, constructed from the file path.\n FULL_MODULE_NAME=$(echo $RELATIVE_DYLIB | 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_DYLIB\" \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 defaults write \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\" CFBundleExecutable -string \"$DYLIB\"\n defaults write \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\" CFBundleIdentifier -string \"$FRAMEWORK_BUNDLE_ID\" \n fi\n \n echo \"Installing binary for $RELATIVE_DYLIB\" \n mv \"$FULL_DYLIB\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\"\n}\n\nPYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/python/lib\")\necho \"Install Python $PYTHON_VER standard library dylibs...\"\nfind \"$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload\" -name \"*.dylib\" | while read FULL_DYLIB; do\n install_dylib python/lib/$PYTHON_VER/lib-dynload \"$FULL_DYLIB\"\ndone\n\n# Clean up dylib template \nrm -f \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\"\necho \"Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)...\"\nfind \"$CODESIGNING_FOLDER_PATH/Frameworks\" -name \"*.framework\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der \"{}\" \\; \n"; -+ }; -+/* End PBXShellScriptBuildPhase section */ -+ -+/* Begin PBXSourcesBuildPhase section */ -+ 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 /* iOSTestbedTests.m in Sources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXSourcesBuildPhase section */ -+ -+/* Begin PBXTargetDependency section */ -+ 607A662F2B0EFA3A0010BFC8 /* PBXTargetDependency */ = { -+ isa = PBXTargetDependency; -+ 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; -+ 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 = iphoneos; -+ }; -+ 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 = iphoneos; -+ 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 = 3HEZE76D99; -+ ENABLE_USER_SCRIPT_SANDBOXING = NO; -+ HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; -+ 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 = 12.0; -+ LD_RUNPATH_SEARCH_PATHS = ( -+ "$(inherited)", -+ "@executable_path/Frameworks", -+ ); -+ MARKETING_VERSION = 3.13.0a1; -+ PRODUCT_BUNDLE_IDENTIFIER = org.python.iOSTestbed; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ SWIFT_EMIT_LOC_STRINGS = YES; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ }; -+ 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 = 3HEZE76D99; -+ ENABLE_USER_SCRIPT_SANDBOXING = NO; -+ HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; -+ 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 = 12.0; -+ LD_RUNPATH_SEARCH_PATHS = ( -+ "$(inherited)", -+ "@executable_path/Frameworks", -+ ); -+ MARKETING_VERSION = 3.13.0a1; -+ PRODUCT_BUNDLE_IDENTIFIER = org.python.iOSTestbed; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ SWIFT_EMIT_LOC_STRINGS = YES; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ }; -+ 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.iOSTestbedTests; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ SWIFT_EMIT_LOC_STRINGS = NO; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/iOSTestbed.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/iOSTestbed"; -+ }; -+ 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.iOSTestbedTests; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ SWIFT_EMIT_LOC_STRINGS = NO; -+ 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 "iOSTestbed" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 607A663F2B0EFA3A0010BFC8 /* Debug */, -+ 607A66402B0EFA3A0010BFC8 /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+ 607A66412B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "iOSTestbed" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 607A66422B0EFA3A0010BFC8 /* Debug */, -+ 607A66432B0EFA3A0010BFC8 /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+ 607A66442B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "iOSTestbedTests" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 607A66452B0EFA3A0010BFC8 /* Debug */, -+ 607A66462B0EFA3A0010BFC8 /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+/* End XCConfigurationList section */ -+ }; -+ rootObject = 607A660A2B0EFA380010BFC8 /* Project object */; -+} ---- /dev/null -+++ b/Tools/iOSTestbed/iOSTestbed/AppDelegate.h -@@ -0,0 +1,14 @@ -+// -+// AppDelegate.h -+// iOSTestbed -+// -+// Created by Russell Keith-Magee on 23/11/2023. -+// -+ -+#import -+ -+@interface AppDelegate : UIResponder -+ -+ -+@end -+ ---- /dev/null -+++ b/Tools/iOSTestbed/iOSTestbed/AppDelegate.m -@@ -0,0 +1,22 @@ -+// -+// AppDelegate.m -+// iOSTestbed -+// -+// Created by Russell Keith-Magee on 23/11/2023. -+// -+ -+#import "AppDelegate.h" -+ -+@interface AppDelegate () -+ -+@end -+ -+@implementation AppDelegate -+ -+ -+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { -+ // Override point for customization after application launch. -+ return YES; -+} -+ -+@end ---- /dev/null -+++ b/Tools/iOSTestbed/iOSTestbed/Assets.xcassets/AccentColor.colorset/Contents.json -@@ -0,0 +1,11 @@ -+{ -+ "colors" : [ -+ { -+ "idiom" : "universal" -+ } -+ ], -+ "info" : { -+ "author" : "xcode", -+ "version" : 1 -+ } -+} ---- /dev/null -+++ b/Tools/iOSTestbed/iOSTestbed/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/Tools/iOSTestbed/iOSTestbed/Assets.xcassets/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "author" : "xcode", -+ "version" : 1 -+ } -+} ---- /dev/null -+++ b/Tools/iOSTestbed/iOSTestbed/Base.lproj/LaunchScreen.storyboard -@@ -0,0 +1,9 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ ---- /dev/null -+++ b/Tools/iOSTestbed/iOSTestbed/dylib-Info-template.plist -@@ -0,0 +1,26 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ en -+ CFBundleExecutable -+ -+ CFBundleIdentifier -+ -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundlePackageType -+ APPL -+ CFBundleShortVersionString -+ 1.0 -+ CFBundleSupportedPlatforms -+ -+ iPhoneOS -+ -+ MinimumOSVersion -+ 12.0 -+ CFBundleVersion -+ 1 -+ -+ ---- /dev/null -+++ b/Tools/iOSTestbed/iOSTestbed/iOSTestbed-Info.plist.in -@@ -0,0 +1,54 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ en -+ CFBundleDisplayName -+ ${PRODUCT_NAME} -+ CFBundleExecutable -+ ${EXECUTABLE_NAME} -+ CFBundleIdentifier -+ org.python.iOSTestbed -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundleName -+ ${PRODUCT_NAME} -+ CFBundlePackageType -+ APPL -+ CFBundleShortVersionString -+ @VERSION@ -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ 1 -+ LSRequiresIPhoneOS -+ -+ UIRequiresFullScreen -+ -+ UILaunchStoryboardName -+ Launch Screen -+ UISupportedInterfaceOrientations -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ UISupportedInterfaceOrientations~ipad -+ -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationPortraitUpsideDown -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight -+ -+ MainModule -+ ios -+ UIApplicationSceneManifest -+ -+ UIApplicationSupportsMultipleScenes -+ -+ UISceneConfigurations -+ -+ -+ -+ ---- /dev/null -+++ b/Tools/iOSTestbed/iOSTestbed/main.m -@@ -0,0 +1,23 @@ -+// -+// main.m -+// iOSTestbed -+// -+// Created by Russell Keith-Magee on 23/11/2023. -+// -+ -+#import -+#import "AppDelegate.h" -+ -+int main(int argc, char * argv[]) { -+ NSString * appDelegateClassName; -+ @autoreleasepool { -+ // Setup code that might create autoreleased objects goes here. -+ appDelegateClassName = NSStringFromClass([AppDelegate class]); -+ } -+ -+ // iOS doesn't like uncaught signals. -+ signal(SIGPIPE, SIG_IGN); -+ signal(SIGXFSZ, SIG_IGN); -+ -+ return UIApplicationMain(argc, argv, nil, appDelegateClassName); -+} ---- /dev/null -+++ b/Tools/iOSTestbed/iOSTestbedTests/iOSTestbedTests.m -@@ -0,0 +1,188 @@ -+#import -+#import -+ -+@interface iOSTestbedTests : XCTestCase -+ -+@end -+ -+@implementation iOSTestbedTests -+ -+ -+- (void)testPython { -+ // 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` -+ const char *argv[] = { -+ "iOSTestbed", // argv[0] is the process that is running. -+ "-uall,-subprocess,-gui,-curses", // Enable most resources -+ "-v", // run in verbose mode so we get test failure information -+ // To run a subset of tests, add the test names below; e.g., -+ // "test_os", -+ // "test_sys", -+ }; -+ -+ // Start a Python interpreter. -+ int success = -1; -+ PyStatus status; -+ PyPreConfig preconfig; -+ PyConfig config; -+ NSString *python_home; -+ NSString *path; -+ wchar_t *wtmp_str; -+ -+ PyObject *app_module; -+ PyObject *module; -+ PyObject *module_attr; -+ PyObject *method_args; -+ PyObject *result; -+ PyObject *exc_type; -+ PyObject *exc_value; -+ PyObject *exc_traceback; -+ PyObject *systemExit_code; -+ -+ NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; -+ -+ // Extract Python version from bundle -+ NSString *py_version_string = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; -+ -+ // Generate an isolated Python configuration. -+ NSLog(@"Configuring isolated Python %@...", py_version_string); -+ 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; -+ // 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; -+ // Disable the user site module -+ config.site_import = 0; -+ // 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); -+ -+ // Set the stdlib location for the Python interpreter -+ path = [NSString stringWithFormat:@"%@/python/lib/python%@", resourcePath, py_version_string, nil]; -+ NSLog(@"Stdlib dir: %@", path); -+ wtmp_str = Py_DecodeLocale([path UTF8String], NULL); -+ status = PyConfig_SetString(&config, &config.stdlib_dir, wtmp_str); -+ if (PyStatus_Exception(status)) { -+ XCTFail(@"Unable to set stdlib dir: %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, sizeof(argv) / sizeof(char *), (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; -+ } -+ -+ // Start the test suite. -+ // -+ // From here to Py_ObjectCall(runmodule...) is effectively -+ // a copy of Py_RunMain() (and, more specifically, the -+ // pymain_run_module() method); we need to re-implement it -+ // because we need to be able to inspect the error state of -+ // the interpreter, not just the return code of the module. -+ NSLog(@"Running CPython test suite"); -+ module = PyImport_ImportModule("runpy"); -+ if (module == NULL) { -+ XCTFail(@"Could not import runpy module"); -+ } -+ -+ module_attr = PyObject_GetAttrString(module, "_run_module_as_main"); -+ if (module_attr == NULL) { -+ XCTFail(@"Could not access runpy._run_module_as_main"); -+ } -+ -+ app_module = PyUnicode_FromString("test"); -+ if (app_module == NULL) { -+ XCTFail(@"Could not convert module name to unicode"); -+ } -+ -+ method_args = Py_BuildValue("(Oi)", app_module, 0); -+ if (method_args == NULL) { -+ XCTFail(@"Could not create arguments for runpy._run_module_as_main"); -+ } -+ -+ // Print a separator to differentiate Python startup logs from app logs -+ NSLog(@"---------------------------------------------------------------------------"); -+ -+ // Invoke the app module -+ result = PyObject_Call(module_attr, method_args, NULL); -+ -+ NSLog(@"---------------------------------------------------------------------------"); -+ -+ // The test method doesn't return an object of any interest; -+ // but if the call returns NULL, there's been a problem. -+ if (result == NULL) { -+ // Retrieve the current error state of the interpreter. -+ PyErr_Fetch(&exc_type, &exc_value, &exc_traceback); -+ PyErr_NormalizeException(&exc_type, &exc_value, &exc_traceback); -+ -+ if (exc_traceback == NULL) { -+ XCTFail(@"Could not retrieve traceback"); -+ } -+ -+ if (PyErr_GivenExceptionMatches(exc_value, PyExc_SystemExit)) { -+ systemExit_code = PyObject_GetAttrString(exc_value, "code"); -+ if (systemExit_code == NULL) { -+ XCTFail(@"Could not determine exit code"); -+ } -+ else { -+ success = (int) PyLong_AsLong(systemExit_code); -+ XCTAssertEqual(success, 0, @"Python test suite did not pass"); -+ } -+ } else { -+ PyErr_DisplayException(exc_value); -+ XCTFail(@"Test suite generated exception"); -+ } -+ } -+ Py_Finalize(); -+} -+ -+ -+@end -diff --git a/config.sub b/config.sub -index d74fb6deac..09ebc4287c 100755 ---- a/config.sub -+++ b/config.sub -@@ -1121,7 +1121,7 @@ - xscale-* | xscalee[bl]-*) - cpu=`echo "$cpu" | sed 's/^xscale/arm/'` - ;; -- arm64-*) -+ arm64-* | arm64_32-*) - cpu=aarch64 - ;; - -@@ -1723,7 +1723,7 @@ - | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ - | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ - | hiux* | abug | nacl* | netware* | windows* \ -- | os9* | macos* | osx* | ios* \ -+ | os9* | macos* | osx* | ios* | tvos* | watchos* \ - | mpw* | magic* | mmixware* | mon960* | lnews* \ - | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ - | aos* | aros* | cloudabi* | sortix* | twizzler* \ -@@ -1786,6 +1786,8 @@ - ;; - *-eabi* | *-gnueabi*) - ;; -+ ios*-simulator | tvos*-simulator | watchos*-simulator) -+ ;; - -*) - # Blank kernel with real OS is always fine. - ;; diff --git a/configure b/configure -index c87f518382..0e24298436 100755 +index bff4f6ceb4a..1ce51fcffeb 100755 --- a/configure +++ b/configure -@@ -963,10 +963,13 @@ +@@ -978,10 +978,13 @@ CFLAGS CC HAS_XCRUN +WATCHOS_DEPLOYMENT_TARGET +TVOS_DEPLOYMENT_TARGET -+IOS_DEPLOYMENT_TARGET + IPHONEOS_DEPLOYMENT_TARGET EXPORT_MACOSX_DEPLOYMENT_TARGET CONFIGURE_MACOSX_DEPLOYMENT_TARGET _PYTHON_HOST_PLATFORM --MACHDEP -+INSTALLTARGETS ++APP_STORE_COMPLIANCE_PATCH + INSTALLTARGETS FRAMEWORKINSTALLAPPSPREFIX FRAMEWORKUNIXTOOLSPREFIX - FRAMEWORKPYTHONW -@@ -974,6 +977,8 @@ - FRAMEWORKALTINSTALLFIRST - FRAMEWORKINSTALLLAST - FRAMEWORKINSTALLFIRST -+RESSRCDIR -+PYTHONFRAMEWORKINSTALLNAMEPREFIX - PYTHONFRAMEWORKINSTALLDIR - PYTHONFRAMEWORKPREFIX - PYTHONFRAMEWORKDIR -@@ -983,6 +988,7 @@ - LIPO_32BIT_FLAGS - ARCH_RUN_32BIT - UNIVERSALSDK -+MACHDEP - PKG_CONFIG_LIBDIR - PKG_CONFIG_PATH - PKG_CONFIG -@@ -3988,6 +3994,86 @@ - as_fn_error $? "pkg-config is required" "$LINENO" 5] - fi - -+# Set name for machine-dependent library files -+ -+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking MACHDEP" >&5 -+printf %s "checking MACHDEP... " >&6; } -+if test -z "$MACHDEP" -+then -+ # avoid using uname for cross builds -+ if test "$cross_compiling" = yes; then -+ # ac_sys_system and ac_sys_release are used for setting -+ # a lot of different things including 'define_xopen_source' -+ # in the case statement below. -+ case "$host" in -+ *-*-linux-android*) -+ ac_sys_system=Linux-android -+ ;; -+ *-*-linux*) -+ ac_sys_system=Linux -+ ;; -+ *-*-cygwin*) -+ ac_sys_system=Cygwin -+ ;; -+ *-apple-ios*) -+ ac_sys_system=iOS -+ ;; +@@ -1077,6 +1080,7 @@ + with_universal_archs + with_framework_name + enable_framework ++with_app_store_compliance + with_emscripten_target + enable_wasm_dynamic_linking + enable_wasm_pthreads +@@ -1856,6 +1860,10 @@ + specify the name for the python framework on macOS + only valid when --enable-framework is set. see + Mac/README.rst (default is 'Python') ++ --with-app-store-compliance=[PATCH-FILE] ++ Enable any patches required for compiliance with app ++ stores. Optional PATCH-FILE specifies the custom ++ patch to apply. + --with-emscripten-target=[browser|node] + Emscripten platform + --with-suffix=SUFFIX set executable suffix to SUFFIX (default is empty, +@@ -4048,6 +4056,12 @@ + *-apple-ios*) + ac_sys_system=iOS + ;; + *-apple-tvos*) + ac_sys_system=tvOS + ;; + *-apple-watchos*) + ac_sys_system=watchOS + ;; -+ *-*-vxworks*) -+ ac_sys_system=VxWorks -+ ;; -+ *-*-emscripten) -+ ac_sys_system=Emscripten -+ ;; -+ *-*-wasi) -+ ac_sys_system=WASI -+ ;; -+ *) -+ # for now, limit cross builds to known configurations -+ MACHDEP="unknown" -+ as_fn_error $? "cross build not supported for $host" "$LINENO" 5 -+ esac -+ ac_sys_release= -+ else -+ ac_sys_system=`uname -s` -+ if test "$ac_sys_system" = "AIX" \ -+ -o "$ac_sys_system" = "UnixWare" -o "$ac_sys_system" = "OpenUNIX"; then -+ ac_sys_release=`uname -v` -+ else -+ ac_sys_release=`uname -r` -+ fi -+ fi -+ ac_md_system=`echo $ac_sys_system | -+ tr -d '/ ' | tr '[A-Z]' '[a-z]'` -+ ac_md_release=`echo $ac_sys_release | -+ tr -d '/ ' | sed 's/^[A-Z]\.//' | sed 's/\..*//'` -+ MACHDEP="$ac_md_system$ac_md_release" -+ -+ case $MACHDEP in -+ aix*) MACHDEP="aix";; -+ linux*) MACHDEP="linux";; -+ cygwin*) MACHDEP="cygwin";; -+ darwin*) MACHDEP="darwin";; -+ '') MACHDEP="unknown";; + *-*-vxworks*) + ac_sys_system=VxWorks + ;; +@@ -4102,7 +4116,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, 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 +@@ -4117,6 +4131,14 @@ + aarch64-apple-ios*-simulator) AR=arm64-apple-ios-simulator-ar ;; + aarch64-apple-ios*) AR=arm64-apple-ios-ar ;; + x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;; ++ ++ aarch64-apple-tvos*-simulator) AR=arm64-apple-tvos-simulator-ar ;; ++ aarch64-apple-tvos*) AR=arm64-apple-tvos-ar ;; ++ x86_64-apple-tvos*-simulator) AR=x86_64-apple-tvos-simulator-ar ;; ++ ++ aarch64-apple-watchos*-simulator) AR=arm64-apple-watchos-simulator-ar ;; ++ aarch64-apple-watchos*) AR=arm64_32-apple-watchos-ar ;; ++ x86_64-apple-watchos*-simulator) AR=x86_64-apple-watchos-simulator-ar ;; + *) + esac + fi +@@ -4125,6 +4147,14 @@ + aarch64-apple-ios*-simulator) CC=arm64-apple-ios-simulator-clang ;; + aarch64-apple-ios*) CC=arm64-apple-ios-clang ;; + x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;; ++ ++ aarch64-apple-tvos*-simulator) CC=arm64-apple-tvos-simulator-clang ;; ++ aarch64-apple-tvos*) CC=arm64-apple-tvos-clang ;; ++ x86_64-apple-tvos*-simulator) CC=x86_64-apple-tvos-simulator-clang ;; ++ ++ aarch64-apple-watchos*-simulator) CC=arm64-apple-watchos-simulator-clang ;; ++ aarch64-apple-watchos*) CC=arm64_32-apple-watchos-clang ;; ++ x86_64-apple-watchos*-simulator) CC=x86_64-apple-watchos-simulator-clang ;; + *) + esac + fi +@@ -4133,6 +4163,14 @@ + aarch64-apple-ios*-simulator) CPP=arm64-apple-ios-simulator-cpp ;; + aarch64-apple-ios*) CPP=arm64-apple-ios-cpp ;; + x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;; ++ ++ aarch64-apple-tvos*-simulator) CPP=arm64-apple-tvos-simulator-cpp ;; ++ aarch64-apple-tvos*) CPP=arm64-apple-tvos-cpp ;; ++ x86_64-apple-tvos*-simulator) CPP=x86_64-apple-tvos-simulator-cpp ;; ++ ++ aarch64-apple-watchos*-simulator) CPP=arm64-apple-watchos-simulator-cpp ;; ++ aarch64-apple-watchos*) CPP=arm64_32-apple-watchos-cpp ;; ++ x86_64-apple-watchos*-simulator) CPP=x86_64-apple-watchos-simulator-cpp ;; + *) + esac + fi +@@ -4141,6 +4179,14 @@ + aarch64-apple-ios*-simulator) CXX=arm64-apple-ios-simulator-clang ;; + aarch64-apple-ios*) CXX=arm64-apple-ios-clang ;; + x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang ;; ++ ++ aarch64-apple-tvos*-simulator) CXX=arm64-apple-tvos-simulator-clang ;; ++ aarch64-apple-tvos*) CXX=arm64-apple-tvos-clang ;; ++ x86_64-apple-tvos*-simulator) CXX=x86_64-apple-tvos-simulator-clang ;; ++ ++ aarch64-apple-watchos*-simulator) CXX=arm64-apple-watchos-simulator-clang ;; ++ aarch64-apple-watchos*) CXX=arm64_32-apple-watchos-clang ;; ++ x86_64-apple-watchos*-simulator) CXX=x86_64-apple-watchos-simulator-clang ;; + *) + esac + fi +@@ -4261,8 +4307,10 @@ + case $enableval in + yes) + case $ac_sys_system in +- Darwin) enableval=/Library/Frameworks ;; +- iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;; ++ Darwin) enableval=/Library/Frameworks ;; ++ iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;; ++ tvOS) enableval=tvOS/Frameworks/\$\(MULTIARCH\) ;; ++ watchOS) enableval=watchOS/Frameworks/\$\(MULTIARCH\) ;; + *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 + esac + esac +@@ -4271,6 +4319,8 @@ + no) + case $ac_sys_system in + iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; ++ tvOS) as_fn_error $? "tvOS builds must use --enable-framework" "$LINENO" 5 ;; ++ watchOS) as_fn_error $? "watchOS builds must use --enable-framework" "$LINENO" 5 ;; + *) + PYTHONFRAMEWORK= + PYTHONFRAMEWORKDIR=no-framework +@@ -4377,6 +4427,36 @@ + + ac_config_files="$ac_config_files iOS/Resources/Info.plist" + ++ ;; ++ tvOS) : ++ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=tvOS/Resources ++ ++ ac_config_files="$ac_config_files tvOS/Resources/Info.plist" ++ ++ ;; ++ watchOS) : ++ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=watchOS/Resources ++ ++ ac_config_files="$ac_config_files watchOS/Resources/Info.plist" ++ + ;; + *) + as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 +@@ -4388,6 +4468,8 @@ + + case $ac_sys_system in + iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; ++ tvOS) as_fn_error $? "tvOS builds must use --enable-framework" "$LINENO" 5 ;; ++ watchOS) as_fn_error $? "watchOS builds must use --enable-framework" "$LINENO" 5 ;; + *) + PYTHONFRAMEWORK= + PYTHONFRAMEWORKDIR=no-framework +@@ -4431,6 +4513,53 @@ + printf "%s\n" "#define _PYTHONFRAMEWORK \"${PYTHONFRAMEWORK}\"" >>confdefs.h + + ++{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --with-app-store-compliance" >&5 ++printf %s "checking for --with-app-store-compliance... " >&6; } ++ ++# Check whether --with-app_store_compliance was given. ++if test ${with_app_store_compliance+y} ++then : ++ withval=$with_app_store_compliance; ++ case "$withval" in ++ yes) ++ case $ac_sys_system in ++ Darwin|iOS|tvOS|watchOS) ++ # iOS/tvOS/watchOS is able to share the macOS patch ++ APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" ++ ;; ++ *) as_fn_error $? "no default app store compliance patch available for $ac_sys_system" "$LINENO" 5 ;; ++ esac ++ { 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; } ++ ;; ++ *) ++ APP_STORE_COMPLIANCE_PATCH="${withval}" ++ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: applying custom app store compliance patch" >&5 ++printf "%s\n" "applying custom app store compliance patch" >&6; } ++ ;; + esac + -+ if test "$ac_sys_system" = "SunOS"; then -+ # For Solaris, there isn't an OS version specific macro defined -+ # in most compilers, so we define one here. -+ SUNOS_VERSION=`echo $ac_sys_release | sed -e 's!\.\(0-9\)$!.0\1!g' | tr -d '.'` -+ -+printf "%s\n" "#define Py_SUNOS_VERSION $SUNOS_VERSION" >>confdefs.h ++else $as_nop + -+ fi -+fi -+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: \"$MACHDEP\"" >&5 -+printf "%s\n" "\"$MACHDEP\"" >&6; } -+ - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --enable-universalsdk" >&5 - printf %s "checking for --enable-universalsdk... " >&6; } - # Check whether --enable-universalsdk was given. -@@ -4111,11 +4197,15 @@ - PYTHONFRAMEWORKDIR=no-framework - PYTHONFRAMEWORKPREFIX= - PYTHONFRAMEWORKINSTALLDIR= -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX= -+ RESSRCDIR= - FRAMEWORKINSTALLFIRST= - FRAMEWORKINSTALLLAST= - FRAMEWORKALTINSTALLFIRST= - FRAMEWORKALTINSTALLLAST= - FRAMEWORKPYTHONW= -+ INSTALLTARGETS="commoninstall bininstall maninstall" -+ - if test "x${prefix}" = "xNONE"; then - FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}" - else -@@ -4128,65 +4218,114 @@ - PYTHONFRAMEWORKINSTALLDIR=$PYTHONFRAMEWORKPREFIX/$PYTHONFRAMEWORKDIR - FRAMEWORKINSTALLFIRST="frameworkinstallstructure" - FRAMEWORKALTINSTALLFIRST="frameworkinstallstructure " -- FRAMEWORKINSTALLLAST="frameworkinstallmaclib frameworkinstallapps frameworkinstallunixtools" -- FRAMEWORKALTINSTALLLAST="frameworkinstallmaclib frameworkinstallapps frameworkaltinstallunixtools" -- FRAMEWORKPYTHONW="frameworkpythonw" -- FRAMEWORKINSTALLAPPSPREFIX="/Applications" - -- if test "x${prefix}" = "xNONE" ; then -- FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}" -+ case $ac_sys_system in #( -+ iOS) : -+ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKPYTHONW= -+ INSTALLTARGETS="libinstall inclinstall sharedinstall" - -- else -- FRAMEWORKUNIXTOOLSPREFIX="${prefix}" -- fi -+ prefix=$PYTHONFRAMEWORKPREFIX -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" -+ RESSRCDIR=iOS/Resources - -- case "${enableval}" in -- /System*) -- FRAMEWORKINSTALLAPPSPREFIX="/Applications" -- if test "${prefix}" = "NONE" ; then -- # See below -- FRAMEWORKUNIXTOOLSPREFIX="/usr" -- fi -- ;; -+ ac_config_files="$ac_config_files iOS/Resources/Info.plist" - -- /Library*) -- FRAMEWORKINSTALLAPPSPREFIX="/Applications" -- ;; -+ ac_config_files="$ac_config_files Tools/iOSTestbed/iOSTestbed/iOSTestbed-Info.plist" - -- */Library/Frameworks) -- MDIR="`dirname "${enableval}"`" -- MDIR="`dirname "${MDIR}"`" -- FRAMEWORKINSTALLAPPSPREFIX="${MDIR}/Applications" -- -- if test "${prefix}" = "NONE"; then -- # User hasn't specified the -- # --prefix option, but wants to install -- # the framework in a non-default location, -- # ensure that the compatibility links get -- # installed relative to that prefix as well -- # instead of in /usr/local. -- FRAMEWORKUNIXTOOLSPREFIX="${MDIR}" -- fi -- ;; ++ case $ac_sys_system in ++ iOS|tvOS|watchOS) ++ # Always apply the compliance patch on iOS/tvOS/watchOS; we can use the macOS patch ++ APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" ++ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: applying default app store compliance patch" >&5 ++printf "%s\n" "applying default app store compliance patch" >&6; } + ;; -+ tvOS) : -+ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKPYTHONW= -+ INSTALLTARGETS="libinstall inclinstall sharedinstall" - -- *) -- FRAMEWORKINSTALLAPPSPREFIX="/Applications" -- ;; -- esac -+ prefix=$PYTHONFRAMEWORKPREFIX -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" -+ RESSRCDIR=tvOS/Resources - -- prefix=$PYTHONFRAMEWORKINSTALLDIR/Versions/$VERSION -+ ac_config_files="$ac_config_files tvOS/Resources/Info.plist" - -- # Add files for Mac specific code to the list of output -- # files: -- ac_config_files="$ac_config_files Mac/Makefile" ++ *) ++ # No default app compliance patching on any other platform ++ APP_STORE_COMPLIANCE_PATCH= ++ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not patching for app store compliance" >&5 ++printf "%s\n" "not patching for app store compliance" >&6; } + ;; -+ watchOS) : -+ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKPYTHONW= -+ INSTALLTARGETS="libinstall inclinstall sharedinstall" -+ -+ prefix=$PYTHONFRAMEWORKPREFIX -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" -+ RESSRCDIR=watchOS/Resources - -- ac_config_files="$ac_config_files Mac/PythonLauncher/Makefile" -+ ac_config_files="$ac_config_files watchOS/Resources/Info.plist" - -- ac_config_files="$ac_config_files Mac/Resources/framework/Info.plist" -+ ;; -+ *) : -+ FRAMEWORKINSTALLLAST="frameworkinstallmaclib frameworkinstallapps frameworkinstallunixtools" -+ FRAMEWORKALTINSTALLLAST="frameworkinstallmaclib frameworkinstallapps frameworkaltinstallunixtools" -+ FRAMEWORKPYTHONW="frameworkpythonw" -+ FRAMEWORKINSTALLAPPSPREFIX="/Applications" -+ INSTALLTARGETS="commoninstall bininstall maninstall" - -- ac_config_files="$ac_config_files Mac/Resources/app/Info.plist" -+ if test "x${prefix}" = "xNONE" ; then -+ FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}" - -- esac -+ else -+ FRAMEWORKUNIXTOOLSPREFIX="${prefix}" -+ fi -+ -+ case "${enableval}" in -+ /System*) -+ FRAMEWORKINSTALLAPPSPREFIX="/Applications" -+ if test "${prefix}" = "NONE" ; then -+ # See below -+ FRAMEWORKUNIXTOOLSPREFIX="/usr" -+ fi -+ ;; -+ -+ /Library*) -+ FRAMEWORKINSTALLAPPSPREFIX="/Applications" -+ ;; -+ -+ */Library/Frameworks) -+ MDIR="`dirname "${enableval}"`" -+ MDIR="`dirname "${MDIR}"`" -+ FRAMEWORKINSTALLAPPSPREFIX="${MDIR}/Applications" -+ -+ if test "${prefix}" = "NONE"; then -+ # User hasn't specified the -+ # --prefix option, but wants to install -+ # the framework in a non-default location, -+ # ensure that the compatibility links get -+ # installed relative to that prefix as well -+ # instead of in /usr/local. -+ FRAMEWORKUNIXTOOLSPREFIX="${MDIR}" -+ fi -+ ;; -+ -+ *) -+ FRAMEWORKINSTALLAPPSPREFIX="/Applications" -+ ;; -+ esac -+ -+ prefix=$PYTHONFRAMEWORKINSTALLDIR/Versions/$VERSION -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX=${prefix} -+ RESSRCDIR=Mac/Resources/framework -+ -+ # Add files for Mac specific code to the list of output -+ # files: -+ ac_config_files="$ac_config_files Mac/Makefile" -+ -+ ac_config_files="$ac_config_files Mac/PythonLauncher/Makefile" -+ -+ ac_config_files="$ac_config_files Mac/Resources/app/Info.plist" -+ -+ ac_config_files="$ac_config_files Mac/Resources/framework/Info.plist" ++ esac ++ ++fi ++ ++ + -+ ;; -+ esac -+ esac - - else $as_nop - -@@ -4194,6 +4333,8 @@ - PYTHONFRAMEWORKDIR=no-framework - PYTHONFRAMEWORKPREFIX= - PYTHONFRAMEWORKINSTALLDIR= -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX= -+ RESSRCDIR= - FRAMEWORKINSTALLFIRST= - FRAMEWORKINSTALLLAST= - FRAMEWORKALTINSTALLFIRST= -@@ -4223,79 +4364,11 @@ - - - --printf "%s\n" "#define _PYTHONFRAMEWORK \"${PYTHONFRAMEWORK}\"" >>confdefs.h -- -- --# Set name for machine-dependent library files -- --{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking MACHDEP" >&5 --printf %s "checking MACHDEP... " >&6; } --if test -z "$MACHDEP" --then -- # avoid using uname for cross builds -- if test "$cross_compiling" = yes; then -- # ac_sys_system and ac_sys_release are used for setting -- # a lot of different things including 'define_xopen_source' -- # in the case statement below. -- case "$host" in -- *-*-linux-android*) -- ac_sys_system=Linux-android -- ;; -- *-*-linux*) -- ac_sys_system=Linux -- ;; -- *-*-cygwin*) -- ac_sys_system=Cygwin -- ;; -- *-*-vxworks*) -- ac_sys_system=VxWorks -- ;; -- *-*-emscripten) -- ac_sys_system=Emscripten -- ;; -- *-*-wasi) -- ac_sys_system=WASI -- ;; -- *) -- # for now, limit cross builds to known configurations -- MACHDEP="unknown" -- as_fn_error $? "cross build not supported for $host" "$LINENO" 5 -- esac -- ac_sys_release= -- else -- ac_sys_system=`uname -s` -- if test "$ac_sys_system" = "AIX" \ -- -o "$ac_sys_system" = "UnixWare" -o "$ac_sys_system" = "OpenUNIX"; then -- ac_sys_release=`uname -v` -- else -- ac_sys_release=`uname -r` -- fi -- fi -- ac_md_system=`echo $ac_sys_system | -- tr -d '/ ' | tr '[A-Z]' '[a-z]'` -- ac_md_release=`echo $ac_sys_release | -- tr -d '/ ' | sed 's/^[A-Z]\.//' | sed 's/\..*//'` -- MACHDEP="$ac_md_system$ac_md_release" - -- case $MACHDEP in -- aix*) MACHDEP="aix";; -- linux*) MACHDEP="linux";; -- cygwin*) MACHDEP="cygwin";; -- darwin*) MACHDEP="darwin";; -- '') MACHDEP="unknown";; -- esac - -- if test "$ac_sys_system" = "SunOS"; then -- # For Solaris, there isn't an OS version specific macro defined -- # in most compilers, so we define one here. -- SUNOS_VERSION=`echo $ac_sys_release | sed -e 's!\.\(0-9\)$!.0\1!g' | tr -d '.'` - --printf "%s\n" "#define Py_SUNOS_VERSION $SUNOS_VERSION" >>confdefs.h -+printf "%s\n" "#define _PYTHONFRAMEWORK \"${PYTHONFRAMEWORK}\"" >>confdefs.h - -- fi --fi --{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: \"$MACHDEP\"" >&5 --printf "%s\n" "\"$MACHDEP\"" >&6; } - if test "$cross_compiling" = yes; then -@@ -4303,27 +4376,102 @@ - *-*-linux*) - case "$host_cpu" in - arm*) -- _host_cpu=arm -+ _host_ident=arm - ;; - *) -- _host_cpu=$host_cpu -+ _host_ident=$host_cpu + case "$host" in +@@ -4468,6 +4597,50 @@ + ;; esac ;; - *-*-cygwin*) -- _host_cpu= -+ _host_ident= -+ ;; -+ *-apple-ios*-simulator) -+ _host_os=`echo $host | cut -d '-' -f3` -+ IOS_DEPLOYMENT_TARGET=${_host_os:3} -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${IOS_DEPLOYMENT_TARGET}-iphonesimulator-arm64 -+ ;; -+ *) -+ _host_ident=${IOS_DEPLOYMENT_TARGET}-iphonesimulator-$host_cpu -+ esac -+ ;; -+ *-apple-ios*) -+ _host_os=`echo $host | cut -d '-' -f3` -+ IOS_DEPLOYMENT_TARGET=${_host_os:3} -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${IOS_DEPLOYMENT_TARGET}-iphoneos-arm64 -+ ;; -+ *) -+ _host_ident=${IOS_DEPLOYMENT_TARGET}-iphoneos-$host_cpu -+ esac -+ ;; -+ *-apple-tvos*-simulator) -+ _host_os=`echo $host | cut -d '-' -f3` -+ TVOS_DEPLOYMENT_TARGET=${_host_os:4} -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${TVOS_DEPLOYMENT_TARGET}-appletvsimulator-arm64 -+ ;; -+ *) -+ _host_ident=${TVOS_DEPLOYMENT_TARGET}-appletvsimulator-$host_cpu -+ esac -+ ;; -+ *-apple-tvos*) -+ _host_os=`echo $host | cut -d '-' -f3` -+ TVOS_DEPLOYMENT_TARGET=${_host_os:4} -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${TVOS_DEPLOYMENT_TARGET}-appletvos-arm64 -+ ;; -+ *) -+ _host_ident=${TVOS_DEPLOYMENT_TARGET}-appletvos-$host_cpu -+ esac -+ ;; -+ *-apple-watchos*-simulator) ++ *-apple-tvos*) + _host_os=`echo $host | cut -d '-' -f3` -+ WATCHOS_DEPLOYMENT_TARGET=${_host_os:7} ++ _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=${WATCHOS_DEPLOYMENT_TARGET}-watchsimulator-arm64 -+ ;; -+ *) -+ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-watchsimulator-$host_cpu -+ esac -+ ;; -+ *-apple-watchos*) ++ 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` -+ WATCHOS_DEPLOYMENT_TARGET=${_host_os:7} -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-watchosos-arm64_32 -+ ;; -+ *) -+ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-watchosos-$host_cpu -+ esac -+ ;; -+ *-apple-*) ++ _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 -+ arm*) -+ _host_ident=arm -+ ;; -+ *) -+ _host_ident=$host_cpu ++ aarch64) ++ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-arm64-watch${_host_device} ++ ;; ++ *) ++ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-$host_cpu-watch${_host_device} ++ ;; + esac - ;; ++ ;; *-*-vxworks*) -- _host_cpu=$host_cpu -+ _host_ident=$host_cpu + _host_ident=$host_cpu ;; - wasm32-*-* | wasm64-*-*) -- _host_cpu=$host_cpu -+ _host_ident=$host_cpu - ;; - *) - # for now, limit cross builds to known configurations - MACHDEP="unknown" - as_fn_error $? "cross build not supported for $host" "$LINENO" 5 - esac -- _PYTHON_HOST_PLATFORM="$MACHDEP${_host_cpu:+-$_host_cpu}" -+ _PYTHON_HOST_PLATFORM="$MACHDEP${_host_ident:+-$_host_ident}" - fi - - # Some systems cannot stand _XOPEN_SOURCE being defined at all; they -@@ -4390,6 +4538,13 @@ +@@ -4546,9 +4719,13 @@ define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; -+ # On iOS, defining _POSIX_C_SOURCE also disables platform specific features. -+ iOS/*) -+ define_xopen_source=no;; +- # On iOS, defining _POSIX_C_SOURCE also disables platform specific features. ++ # On iOS/tvOS/watchOS, defining _POSIX_C_SOURCE also disables platform specific features. + iOS/*) + define_xopen_source=no;; + tvOS/*) + define_xopen_source=no;; + watchOS/*) @@ -3836,45 +664,39 @@ index c87f518382..0e24298436 100755 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -4484,6 +4639,32 @@ - ;; - esac +@@ -4611,7 +4788,10 @@ + CONFIGURE_MACOSX_DEPLOYMENT_TARGET= + EXPORT_MACOSX_DEPLOYMENT_TARGET='#' -+case $ac_sys_system in #( -+ iOS) : +-# Record the value of IPHONEOS_DEPLOYMENT_TARGET enforced by the selected host triple. ++# Record the value of IPHONEOS_DEPLOYMENT_TARGET / TVOS_DEPLOYMENT_TARGET / ++# WATCHOS_DEPLOYMENT_TARGET enforced by the selected host triple. + -+ IOS_DEPLOYMENT_TARGET=${IOS_DEPLOYMENT_TARGET:=12.0} -+ as_fn_append CFLAGS " -mios-version-min=${IOS_DEPLOYMENT_TARGET}" -+ as_fn_append LDFLAGS " -mios-version-min=${IOS_DEPLOYMENT_TARGET}" + -+ ;; #( + + + # checks for alternative programs +@@ -4652,6 +4832,16 @@ + as_fn_append CFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" + as_fn_append LDFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" + ;; #( + tvOS) : + -+ TVOS_DEPLOYMENT_TARGET=${TVOS_DEPLOYMENT_TARGET:=9.0} + as_fn_append CFLAGS " -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}" + as_fn_append LDFLAGS " -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}" -+ + ;; #( + watchOS) : + -+ WATCHOS_DEPLOYMENT_TARGET=${WATCHOS_DEPLOYMENT_TARGET:=4.0} + as_fn_append CFLAGS " -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}" + as_fn_append LDFLAGS " -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}" -+ + ;; #( -+ *) : -+ ;; -+esac -+ - if test "$ac_sys_system" = "Darwin" - then - # Extract the first word of "xcrun", so it can be a program name with args. -@@ -6746,6 +6927,12 @@ - case $ac_sys_system in #( - Darwin*) : + *) : + ;; + esac +@@ -6953,6 +7143,10 @@ + MULTIARCH="" ;; #( + iOS) : MULTIARCH="" ;; #( -+ iOS) : -+ MULTIARCH="" ;; #( + tvOS) : + MULTIARCH="" ;; #( + watchOS) : @@ -3882,682 +704,564 @@ index c87f518382..0e24298436 100755 FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -6753,8 +6940,6 @@ - ;; - esac - --{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MULTIARCH" >&5 --printf "%s\n" "$MULTIARCH" >&6; } +@@ -6973,7 +7167,7 @@ + printf "%s\n" "$MULTIARCH" >&6; } - if test x$PLATFORM_TRIPLET != x && test x$MULTIARCH != x; then - if test x$PLATFORM_TRIPLET != x$MULTIARCH; then -@@ -6764,6 +6949,16 @@ - MULTIARCH=$PLATFORM_TRIPLET - fi - -+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MULTIARCH" >&5 -+printf "%s\n" "$MULTIARCH" >&6; } -+ -+case $ac_sys_system in #( + case $ac_sys_system in #( +- iOS) : + iOS|tvOS|watchOS) : -+ SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f1` ;; #( -+ *) : -+ SOABI_PLATFORM=$PLATFORM_TRIPLET -+ ;; -+esac - - if test x$MULTIARCH != x; then - MULTIARCH_CPPFLAGS="-DMULTIARCH=\\\"$MULTIARCH\\\"" -@@ -6807,6 +7002,12 @@ + SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2` ;; #( + *) : + SOABI_PLATFORM=$PLATFORM_TRIPLET +@@ -7024,6 +7218,14 @@ PY_SUPPORT_TIER=3 ;; #( - x86_64-*-freebsd*/clang) : + aarch64-apple-ios*/clang) : PY_SUPPORT_TIER=3 ;; #( -+ aarch64-apple-ios*-simulator/clang) : ++ aarch64-apple-tvos*-simulator/clang) : + PY_SUPPORT_TIER=3 ;; #( -+ x86_64-apple-ios*-simulator/clang) : ++ aarch64-apple-tvos*/clang) : + PY_SUPPORT_TIER=3 ;; #( -+ aarch64-apple-ios*/clang) : ++ aarch64-apple-watchos*-simulator/clang) : + PY_SUPPORT_TIER=3 ;; #( - *) : - PY_SUPPORT_TIER=0 - ;; -@@ -7257,17 +7458,23 @@ - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking LDLIBRARY" >&5 - printf %s "checking LDLIBRARY... " >&6; } - --# MacOSX framework builds need more magic. LDLIBRARY is the dynamic -+# iOS/MacOSX framework builds need more magic. LDLIBRARY is the dynamic - # library that we build, but we do not want to link against it (we - # will find it with a -framework option). For this reason there is an - # extra variable BLDLIBRARY against which Python and the extension - # modules are linked, BLDLIBRARY. This is normally the same as --# LDLIBRARY, but empty for MacOSX framework builds. -+# LDLIBRARY, but empty for MacOSX framework builds. iOS does the same, -+# but uses a non-versioned framework layout. - if test "$enable_framework" - then -- LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' -- RUNSHARED=DYLD_FRAMEWORK_PATH=`pwd`${DYLD_FRAMEWORK_PATH:+:${DYLD_FRAMEWORK_PATH}} -+ case $ac_sys_system in -+ iOS|tvOS|watchOS) -+ LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; -+ *) -+ LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; -+ esac - BLDLIBRARY='' -+ RUNSHARED=DYLD_FRAMEWORK_PATH=`pwd`${DYLD_FRAMEWORK_PATH:+:${DYLD_FRAMEWORK_PATH}} - else - BLDLIBRARY='$(LDLIBRARY)' - fi -@@ -7317,12 +7524,16 @@ - ;; - Darwin*) - LDLIBRARY='libpython$(LDVERSION).dylib' -- BLDLIBRARY='-L. -lpython$(LDVERSION)' -- RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} -+ BLDLIBRARY='-L. -lpython$(LDVERSION)' -+ RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} -+ ;; ++ arm64_32-apple-watchos*/clang) : ++ PY_SUPPORT_TIER=3 ;; #( + aarch64-*-linux-android/clang) : + PY_SUPPORT_TIER=3 ;; #( + x86_64-*-linux-android/clang) : +@@ -7494,7 +7696,7 @@ + case $ac_sys_system in + Darwin) + LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; +- iOS) ++ iOS|tvOS|watchOS) + LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; + *) + as_fn_error $? "Unknown platform for framework build" "$LINENO" 5;; +@@ -7560,7 +7762,7 @@ + BLDLIBRARY='-L. -lpython$(LDVERSION)' + RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} + ;; +- iOS) + iOS|tvOS|watchOS) -+ LDLIBRARY='libpython$(LDVERSION).dylib' -+ BLDLIBRARY='-L. -lpython$(LDVERSION)' - ;; + LDLIBRARY='libpython$(LDVERSION).dylib' + ;; AIX*) -- LDLIBRARY='libpython$(LDVERSION).so' -- RUNSHARED=LIBPATH=`pwd`${LIBPATH:+:${LIBPATH}} -+ LDLIBRARY='libpython$(LDVERSION).so' -+ RUNSHARED=LIBPATH=`pwd`${LIBPATH:+:${LIBPATH}} - ;; - - esac -@@ -12515,6 +12726,7 @@ - esac - ;; - CYGWIN*) SHLIB_SUFFIX=.dll;; -+ iOS|tvOS|watchOS) SHLIB_SUFFIX=.dylib;; - *) SHLIB_SUFFIX=.so;; - esac - fi -@@ -12597,6 +12809,11 @@ +@@ -12895,7 +13097,7 @@ BLDSHARED="$LDSHARED" fi ;; +- iOS/*) + iOS/*|tvOS/*|watchOS/*) -+ LDSHARED='$(CC) -dynamiclib -F . -framework Python' -+ LDCXXSHARED='$(CXX) -dynamiclib -F . -framework Python' -+ BLDSHARED="$LDSHARED" -+ ;; - Emscripten|WASI) - LDSHARED='$(CC) -shared' - LDCXXSHARED='$(CXX) -shared';; -@@ -12750,6 +12967,24 @@ - LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' + LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' + LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' + BLDSHARED="$LDSHARED" +@@ -13028,7 +13230,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/*) + LINKFORSHARED="$extra_undefs -framework CoreFoundation" + + # Issue #18075: the default maximum stack size (8MBytes) is too +@@ -13052,7 +13254,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"; then + LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi - LINKFORSHARED="$LINKFORSHARED";; -+ iOS/*|tvOS/*|watchOS/*) -+ LINKFORSHARED="$extra_undefs -framework CoreFoundation" + ;; +@@ -14451,7 +14653,7 @@ + + ctypes_malloc_closure=yes + ;; #( +- iOS) : ++ iOS|tvOS|watchOS) : + + ctypes_malloc_closure=yes + ;; #( +@@ -17902,12 +18104,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 +@@ -17968,18 +18164,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 +@@ -18406,24 +18590,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 +@@ -18712,12 +18878,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 +@@ -18986,11 +19146,11 @@ + + fi + +-# iOS defines some system methods that can be linked (so they are ++# iOS/tvOS/watchOS define some system methods that can be linked (so they are + # found by configure), but either raise a compilation error (because the + # header definition prevents usage - autoconf doesn't use the headers), or + # raise an error if used at runtime. Force these symbols off. +-if test "$ac_sys_system" != "iOS" ; then ++if test "$ac_sys_system" != "iOS" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" ; then + ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" + if test "x$ac_cv_func_getentropy" = xyes + then : +@@ -19012,6 +19172,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 + -+ # Issue #18075: the default maximum stack size (8MBytes) is too -+ # small for the default recursion limit. Increase the stack size -+ # to ensure that tests don't crash -+ stack_size="1000000" # 16 MB -+ if test "$with_ubsan" = "yes" -+ then -+ # Undefined behavior sanitizer requires an even deeper stack -+ stack_size="4000000" # 64 MB -+ fi ++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 + -+printf "%s\n" "#define THREAD_STACK_SIZE 0x$stack_size" >>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 + -+ LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; - OpenUNIX*|UnixWare*) LINKFORSHARED="-Wl,-Bexport";; - SCO_SV*) LINKFORSHARED="-Wl,-Bexport";; - ReliantUNIX*) LINKFORSHARED="-W1 -Blargedynsym";; -@@ -14138,6 +14373,10 @@ - - ctypes_malloc_closure=yes - ;; #( -+ iOS|tvOS|watchOS) : ++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 + -+ ctypes_malloc_closure=yes -+ ;; #( - sunos5) : - as_fn_append LIBFFI_LIBS " -mimpure-text" - ;; #( -@@ -23651,7 +23890,7 @@ - printf "%s\n" "$ABIFLAGS" >&6; } - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking SOABI" >&5 - printf %s "checking SOABI... " >&6; } --SOABI='cpython-'`echo $VERSION | tr -d .`${ABIFLAGS}${PLATFORM_TRIPLET:+-$PLATFORM_TRIPLET} -+SOABI='cpython-'`echo $VERSION | tr -d .`${ABIFLAGS}${SOABI_PLATFORM:+-$SOABI_PLATFORM} - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $SOABI" >&5 - printf "%s\n" "$SOABI" >&6; } ++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} +@@ -21808,7 +22015,8 @@ -@@ -23660,7 +23899,7 @@ - if test "$Py_DEBUG" = 'true'; then - # Similar to SOABI but remove "d" flag from ABIFLAGS -- ALT_SOABI='cpython-'`echo $VERSION | tr -d .``echo $ABIFLAGS | tr -d d`${PLATFORM_TRIPLET:+-$PLATFORM_TRIPLET} -+ ALT_SOABI='cpython-'`echo $VERSION | tr -d .``echo $ABIFLAGS | tr -d d`${SOABI_PLATFORM:+-$SOABI_PLATFORM} + # 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 : +@@ -21904,7 +22112,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 : +@@ -22061,6 +22269,7 @@ + fi + + done ++fi + + # check for long file support functions + ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" +@@ -22307,10 +22516,10 @@ - printf "%s\n" "#define ALT_SOABI \"${ALT_SOABI}\"" >>confdefs.h + done -@@ -27949,6 +28188,28 @@ +-# On Android and iOS, clock_settime can be linked (so it is found by ++# On Android, iOS, tvOS and watchOS, clock_settime can be linked (so it is found by + # configure), but when used in an unprivileged process, it crashes rather than + # returning an error. Force the symbol off. +-if test "$ac_sys_system" != "Linux-android" && test "$ac_sys_system" != "iOS" ++if test "$ac_sys_system" != "Linux-android" -a "$ac_sys_system" != "iOS" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" + then + + for ac_func in clock_settime +@@ -24537,8 +24746,8 @@ + MODULE_LDFLAGS="\$(BLDLIBRARY)" + fi + +-# On iOS the shared libraries must be linked with the Python framework +-if test "$ac_sys_system" = "iOS"; then ++# On iOS/tvOS/watchOS the shared libraries must be linked with the Python framework ++if test "$ac_sys_system" = "iOS" -o $ac_sys_system = "tvOS" -o $ac_sys_system = "watchOS"; then + MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" + fi + +@@ -27238,7 +27447,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" ; then + ac_cv_file__dev_ptmx=no + ac_cv_file__dev_ptc=no + else +@@ -27671,7 +27880,7 @@ + with_ensurepip=no ;; #( + WASI) : + with_ensurepip=no ;; #( +- iOS) : ++ iOS|tvOS|watchOS) : + with_ensurepip=no ;; #( + *) : + with_ensurepip=upgrade +@@ -28698,7 +28907,7 @@ ;; #( Darwin) : ;; #( +- iOS) : + iOS|tvOS|watchOS) : -+ -+ -+ -+ py_cv_module__curses=n/a -+ py_cv_module__curses_panel=n/a -+ py_cv_module__gdbm=n/a -+ py_cv_module__multiprocessing=n/a -+ py_cv_module__posixshmem=n/a -+ py_cv_module__posixsubprocess=n/a -+ py_cv_module__scproxy=n/a -+ py_cv_module__tkinter=n/a -+ py_cv_module__xxsubinterpreters=n/a -+ py_cv_module_grp=n/a -+ py_cv_module_nis=n/a -+ py_cv_module_readline=n/a -+ py_cv_module_pwd=n/a -+ py_cv_module_spwd=n/a -+ py_cv_module_syslog=n/a -+ py_cv_module_=n/a -+ -+ ;; #( - CYGWIN*) : -@@ -31528,10 +31789,14 @@ - do - case $ac_config_target in - "pyconfig.h") CONFIG_HEADERS="$CONFIG_HEADERS pyconfig.h" ;; -+ "iOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES iOS/Resources/Info.plist" ;; -+ "Tools/iOSTestbed/iOSTestbed/iOSTestbed-Info.plist") CONFIG_FILES="$CONFIG_FILES Tools/iOSTestbed/iOSTestbed/iOSTestbed-Info.plist" ;; + +@@ -32463,6 +32672,8 @@ + "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" ;; - "Mac/Makefile") CONFIG_FILES="$CONFIG_FILES Mac/Makefile" ;; - "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" ;; -+ "Mac/Resources/framework/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/framework/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 cd69f0ede5..0a3321d9f5 100644 +index 65975732f72..940136f3311 100644 --- a/configure.ac +++ b/configure.ac -@@ -310,6 +310,83 @@ - AC_MSG_ERROR([pkg-config is required])] - fi - -+# Set name for machine-dependent library files -+AC_ARG_VAR([MACHDEP], [name for machine-dependent library files]) -+AC_MSG_CHECKING([MACHDEP]) -+if test -z "$MACHDEP" -+then -+ # avoid using uname for cross builds -+ if test "$cross_compiling" = yes; then -+ # ac_sys_system and ac_sys_release are used for setting -+ # a lot of different things including 'define_xopen_source' -+ # in the case statement below. -+ case "$host" in -+ *-*-linux-android*) -+ ac_sys_system=Linux-android -+ ;; -+ *-*-linux*) -+ ac_sys_system=Linux -+ ;; -+ *-*-cygwin*) -+ ac_sys_system=Cygwin -+ ;; -+ *-apple-ios*) -+ ac_sys_system=iOS -+ ;; +@@ -330,6 +330,12 @@ + *-apple-ios*) + ac_sys_system=iOS + ;; + *-apple-tvos*) + ac_sys_system=tvOS + ;; + *-apple-watchos*) + ac_sys_system=watchOS + ;; -+ *-*-vxworks*) -+ ac_sys_system=VxWorks -+ ;; -+ *-*-emscripten) -+ ac_sys_system=Emscripten -+ ;; -+ *-*-wasi) -+ ac_sys_system=WASI -+ ;; -+ *) -+ # for now, limit cross builds to known configurations -+ MACHDEP="unknown" -+ AC_MSG_ERROR([cross build not supported for $host]) -+ esac -+ ac_sys_release= -+ else -+ ac_sys_system=`uname -s` -+ if test "$ac_sys_system" = "AIX" \ -+ -o "$ac_sys_system" = "UnixWare" -o "$ac_sys_system" = "OpenUNIX"; then -+ ac_sys_release=`uname -v` -+ else -+ ac_sys_release=`uname -r` -+ fi -+ fi -+ ac_md_system=`echo $ac_sys_system | -+ tr -d '[/ ]' | tr '[[A-Z]]' '[[a-z]]'` -+ ac_md_release=`echo $ac_sys_release | -+ tr -d '[/ ]' | sed 's/^[[A-Z]]\.//' | sed 's/\..*//'` -+ MACHDEP="$ac_md_system$ac_md_release" -+ -+ case $MACHDEP in -+ aix*) MACHDEP="aix";; -+ linux*) MACHDEP="linux";; -+ cygwin*) MACHDEP="cygwin";; -+ darwin*) MACHDEP="darwin";; -+ '') MACHDEP="unknown";; -+ esac -+ -+ if test "$ac_sys_system" = "SunOS"; then -+ # For Solaris, there isn't an OS version specific macro defined -+ # in most compilers, so we define one here. -+ SUNOS_VERSION=`echo $ac_sys_release | sed -e 's!\.\([0-9]\)$!.0\1!g' | tr -d '.'` -+ AC_DEFINE_UNQUOTED([Py_SUNOS_VERSION], [$SUNOS_VERSION], -+ [The version of SunOS/Solaris as reported by `uname -r' without the dot.]) -+ fi -+fi -+AC_MSG_RESULT(["$MACHDEP"]) -+ - AC_MSG_CHECKING([for --enable-universalsdk]) - AC_ARG_ENABLE([universalsdk], - AS_HELP_STRING([--enable-universalsdk@<:@=SDKDIR@:>@], -@@ -427,11 +504,15 @@ - PYTHONFRAMEWORKDIR=no-framework - PYTHONFRAMEWORKPREFIX= - PYTHONFRAMEWORKINSTALLDIR= -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX= -+ RESSRCDIR= - FRAMEWORKINSTALLFIRST= - FRAMEWORKINSTALLLAST= - FRAMEWORKALTINSTALLFIRST= - FRAMEWORKALTINSTALLLAST= - FRAMEWORKPYTHONW= -+ INSTALLTARGETS="commoninstall bininstall maninstall" -+ - if test "x${prefix}" = "xNONE"; then - FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}" - else -@@ -444,66 +525,113 @@ - PYTHONFRAMEWORKINSTALLDIR=$PYTHONFRAMEWORKPREFIX/$PYTHONFRAMEWORKDIR - FRAMEWORKINSTALLFIRST="frameworkinstallstructure" - FRAMEWORKALTINSTALLFIRST="frameworkinstallstructure " -- FRAMEWORKINSTALLLAST="frameworkinstallmaclib frameworkinstallapps frameworkinstallunixtools" -- FRAMEWORKALTINSTALLLAST="frameworkinstallmaclib frameworkinstallapps frameworkaltinstallunixtools" -- FRAMEWORKPYTHONW="frameworkpythonw" -- FRAMEWORKINSTALLAPPSPREFIX="/Applications" - -- if test "x${prefix}" = "xNONE" ; then -- FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}" -+ case $ac_sys_system in #( -+ iOS) : -+ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKPYTHONW= -+ INSTALLTARGETS="libinstall inclinstall sharedinstall" - -- else -- FRAMEWORKUNIXTOOLSPREFIX="${prefix}" -- fi -+ prefix=$PYTHONFRAMEWORKPREFIX -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" -+ RESSRCDIR=iOS/Resources - -- case "${enableval}" in -- /System*) -- FRAMEWORKINSTALLAPPSPREFIX="/Applications" -- if test "${prefix}" = "NONE" ; then -- # See below -- FRAMEWORKUNIXTOOLSPREFIX="/usr" -- fi -- ;; -+ AC_CONFIG_FILES([iOS/Resources/Info.plist]) -+ AC_CONFIG_FILES([Tools/iOSTestbed/iOSTestbed/iOSTestbed-Info.plist]) -+ ;; -+ tvOS) : -+ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKPYTHONW= -+ INSTALLTARGETS="libinstall inclinstall sharedinstall" - -- /Library*) -- FRAMEWORKINSTALLAPPSPREFIX="/Applications" -- ;; -+ prefix=$PYTHONFRAMEWORKPREFIX -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" -+ RESSRCDIR=tvOS/Resources - -- */Library/Frameworks) -- MDIR="`dirname "${enableval}"`" -- MDIR="`dirname "${MDIR}"`" -- FRAMEWORKINSTALLAPPSPREFIX="${MDIR}/Applications" -- -- if test "${prefix}" = "NONE"; then -- # User hasn't specified the -- # --prefix option, but wants to install -- # the framework in a non-default location, -- # ensure that the compatibility links get -- # installed relative to that prefix as well -- # instead of in /usr/local. -- FRAMEWORKUNIXTOOLSPREFIX="${MDIR}" -- fi -- ;; -+ AC_CONFIG_FILES([tvOS/Resources/Info.plist]) -+ ;; -+ watchOS) : -+ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKPYTHONW= -+ INSTALLTARGETS="libinstall inclinstall sharedinstall" - -- *) -- FRAMEWORKINSTALLAPPSPREFIX="/Applications" -- ;; -- esac -+ prefix=$PYTHONFRAMEWORKPREFIX -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" -+ RESSRCDIR=watchOS/Resources - -- prefix=$PYTHONFRAMEWORKINSTALLDIR/Versions/$VERSION -+ AC_CONFIG_FILES([watchOS/Resources/Info.plist]) -+ ;; -+ *) : -+ FRAMEWORKINSTALLLAST="frameworkinstallmaclib frameworkinstallapps frameworkinstallunixtools" -+ FRAMEWORKALTINSTALLLAST="frameworkinstallmaclib frameworkinstallapps frameworkaltinstallunixtools" -+ FRAMEWORKPYTHONW="frameworkpythonw" -+ FRAMEWORKINSTALLAPPSPREFIX="/Applications" -+ INSTALLTARGETS="commoninstall bininstall maninstall" - -- # Add files for Mac specific code to the list of output -- # files: -- AC_CONFIG_FILES([Mac/Makefile]) -- AC_CONFIG_FILES([Mac/PythonLauncher/Makefile]) -- AC_CONFIG_FILES([Mac/Resources/framework/Info.plist]) -- AC_CONFIG_FILES([Mac/Resources/app/Info.plist]) -- esac -+ if test "x${prefix}" = "xNONE" ; then -+ FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}" -+ -+ else -+ FRAMEWORKUNIXTOOLSPREFIX="${prefix}" -+ fi -+ -+ case "${enableval}" in -+ /System*) -+ FRAMEWORKINSTALLAPPSPREFIX="/Applications" -+ if test "${prefix}" = "NONE" ; then -+ # See below -+ FRAMEWORKUNIXTOOLSPREFIX="/usr" -+ fi -+ ;; -+ -+ /Library*) -+ FRAMEWORKINSTALLAPPSPREFIX="/Applications" -+ ;; -+ -+ */Library/Frameworks) -+ MDIR="`dirname "${enableval}"`" -+ MDIR="`dirname "${MDIR}"`" -+ FRAMEWORKINSTALLAPPSPREFIX="${MDIR}/Applications" -+ -+ if test "${prefix}" = "NONE"; then -+ # User hasn't specified the -+ # --prefix option, but wants to install -+ # the framework in a non-default location, -+ # ensure that the compatibility links get -+ # installed relative to that prefix as well -+ # instead of in /usr/local. -+ FRAMEWORKUNIXTOOLSPREFIX="${MDIR}" -+ fi -+ ;; -+ -+ *) -+ FRAMEWORKINSTALLAPPSPREFIX="/Applications" -+ ;; -+ esac -+ -+ prefix=$PYTHONFRAMEWORKINSTALLDIR/Versions/$VERSION -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX=${prefix} -+ RESSRCDIR=Mac/Resources/framework -+ -+ # Add files for Mac specific code to the list of output -+ # files: -+ AC_CONFIG_FILES([Mac/Makefile]) -+ AC_CONFIG_FILES([Mac/PythonLauncher/Makefile]) -+ AC_CONFIG_FILES([Mac/Resources/app/Info.plist]) -+ AC_CONFIG_FILES([Mac/Resources/framework/Info.plist]) -+ ;; -+ esac -+ esac + *-*-vxworks*) + ac_sys_system=VxWorks + ;; +@@ -382,7 +388,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, 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 +@@ -397,6 +403,14 @@ + aarch64-apple-ios*-simulator) AR=arm64-apple-ios-simulator-ar ;; + aarch64-apple-ios*) AR=arm64-apple-ios-ar ;; + x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;; ++ ++ aarch64-apple-tvos*-simulator) AR=arm64-apple-tvos-simulator-ar ;; ++ aarch64-apple-tvos*) AR=arm64-apple-tvos-ar ;; ++ x86_64-apple-tvos*-simulator) AR=x86_64-apple-tvos-simulator-ar ;; ++ ++ aarch64-apple-watchos*-simulator) AR=arm64-apple-watchos-simulator-ar ;; ++ aarch64-apple-watchos*) AR=arm64_32-apple-watchos-ar ;; ++ x86_64-apple-watchos*-simulator) AR=x86_64-apple-watchos-simulator-ar ;; + *) + esac + fi +@@ -405,6 +419,14 @@ + aarch64-apple-ios*-simulator) CC=arm64-apple-ios-simulator-clang ;; + aarch64-apple-ios*) CC=arm64-apple-ios-clang ;; + x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;; ++ ++ aarch64-apple-tvos*-simulator) CC=arm64-apple-tvos-simulator-clang ;; ++ aarch64-apple-tvos*) CC=arm64-apple-tvos-clang ;; ++ x86_64-apple-tvos*-simulator) CC=x86_64-apple-tvos-simulator-clang ;; ++ ++ aarch64-apple-watchos*-simulator) CC=arm64-apple-watchos-simulator-clang ;; ++ aarch64-apple-watchos*) CC=arm64_32-apple-watchos-clang ;; ++ x86_64-apple-watchos*-simulator) CC=x86_64-apple-watchos-simulator-clang ;; + *) + esac + fi +@@ -413,6 +435,14 @@ + aarch64-apple-ios*-simulator) CPP=arm64-apple-ios-simulator-cpp ;; + aarch64-apple-ios*) CPP=arm64-apple-ios-cpp ;; + x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;; ++ ++ aarch64-apple-tvos*-simulator) CPP=arm64-apple-tvos-simulator-cpp ;; ++ aarch64-apple-tvos*) CPP=arm64-apple-tvos-cpp ;; ++ x86_64-apple-tvos*-simulator) CPP=x86_64-apple-tvos-simulator-cpp ;; ++ ++ aarch64-apple-watchos*-simulator) CPP=arm64-apple-watchos-simulator-cpp ;; ++ aarch64-apple-watchos*) CPP=arm64_32-apple-watchos-cpp ;; ++ x86_64-apple-watchos*-simulator) CPP=x86_64-apple-watchos-simulator-cpp ;; + *) + esac + fi +@@ -421,6 +451,14 @@ + aarch64-apple-ios*-simulator) CXX=arm64-apple-ios-simulator-clang ;; + aarch64-apple-ios*) CXX=arm64-apple-ios-clang ;; + x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang ;; ++ ++ aarch64-apple-tvos*-simulator) CXX=arm64-apple-tvos-simulator-clang ;; ++ aarch64-apple-tvos*) CXX=arm64-apple-tvos-clang ;; ++ x86_64-apple-tvos*-simulator) CXX=x86_64-apple-tvos-simulator-clang ;; ++ ++ aarch64-apple-watchos*-simulator) CXX=arm64-apple-watchos-simulator-clang ;; ++ aarch64-apple-watchos*) CXX=arm64_32-apple-watchos-clang ;; ++ x86_64-apple-watchos*-simulator) CXX=x86_64-apple-watchos-simulator-clang ;; + *) + esac + fi +@@ -535,8 +573,10 @@ + case $enableval in + yes) + case $ac_sys_system in +- Darwin) enableval=/Library/Frameworks ;; +- iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;; ++ Darwin) enableval=/Library/Frameworks ;; ++ iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;; ++ tvOS) enableval=tvOS/Frameworks/\$\(MULTIARCH\) ;; ++ watchOS) enableval=watchOS/Frameworks/\$\(MULTIARCH\) ;; + *) AC_MSG_ERROR([Unknown platform for framework build]) + esac + esac +@@ -545,6 +585,8 @@ + no) + case $ac_sys_system in + iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;; ++ tvOS) AC_MSG_ERROR([tvOS builds must use --enable-framework]) ;; ++ watchOS) AC_MSG_ERROR([watchOS builds must use --enable-framework]) ;; + *) + PYTHONFRAMEWORK= + PYTHONFRAMEWORKDIR=no-framework +@@ -647,6 +689,34 @@ + + AC_CONFIG_FILES([iOS/Resources/Info.plist]) + ;; ++ tvOS) : ++ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=tvOS/Resources ++ ++ AC_CONFIG_FILES([tvOS/Resources/Info.plist]) ++ ;; ++ watchOS) : ++ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=watchOS/Resources ++ ++ AC_CONFIG_FILES([watchOS/Resources/Info.plist]) ++ ;; + *) + AC_MSG_ERROR([Unknown platform for framework build]) + ;; +@@ -655,6 +725,8 @@ ],[ - PYTHONFRAMEWORK= - PYTHONFRAMEWORKDIR=no-framework - PYTHONFRAMEWORKPREFIX= - PYTHONFRAMEWORKINSTALLDIR= -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX= -+ RESSRCDIR= - FRAMEWORKINSTALLFIRST= - FRAMEWORKINSTALLLAST= - FRAMEWORKALTINSTALLFIRST= -@@ -522,6 +650,8 @@ - AC_SUBST([PYTHONFRAMEWORKDIR]) - AC_SUBST([PYTHONFRAMEWORKPREFIX]) - AC_SUBST([PYTHONFRAMEWORKINSTALLDIR]) -+AC_SUBST([PYTHONFRAMEWORKINSTALLNAMEPREFIX]) -+AC_SUBST([RESSRCDIR]) - AC_SUBST([FRAMEWORKINSTALLFIRST]) - AC_SUBST([FRAMEWORKINSTALLLAST]) - AC_SUBST([FRAMEWORKALTINSTALLFIRST]) -@@ -529,105 +659,113 @@ - AC_SUBST([FRAMEWORKPYTHONW]) - AC_SUBST([FRAMEWORKUNIXTOOLSPREFIX]) - AC_SUBST([FRAMEWORKINSTALLAPPSPREFIX]) -+AC_SUBST([INSTALLTARGETS]) - + case $ac_sys_system in + iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;; ++ tvOS) AC_MSG_ERROR([tvOS builds must use --enable-framework]) ;; ++ watchOS) AC_MSG_ERROR([watchOS builds must use --enable-framework]) ;; + *) + PYTHONFRAMEWORK= + PYTHONFRAMEWORKDIR=no-framework +@@ -695,6 +767,47 @@ AC_DEFINE_UNQUOTED([_PYTHONFRAMEWORK], ["${PYTHONFRAMEWORK}"], [framework name]) --# Set name for machine-dependent library files --AC_ARG_VAR([MACHDEP], [name for machine-dependent library files]) --AC_MSG_CHECKING([MACHDEP]) --if test -z "$MACHDEP" --then -- # avoid using uname for cross builds -- if test "$cross_compiling" = yes; then -- # ac_sys_system and ac_sys_release are used for setting -- # a lot of different things including 'define_xopen_source' -- # in the case statement below. -- case "$host" in -- *-*-linux-android*) -- ac_sys_system=Linux-android -- ;; -- *-*-linux*) -- ac_sys_system=Linux -- ;; -- *-*-cygwin*) -- ac_sys_system=Cygwin -- ;; -- *-*-vxworks*) -- ac_sys_system=VxWorks -- ;; -- *-*-emscripten) -- ac_sys_system=Emscripten -- ;; -- *-*-wasi) -- ac_sys_system=WASI -- ;; -- *) -- # for now, limit cross builds to known configurations -- MACHDEP="unknown" -- AC_MSG_ERROR([cross build not supported for $host]) -- esac -- ac_sys_release= -- else -- ac_sys_system=`uname -s` -- if test "$ac_sys_system" = "AIX" \ -- -o "$ac_sys_system" = "UnixWare" -o "$ac_sys_system" = "OpenUNIX"; then -- ac_sys_release=`uname -v` -- else -- ac_sys_release=`uname -r` -- fi -- fi -- ac_md_system=`echo $ac_sys_system | -- tr -d '[/ ]' | tr '[[A-Z]]' '[[a-z]]'` -- ac_md_release=`echo $ac_sys_release | -- tr -d '[/ ]' | sed 's/^[[A-Z]]\.//' | sed 's/\..*//'` -- MACHDEP="$ac_md_system$ac_md_release" -- -- case $MACHDEP in -- aix*) MACHDEP="aix";; -- linux*) MACHDEP="linux";; -- cygwin*) MACHDEP="cygwin";; -- darwin*) MACHDEP="darwin";; -- '') MACHDEP="unknown";; -- esac -- -- if test "$ac_sys_system" = "SunOS"; then -- # For Solaris, there isn't an OS version specific macro defined -- # in most compilers, so we define one here. -- SUNOS_VERSION=`echo $ac_sys_release | sed -e 's!\.\([0-9]\)$!.0\1!g' | tr -d '.'` -- AC_DEFINE_UNQUOTED([Py_SUNOS_VERSION], [$SUNOS_VERSION], -- [The version of SunOS/Solaris as reported by `uname -r' without the dot.]) -- fi --fi --AC_MSG_RESULT(["$MACHDEP"]) -- ++dnl quadrigraphs "@<:@" and "@:>@" produce "[" and "]" in the output ++AC_MSG_CHECKING([for --with-app-store-compliance]) ++AC_ARG_WITH( ++ [app_store_compliance], ++ [AS_HELP_STRING( ++ [--with-app-store-compliance=@<:@PATCH-FILE@:>@], ++ [Enable any patches required for compiliance with app stores. ++ Optional PATCH-FILE specifies the custom patch to apply.] ++ )],[ ++ case "$withval" in ++ yes) ++ case $ac_sys_system in ++ Darwin|iOS|tvOS|watchOS) ++ # iOS/tvOS/watchOS is able to share the macOS patch ++ APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" ++ ;; ++ *) AC_MSG_ERROR([no default app store compliance patch available for $ac_sys_system]) ;; ++ esac ++ AC_MSG_RESULT([applying default app store compliance patch]) ++ ;; ++ *) ++ APP_STORE_COMPLIANCE_PATCH="${withval}" ++ AC_MSG_RESULT([applying custom app store compliance patch]) ++ ;; ++ esac ++ ],[ ++ case $ac_sys_system in ++ iOS|tvOS|watchOS) ++ # Always apply the compliance patch on iOS/tvOS/watchOS; we can use the macOS patch ++ APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" ++ AC_MSG_RESULT([applying default app store compliance patch]) ++ ;; ++ *) ++ # No default app compliance patching on any other platform ++ APP_STORE_COMPLIANCE_PATCH= ++ AC_MSG_RESULT([not patching for app store compliance]) ++ ;; ++ esac ++]) ++AC_SUBST([APP_STORE_COMPLIANCE_PATCH]) ++ AC_SUBST([_PYTHON_HOST_PLATFORM]) if test "$cross_compiling" = yes; then case "$host" in - *-*-linux*) - case "$host_cpu" in - arm*) -- _host_cpu=arm -+ _host_ident=arm - ;; - *) -- _host_cpu=$host_cpu -+ _host_ident=$host_cpu +@@ -730,6 +843,46 @@ + ;; esac ;; - *-*-cygwin*) -- _host_cpu= -+ _host_ident= -+ ;; -+ *-apple-ios*-simulator) -+ _host_os=`echo $host | cut -d '-' -f3` -+ IOS_DEPLOYMENT_TARGET=${_host_os:3} -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${IOS_DEPLOYMENT_TARGET}-iphonesimulator-arm64 -+ ;; -+ *) -+ _host_ident=${IOS_DEPLOYMENT_TARGET}-iphonesimulator-$host_cpu -+ esac -+ ;; -+ *-apple-ios*) -+ _host_os=`echo $host | cut -d '-' -f3` -+ IOS_DEPLOYMENT_TARGET=${_host_os:3} -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${IOS_DEPLOYMENT_TARGET}-iphoneos-arm64 -+ ;; -+ *) -+ _host_ident=${IOS_DEPLOYMENT_TARGET}-iphoneos-$host_cpu -+ esac -+ ;; -+ *-apple-tvos*-simulator) -+ _host_os=`echo $host | cut -d '-' -f3` -+ TVOS_DEPLOYMENT_TARGET=${_host_os:4} -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${TVOS_DEPLOYMENT_TARGET}-appletvsimulator-arm64 -+ ;; -+ *) -+ _host_ident=${TVOS_DEPLOYMENT_TARGET}-appletvsimulator-$host_cpu -+ esac -+ ;; -+ *-apple-tvos*) -+ _host_os=`echo $host | cut -d '-' -f3` -+ TVOS_DEPLOYMENT_TARGET=${_host_os:4} -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${TVOS_DEPLOYMENT_TARGET}-appletvos-arm64 -+ ;; -+ *) -+ _host_ident=${TVOS_DEPLOYMENT_TARGET}-appletvos-$host_cpu -+ esac -+ ;; -+ *-apple-watchos*-simulator) ++ *-apple-tvos*) + _host_os=`echo $host | cut -d '-' -f3` -+ WATCHOS_DEPLOYMENT_TARGET=${_host_os:7} ++ _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=${WATCHOS_DEPLOYMENT_TARGET}-watchsimulator-arm64 -+ ;; -+ *) -+ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-watchsimulator-$host_cpu -+ esac -+ ;; -+ *-apple-watchos*) ++ 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` -+ WATCHOS_DEPLOYMENT_TARGET=${_host_os:7} -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-watchosos-arm64_32 -+ ;; -+ *) -+ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-watchosos-$host_cpu -+ esac -+ ;; -+ *-apple-*) ++ _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 -+ arm*) -+ _host_ident=arm -+ ;; -+ *) -+ _host_ident=$host_cpu ++ aarch64) ++ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-arm64-watch${_host_device} ++ ;; ++ *) ++ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-$host_cpu-watch${_host_device} ++ ;; + esac - ;; ++ ;; *-*-vxworks*) -- _host_cpu=$host_cpu -+ _host_ident=$host_cpu + _host_ident=$host_cpu ;; - wasm32-*-* | wasm64-*-*) -- _host_cpu=$host_cpu -+ _host_ident=$host_cpu - ;; - *) - # for now, limit cross builds to known configurations - MACHDEP="unknown" - AC_MSG_ERROR([cross build not supported for $host]) - esac -- _PYTHON_HOST_PLATFORM="$MACHDEP${_host_cpu:+-$_host_cpu}" -+ _PYTHON_HOST_PLATFORM="$MACHDEP${_host_ident:+-$_host_ident}" - fi - - # Some systems cannot stand _XOPEN_SOURCE being defined at all; they -@@ -693,6 +831,13 @@ +@@ -807,9 +960,13 @@ define_xopen_source=no;; Darwin/@<:@[12]@:>@@<:@0-9@:>@.*) define_xopen_source=no;; -+ # On iOS, defining _POSIX_C_SOURCE also disables platform specific features. -+ iOS/*) -+ define_xopen_source=no;; +- # On iOS, defining _POSIX_C_SOURCE also disables platform specific features. ++ # On iOS/tvOS/watchOS, defining _POSIX_C_SOURCE also disables platform specific features. + iOS/*) + define_xopen_source=no;; + tvOS/*) + define_xopen_source=no;; + watchOS/*) @@ -4565,701 +1269,283 @@ index cd69f0ede5..0a3321d9f5 100644 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -783,6 +928,26 @@ +@@ -868,8 +1025,11 @@ + CONFIGURE_MACOSX_DEPLOYMENT_TARGET= + EXPORT_MACOSX_DEPLOYMENT_TARGET='#' + +-# Record the value of IPHONEOS_DEPLOYMENT_TARGET enforced by the selected host triple. ++# Record the value of IPHONEOS_DEPLOYMENT_TARGET / TVOS_DEPLOYMENT_TARGET / ++# WATCHOS_DEPLOYMENT_TARGET enforced by the selected host triple. + AC_SUBST([IPHONEOS_DEPLOYMENT_TARGET]) ++AC_SUBST([TVOS_DEPLOYMENT_TARGET]) ++AC_SUBST([WATCHOS_DEPLOYMENT_TARGET]) + + # checks for alternative programs + +@@ -903,11 +1063,17 @@ ], ) -+dnl iOS/tvOS/watchOS need to enforce the deployment target. -+AS_CASE([$ac_sys_system], -+ [iOS], [ -+ IOS_DEPLOYMENT_TARGET=${IOS_DEPLOYMENT_TARGET:=12.0} -+ AS_VAR_APPEND([CFLAGS], [" -mios-version-min=${IOS_DEPLOYMENT_TARGET}"]) -+ AS_VAR_APPEND([LDFLAGS], [" -mios-version-min=${IOS_DEPLOYMENT_TARGET}"]) -+ AC_SUBST([IOS_DEPLOYMENT_TARGET]) +-dnl Add the compiler flag for the iOS minimum supported OS version. ++dnl Add the compiler flag for the iOS/tvOS/watchOS minimum supported OS version. + AS_CASE([$ac_sys_system], + [iOS], [ + AS_VAR_APPEND([CFLAGS], [" -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"]) + AS_VAR_APPEND([LDFLAGS], [" -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"]) + ],[tvOS], [ -+ TVOS_DEPLOYMENT_TARGET=${TVOS_DEPLOYMENT_TARGET:=9.0} + AS_VAR_APPEND([CFLAGS], [" -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}"]) + AS_VAR_APPEND([LDFLAGS], [" -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}"]) -+ AC_SUBST([TVOS_DEPLOYMENT_TARGET]) + ],[watchOS], [ -+ WATCHOS_DEPLOYMENT_TARGET=${WATCHOS_DEPLOYMENT_TARGET:=4.0} + AS_VAR_APPEND([CFLAGS], [" -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}"]) + AS_VAR_APPEND([LDFLAGS], [" -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}"]) -+ AC_SUBST([WATCHOS_DEPLOYMENT_TARGET]) -+ ], -+) -+ - if test "$ac_sys_system" = "Darwin" - then - dnl look for SDKROOT -@@ -941,11 +1106,13 @@ - AC_MSG_CHECKING([for multiarch]) + ], + ) + +@@ -1095,6 +1261,8 @@ AS_CASE([$ac_sys_system], [Darwin*], [MULTIARCH=""], -+ [iOS], [MULTIARCH=""], + [iOS], [MULTIARCH=""], + [tvOS], [MULTIARCH=""], + [watchOS], [MULTIARCH=""], [FreeBSD*], [MULTIARCH=""], [MULTIARCH=$($CC --print-multiarch 2>/dev/null)] ) - AC_SUBST([MULTIARCH]) --AC_MSG_RESULT([$MULTIARCH]) - - if test x$PLATFORM_TRIPLET != x && test x$MULTIARCH != x; then - if test x$PLATFORM_TRIPLET != x$MULTIARCH; then -@@ -955,6 +1122,12 @@ - MULTIARCH=$PLATFORM_TRIPLET - fi - AC_SUBST([PLATFORM_TRIPLET]) -+AC_MSG_RESULT([$MULTIARCH]) -+ -+AS_CASE([$ac_sys_system], -+ [iOS|tvOS|watchOS], [SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f1`], -+ [SOABI_PLATFORM=$PLATFORM_TRIPLET] -+) - - if test x$MULTIARCH != x; then - MULTIARCH_CPPFLAGS="-DMULTIARCH=\\\"$MULTIARCH\\\"" -@@ -985,6 +1158,9 @@ - [wasm32-unknown-emscripten/clang], [PY_SUPPORT_TIER=3], dnl WebAssembly Emscripten - [wasm32-unknown-wasi/clang], [PY_SUPPORT_TIER=3], dnl WebAssembly System Interface - [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 -+ [x86_64-apple-ios*-simulator/clang], [PY_SUPPORT_TIER=3], dnl iOS Simulator on x86_64 -+ [aarch64-apple-ios*/clang], [PY_SUPPORT_TIER=3], dnl iOS on ARM64 - [PY_SUPPORT_TIER=0] +@@ -1116,7 +1284,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], [SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2`], + [SOABI_PLATFORM=$PLATFORM_TRIPLET] ) -@@ -1298,17 +1474,23 @@ - - AC_MSG_CHECKING([LDLIBRARY]) - --# MacOSX framework builds need more magic. LDLIBRARY is the dynamic -+# iOS/MacOSX framework builds need more magic. LDLIBRARY is the dynamic - # library that we build, but we do not want to link against it (we - # will find it with a -framework option). For this reason there is an - # extra variable BLDLIBRARY against which Python and the extension - # modules are linked, BLDLIBRARY. This is normally the same as --# LDLIBRARY, but empty for MacOSX framework builds. -+# LDLIBRARY, but empty for MacOSX framework builds. iOS does the same, -+# but uses a non-versioned framework layout. - if test "$enable_framework" - then -- LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' -- RUNSHARED=DYLD_FRAMEWORK_PATH=`pwd`${DYLD_FRAMEWORK_PATH:+:${DYLD_FRAMEWORK_PATH}} -+ case $ac_sys_system in -+ iOS|tvOS|watchOS) -+ LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; -+ *) -+ LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; -+ esac - BLDLIBRARY='' -+ RUNSHARED=DYLD_FRAMEWORK_PATH=`pwd`${DYLD_FRAMEWORK_PATH:+:${DYLD_FRAMEWORK_PATH}} - else - BLDLIBRARY='$(LDLIBRARY)' - fi -@@ -1357,12 +1539,16 @@ - ;; - Darwin*) - LDLIBRARY='libpython$(LDVERSION).dylib' -- BLDLIBRARY='-L. -lpython$(LDVERSION)' -- RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} -+ BLDLIBRARY='-L. -lpython$(LDVERSION)' -+ RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} -+ ;; +@@ -1150,6 +1318,10 @@ + [x86_64-*-freebsd*/clang], [PY_SUPPORT_TIER=3], dnl FreeBSD on AMD64 + [aarch64-apple-ios*-simulator/clang], [PY_SUPPORT_TIER=3], dnl iOS Simulator on arm64 + [aarch64-apple-ios*/clang], [PY_SUPPORT_TIER=3], dnl iOS on ARM64 ++ [aarch64-apple-tvos*-simulator/clang], [PY_SUPPORT_TIER=3], dnl tvOS Simulator on arm64 ++ [aarch64-apple-tvos*/clang], [PY_SUPPORT_TIER=3], dnl tvOS on ARM64 ++ [aarch64-apple-watchos*-simulator/clang], [PY_SUPPORT_TIER=3], dnl watchOS Simulator on arm64 ++ [arm64_32-apple-watchos*/clang], [PY_SUPPORT_TIER=3], dnl watchOS on ARM64 + [aarch64-*-linux-android/clang], [PY_SUPPORT_TIER=3], dnl Android on ARM64 + [x86_64-*-linux-android/clang], [PY_SUPPORT_TIER=3], dnl Android on AMD64 + +@@ -1481,7 +1653,7 @@ + case $ac_sys_system in + Darwin) + LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; +- iOS) ++ iOS|tvOS|watchOS) + LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; + *) + AC_MSG_ERROR([Unknown platform for framework build]);; +@@ -1546,7 +1718,7 @@ + BLDLIBRARY='-L. -lpython$(LDVERSION)' + RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} + ;; +- iOS) + iOS|tvOS|watchOS) -+ LDLIBRARY='libpython$(LDVERSION).dylib' -+ BLDLIBRARY='-L. -lpython$(LDVERSION)' - ;; + LDLIBRARY='libpython$(LDVERSION).dylib' + ;; AIX*) -- LDLIBRARY='libpython$(LDVERSION).so' -- RUNSHARED=LIBPATH=`pwd`${LIBPATH:+:${LIBPATH}} -+ LDLIBRARY='libpython$(LDVERSION).so' -+ RUNSHARED=LIBPATH=`pwd`${LIBPATH:+:${LIBPATH}} - ;; - - esac -@@ -3085,6 +3271,7 @@ - esac - ;; - CYGWIN*) SHLIB_SUFFIX=.dll;; -+ iOS|tvOS|watchOS) SHLIB_SUFFIX=.dylib;; - *) SHLIB_SUFFIX=.so;; - esac - fi -@@ -3165,6 +3352,11 @@ +@@ -3417,7 +3589,7 @@ BLDSHARED="$LDSHARED" fi ;; +- iOS/*) + iOS/*|tvOS/*|watchOS/*) -+ LDSHARED='$(CC) -dynamiclib -F . -framework Python' -+ LDCXXSHARED='$(CXX) -dynamiclib -F . -framework Python' -+ BLDSHARED="$LDSHARED" -+ ;; - Emscripten|WASI) - LDSHARED='$(CC) -shared' - LDCXXSHARED='$(CXX) -shared';; -@@ -3309,6 +3501,24 @@ - LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' + LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' + LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' + BLDSHARED="$LDSHARED" +@@ -3541,7 +3713,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/*) + LINKFORSHARED="$extra_undefs -framework CoreFoundation" + + # Issue #18075: the default maximum stack size (8MBytes) is too +@@ -3565,7 +3737,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"; then + LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi - LINKFORSHARED="$LINKFORSHARED";; -+ iOS/*|tvOS/*|watchOS/*) -+ LINKFORSHARED="$extra_undefs -framework CoreFoundation" -+ -+ # Issue #18075: the default maximum stack size (8MBytes) is too -+ # small for the default recursion limit. Increase the stack size -+ # to ensure that tests don't crash -+ stack_size="1000000" # 16 MB -+ if test "$with_ubsan" = "yes" -+ then -+ # Undefined behavior sanitizer requires an even deeper stack -+ stack_size="4000000" # 64 MB -+ fi -+ -+ AC_DEFINE_UNQUOTED([THREAD_STACK_SIZE], -+ [0x$stack_size], -+ [Custom thread stack size depending on chosen sanitizer runtimes.]) -+ -+ LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; - OpenUNIX*|UnixWare*) LINKFORSHARED="-Wl,-Bexport";; - SCO_SV*) LINKFORSHARED="-Wl,-Bexport";; - ReliantUNIX*) LINKFORSHARED="-W1 -Blargedynsym";; -@@ -3682,6 +3892,9 @@ + ;; +@@ -3949,7 +4121,7 @@ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], +- [iOS], [ + [iOS|tvOS|watchOS], [ -+ ctypes_malloc_closure=yes -+ ], + ctypes_malloc_closure=yes + ], [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] - ) - AS_VAR_IF([ctypes_malloc_closure], [yes], [ -@@ -5714,7 +5927,7 @@ - AC_MSG_CHECKING([ABIFLAGS]) - AC_MSG_RESULT([$ABIFLAGS]) - AC_MSG_CHECKING([SOABI]) --SOABI='cpython-'`echo $VERSION | tr -d .`${ABIFLAGS}${PLATFORM_TRIPLET:+-$PLATFORM_TRIPLET} -+SOABI='cpython-'`echo $VERSION | tr -d .`${ABIFLAGS}${SOABI_PLATFORM:+-$SOABI_PLATFORM} - AC_MSG_RESULT([$SOABI]) +@@ -5043,9 +5215,9 @@ + # checks for library functions + AC_CHECK_FUNCS([ \ + accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ +- copy_file_range ctermid dup dup3 execv explicit_bzero explicit_memset \ ++ copy_file_range ctermid dup dup3 explicit_bzero explicit_memset \ + faccessat fchmod fchmodat fchown fchownat fdopendir fdwalk fexecve \ +- fork fork1 fpathconf fstatat ftime ftruncate futimens futimes futimesat \ ++ fpathconf fstatat ftime ftruncate futimens futimes futimesat \ + gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \ + getgrnam_r getgrouplist gethostname getitimer getloadavg getlogin \ + getpeername getpgid getpid getppid getpriority _getpty \ +@@ -5053,15 +5225,14 @@ + 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 ptsname ptsname_r pwrite pwritev pwritev2 readlink readlinkat readv realpath renameat \ + rtpSpawn sched_get_priority_max sched_rr_get_interval sched_setaffinity \ + 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 \ +@@ -5076,12 +5247,20 @@ + AC_CHECK_FUNCS([lchmod]) + fi + +-# iOS defines some system methods that can be linked (so they are ++# iOS/tvOS/watchOS define some system methods that can be linked (so they are + # found by configure), but either raise a compilation error (because the + # header definition prevents usage - autoconf doesn't use the headers), or + # raise an error if used at runtime. Force these symbols off. +-if test "$ac_sys_system" != "iOS" ; then +- AC_CHECK_FUNCS([getentropy getgroups system]) ++if test "$ac_sys_system" != "iOS" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" ; then ++ AC_CHECK_FUNCS([ getentropy getgroups system ]) ++fi ++ ++# tvOS/watchOS have some additional methods that can be found, but not used. ++if test "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" ; then ++ AC_CHECK_FUNCS([ \ ++ execv fork fork1 posix_spawn posix_spawnp posix_spawn_file_actions_addclosefrom_np \ ++ sigaltstack \ ++ ]) + fi + + AC_CHECK_DECL([dirfd], +@@ -5332,20 +5511,22 @@ + ]) + + # 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]) +@@ -5384,10 +5565,10 @@ + ]) + ]) + +-# On Android and iOS, clock_settime can be linked (so it is found by ++# On Android, iOS, tvOS and watchOS, clock_settime can be linked (so it is found by + # configure), but when used in an unprivileged process, it crashes rather than + # returning an error. Force the symbol off. +-if test "$ac_sys_system" != "Linux-android" && test "$ac_sys_system" != "iOS" ++if test "$ac_sys_system" != "Linux-android" -a "$ac_sys_system" != "iOS" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" + then + AC_CHECK_FUNCS([clock_settime], [], [ + AC_CHECK_LIB([rt], [clock_settime], [ +@@ -6135,8 +6316,8 @@ + MODULE_LDFLAGS="\$(BLDLIBRARY)" + fi - # Release build, debug build (Py_DEBUG), and trace refs build (Py_TRACE_REFS) -@@ -5722,7 +5935,7 @@ - if test "$Py_DEBUG" = 'true'; then - # Similar to SOABI but remove "d" flag from ABIFLAGS - AC_SUBST([ALT_SOABI]) -- ALT_SOABI='cpython-'`echo $VERSION | tr -d .``echo $ABIFLAGS | tr -d d`${PLATFORM_TRIPLET:+-$PLATFORM_TRIPLET} -+ ALT_SOABI='cpython-'`echo $VERSION | tr -d .``echo $ABIFLAGS | tr -d d`${SOABI_PLATFORM:+-$SOABI_PLATFORM} - AC_DEFINE_UNQUOTED([ALT_SOABI], ["${ALT_SOABI}"], - [Alternative SOABI used in debug build to load C extensions built in release mode]) +-# On iOS the shared libraries must be linked with the Python framework +-if test "$ac_sys_system" = "iOS"; then ++# On iOS/tvOS/watchOS the shared libraries must be linked with the Python framework ++if test "$ac_sys_system" = "iOS" -o $ac_sys_system = "tvOS" -o $ac_sys_system = "watchOS"; then + MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -7068,6 +7281,29 @@ + +@@ -6772,7 +6953,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" ; then + ac_cv_file__dev_ptmx=no + ac_cv_file__dev_ptc=no + else +@@ -7029,7 +7210,7 @@ + AS_CASE([$ac_sys_system], + [Emscripten], [with_ensurepip=no], + [WASI], [with_ensurepip=no], +- [iOS], [with_ensurepip=no], ++ [iOS|tvOS|watchOS], [with_ensurepip=no], + [with_ensurepip=upgrade] + ) + ]) +@@ -7439,7 +7620,7 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], +- [iOS], [ + [iOS|tvOS|watchOS], [ -+ dnl subprocess and multiprocessing are not supported (no fork syscall). -+ dnl curses and tkinter user interface are not available. -+ dnl gdbm and nis aren't available -+ dnl Stub implementations are provided for pwd, grp etc APIs -+ PY_STDLIB_MOD_SET_NA( -+ [_curses], -+ [_curses_panel], -+ [_gdbm], -+ [_multiprocessing], -+ [_posixshmem], -+ [_posixsubprocess], -+ [_scproxy], -+ [_tkinter], -+ [_xxsubinterpreters], -+ [grp], -+ [nis], -+ [readline], -+ [pwd], -+ [spwd], -+ [syslog], -+ ) -+ ], - [CYGWIN*], [PY_STDLIB_MOD_SET_NA([_scproxy])], - [QNX*], [PY_STDLIB_MOD_SET_NA([_scproxy])], - [FreeBSD*], [PY_STDLIB_MOD_SET_NA([_scproxy])], ---- /dev/null -+++ b/iOS/README.rst -@@ -0,0 +1,354 @@ -+==================== -+Python on iOS README -+==================== -+ -+:Authors: -+ Russell Keith-Magee (2023-11) -+ -+This document provides a quick overview of some iOS specific features in the -+Python distribution. -+ -+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 on iOS, tools such as `BeeWare's -+Briefcase `__ and `Kivy's Builddozer -+`__ will provide a much more approachable user -+experience. -+ -+Compilers for building on iOS -+============================= -+ -+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, on the most -+recently released macOS. -+ -+iOS 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 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. -+ -+How do I build 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: -+ -+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. This can be done using the ``lipo`` tool, provide by Xcode: -+ -+ $ lipo -create -output module.dylib path/to/x86_64/module.dylib path/to/arm64/module.dylib -+ -+3. Merge the headers for each architecture. The header files will be identical on each platform, -+ except for ``pyconfig.h``. Copy all the headers from one platform (say, arm64), -+ rename ``pyconfig.h`` to ``pyconfig-arm64.h``, and copy the ``pyconfig.h`` for -+ the other architecture into the merged header folder as ``pyconfig-x86_64.h``. -+ Then copy the ``iOS/Resources/pyconfig.h`` file into the merged headers folder. -+ This will allow the two Python architectures to share header files. -+4. Merge the "fat" frameworks for each ABI into a single XCframework. -+ -+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`/iOS/Resources/bin:$PATH -+ $ ./configure \ -+ AR=arm64-apple-ios-simulator-ar \ -+ CC=arm64-apple-ios-simulator-clang \ -+ CPP=arm64-apple-ios-simulator-cpp \ -+ CXX=arm64-apple-ios-simulator-clang \ -+ --enable-framework=/path/to/install \ -+ --host=aarch64-apple-ios-simulator \ -+ --build=aarch64-apple-darwin \ -+ --with-build-python=/path/to/python.exe \ -+ ac_cv_file__dev_ptmx=no \ -+ ac_cv_file__dev_ptc=no -+ $ make -+ $ make install -+ -+In this invocation: -+ -+* ``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; howver, ``xcrun`` embeds user- and -+ version-specific paths into the sysconfig data, which limits the portability -+ of the compiled Python. It also 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. -+ -+* ``/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-ios`` for ARM64 iOS devices. -+ - ``aarch64-apple-ios-simulator`` for the iOS simulator running on Apple -+ Silicon devices. -+ - ``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 -+ 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 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. -+ -+In practice, you will likely also need to specify the paths to iOS builds of the -+binary libraries that CPython depends on (XZ, BZip2, LibFFI and OpenSSL). -+ -+How do I test Python on iOS? -+---------------------------- -+ -+The ``Tools/iOSTestbed`` folder that contains an Xcode project that is able to run -+the iOS test suite. 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=aarch64-apple-ios-simulator`` or ``--host=x86_64-apple-ios-simulator`` -+), setting the framework location to the testbed project:: -+ -+ --enable-framework="./Tools/iOSTestbed/Python.xcframework/ios-arm64_x86_64-simulator" -+ -+Then run ``make all install testiOS``. This will build an iOS framework for your -+chosen architecture, install the Python iOS framework into the testbed project, -+and run the test suite on an "iPhone SE (3rd generation)" simulator. -+ -+While the test suite is running, Xcode does not display any console output. -+After showing some Xcode build commands, the console output will print ``Testing -+started``, and then appear to stop. It will remain in this state until the test -+suite completes. On a 2022 M1 MacBook Pro, the test suite takes approximately 12 -+minutes to run; a couple of extra minutes is required to boot and prepare the -+iOS simulator. -+ -+On success, the test suite will exit and report successful completion of the -+test suite. No output of the Python test suite will be displayed. -+ -+On failure, the output of the Python test suite *will* be displayed. This will -+show the details of the tests that failed. -+ -+How do I debug test failures? -+----------------------------- -+ -+The easiest way to diagnose a single test failure is to open the testbed project -+in Xcode and run the tests from there using the "Product > Test" menu item. -+ -+Running specific tests -+^^^^^^^^^^^^^^^^^^^^^^ -+ -+As the test suite is being executed on an iOS simulator, it is not possible to -+pass in command line arguments to configure test suite operation. To work around -+this limitation, the arguments that would normally be passed as command line -+arguments are configured as a static string at the start of the XCTest method -+``- (void)testPython`` in ``iOSTestbedTests.m``. To pass an argument to the test -+suite, add a a string to the ``argv`` defintion. These arguments will be passed -+to the test suite as if they had been passed to ``python -m test`` at the -+command line. -+ -+Disabling automated Breakpoints -+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -+ -+By default, Xcode will inserts an automatic breakpoint whenever a signal is -+raised. The Python test suite raises many of these signals as part of normal -+operation; unless you are trying to diagnose an issue with signals, the -+automatic breakpoints can be inconvenient. However, they can be disabled by -+creating a symbolic breakpoint that is triggered at the start of the test run. -+ -+Select "Debug > Breakpoints > Create Symbolic Breakpoint" from the Xcode menu, and -+populate the new brewpoint with the following details: -+ -+* **Name**: IgnoreSignals -+* **Symbol**: UIApplicationMain -+* **Action**: Add debugger commands for: -+ - ``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`` -+* Check the "Automatically continue after evaluating" box. -+ -+All other details can be left blank. When the process executes the -+``UIApplicationMain`` entry point, the breakpoint will trigger, run the debugger -+commands to disable the automatic breakpoints, and automatically resume. -+ -+Using Python on iOS -+=================== -+ -+To add Python to an iOS Xcode project: -+ -+1. Build Python for each architecture that you want to support. At a minimum, -+ you will need a build for `arm64-apple-ios`, plus one of either -+ `arm64-apple-ios-simulator` or `x86_64-apple-ios-simulator`. This will -+ produce a ``Python.framework``, plus a ``bin`` and ``lib`` folder in the same -+ directory as the ``Python.framework``. -+ -+2. Create an XCframework from the individual single-platform frameworks. The -+ basic structure can be compiled from the individual ``Python.framework`` -+ outputs:: -+ -+ xcodebuild -create-xcframework -output Python.xcframework -framework path/to/iphoneos/Python.framework -framework path/to/iphonesimulator/Python.framework -+ -+ Then, copy the ``bin`` and ``lib`` folders into the architecture-specific slices of -+ the XCframework:: -+ -+ cp path/to/iphoneos/bin Python.xcframework/ios-arm64 -+ cp path/to/iphoneos/lib Python.xcframework/ios-arm64 -+ -+ cp path/to/iphonesimulator/bin Python.xcframework/ios-arm64-simulator -+ cp path/to/iphonesimulator/lib Python.xcframework/ios-arm64-simulator -+ -+ Note that the name of the architecture-specific slice for the simulator will -+ depend on the CPU architecture that you build. -+ -+3. Add symbolic links to "common" platform names for each slice:: -+ -+ ln -si ios-arm64 Python.xcframework/iphoneos -+ ln -si ios-arm64-simulator Python.xcframework/iphonesimulator -+ -+4. Drag the XCframework into your iOS project. In the following instructions, -+ we'll assume you've dropped the XCframework into the root of your project; -+ however, you can use any other location that you want. -+ -+5. Drag the ``iOS/Resources/dylib-Info-template.plist`` file into your project, -+ and ensure it is associated with the app target. -+ -+6. Select the app target by selecting the root node of your Xcode project, then -+ the target name in the sidebar that appears. -+ -+7. In the "General" settings, under "Frameworks, Libraries and Embedded -+ Content", Add ``Python.xcframework``, with "Embed & Sign" selected. -+ -+8. In the "Build Settings" tab, modify the following: -+ -+ - Build Options -+ * User script sandboxing: No -+ - Search Paths -+ * Framework Search Paths: ``$(PROJECT_DIR)`` -+ * Header Search Paths: ``"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers"`` -+ - Apple Clang - Warnings - All languages -+ * Quoted Include in Framework Header: No -+ -+9. In the "Build Phases" tab, add a new "Run Script" build step *before* the -+ "Embed Frameworks" step. Name the step "Install Target Specific Python -+ Standard Library", disable the "Based on dependency analysis" checkbox, and -+ set the script content to:: -+ -+ 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/iphonesimulator/lib/" "$CODESIGNING_FOLDER_PATH/python/lib/" -+ else -+ echo "Installing Python modules for iOS Device" -+ rsync -au --delete "$PROJECT_DIR/Python.xcframework/iphoneos/lib/" "$CODESIGNING_FOLDER_PATH/python/lib/" -+ fi -+ -+10. Add a second "Run Script" build step *directly after* the step you just -+ added, named "Prepare Python Binary Modules". It should also have "Based on -+ dependency analysis" unchecked, with the following script content:: -+ -+ set -e -+ -+ install_dylib () { -+ INSTALL_BASE=$1 -+ FULL_DYLIB=$2 -+ -+ # The name of the .dylib file -+ DYLIB=$(basename "$FULL_DYLIB") -+ # The name of the .dylib file, relative to the install base -+ RELATIVE_DYLIB=${FULL_DYLIB#$CODESIGNING_FOLDER_PATH/$INSTALL_BASE/} -+ # The full dotted name of the binary module, constructed from the file path. -+ FULL_MODULE_NAME=$(echo $RELATIVE_DYLIB | 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_DYLIB" -+ mkdir -p "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER" -+ -+ cp "$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist" -+ defaults write "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist" CFBundleExecutable -string "$DYLIB" -+ defaults write "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist" CFBundleIdentifier -string "$FRAMEWORK_BUNDLE_ID" -+ fi -+ -+ echo "Installing binary for $RELATIVE_DYLIB" -+ mv "$FULL_DYLIB" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER" -+ } -+ -+ PYTHON_VER=$(ls "$CODESIGNING_FOLDER_PATH/python/lib") -+ echo "Install Python $PYTHON_VER standard library dylibs..." -+ find "$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload" -name "*.dylib" | while read FULL_DYLIB; do -+ install_dylib python/lib/$PYTHON_VER/lib-dynload "$FULL_DYLIB" -+ 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 "{}" \; -+ -+11. Add Objective C code to initialize and use a Python interpreter in embedded -+ mode. When configuring the interpreter, you can use: -+ -+ [NSString stringWithFormat:@"%@/python", [[NSBundle mainBundle] resourcePath], nil] -+ -+ as the value of ``PYTHONHOME``; the standard library will be installed as the -+ ``lib/python3.X`` subfolder of that ``PYTHONHOME``. -+ -+If you have third-party binary modules in your app, they will need to be: -+ -+* Compiled for both on-device and simulator platforms; -+* Copied into your project as part of the script in step 9; -+* Installed and signed as part of the script in step 10. ---- /dev/null + 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 -@@ -0,0 +1,34 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ en -+ CFBundleExecutable -+ Python -+ CFBundleGetInfoString -+ Python Runtime and Library -+ CFBundleIdentifier -+ @PYTHONFRAMEWORKIDENTIFIER@ -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundleName -+ Python -+ CFBundlePackageType -+ FMWK -+ CFBundleShortVersionString +@@ -17,13 +17,13 @@ + CFBundlePackageType + FMWK + CFBundleShortVersionString +- @VERSION@ + %VERSION% -+ CFBundleLongVersionString -+ %VERSION%, (c) 2001-2023 Python Software Foundation. -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ 1 -+ CFBundleSupportedPlatforms -+ -+ iPhoneOS -+ -+ MinimumOSVersion -+ @IOS_DEPLOYMENT_TARGET@ -+ -+ ---- /dev/null -+++ b/iOS/Resources/bin/arm64-apple-ios-ar -@@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk iphoneos ar $@ ---- /dev/null -+++ b/iOS/Resources/bin/arm64-apple-ios-clang -@@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk iphoneos clang -target arm64-apple-ios $@ ---- /dev/null -+++ b/iOS/Resources/bin/arm64-apple-ios-cpp -@@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk iphoneos clang -target arm64-apple-ios -E $@ ---- /dev/null -+++ b/iOS/Resources/bin/arm64-apple-ios-simulator-ar -@@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk iphonesimulator ar $@ ---- /dev/null -+++ b/iOS/Resources/bin/arm64-apple-ios-simulator-clang -@@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk iphonesimulator clang -target arm64-apple-ios-simulator $@ ---- /dev/null -+++ b/iOS/Resources/bin/arm64-apple-ios-simulator-cpp -@@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk iphonesimulator clang -target arm64-apple-ios-simulator -E $@ ---- /dev/null -+++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-ar -@@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk iphonesimulator ar $@ ---- /dev/null -+++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-clang -@@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk iphonesimulator clang -target x86_64-apple-ios-simulator $@ ---- /dev/null -+++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp -@@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk iphonesimulator clang -target x86_64-apple-ios-simulator -E $@ ---- /dev/null -+++ b/iOS/Resources/dylib-Info-template.plist -@@ -0,0 +1,26 @@ -+ -+ -+ -+ -+ CFBundleDevelopmentRegion -+ en -+ CFBundleExecutable -+ -+ CFBundleIdentifier -+ -+ CFBundleInfoDictionaryVersion -+ 6.0 -+ CFBundlePackageType -+ APPL -+ CFBundleShortVersionString -+ 1.0 -+ CFBundleSupportedPlatforms -+ -+ iPhoneOS -+ -+ MinimumOSVersion -+ 12.0 -+ CFBundleVersion -+ 1 -+ -+ ---- /dev/null -+++ b/iOS/Resources/pyconfig.h -@@ -0,0 +1,7 @@ -+#ifdef __arm64__ -+#include "pyconfig-arm64.h" -+#endif -+ -+#ifdef __x86_64__ -+#include "pyconfig-x86_64.h" -+#endif + CFBundleLongVersionString + %VERSION%, (c) 2001-2024 Python Software Foundation. + CFBundleSignature + ???? + CFBundleVersion +- 1 ++ %VERSION% + CFBundleSupportedPlatforms + + iPhoneOS --- /dev/null +++ b/tvOS/README.rst @@ -0,0 +1,108 @@ @@ -5395,11 +1681,11 @@ index cd69f0ede5..0a3321d9f5 100644 + CFBundleShortVersionString + %VERSION% + CFBundleLongVersionString -+ %VERSION%, (c) 2001-2023 Python Software Foundation. ++ %VERSION%, (c) 2001-2024 Python Software Foundation. + CFBundleSignature + ???? + CFBundleVersion -+ %VERSION% ++ 1 + CFBundleSupportedPlatforms + + tvOS @@ -5412,47 +1698,47 @@ index cd69f0ede5..0a3321d9f5 100644 +++ b/tvOS/Resources/bin/arm64-apple-tvos-ar @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk appletvos ar $@ ++xcrun --sdk appletvos${TVOS_SDK_VERSION} ar $@ --- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-clang @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk appletvos clang -target arm64-apple-tvos $@ ++xcrun --sdk appletvos${TVOS_SDK_VERSION} clang -target arm64-apple-tvos $@ --- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-cpp @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk appletvos clang -target arm64-apple-tvos -E $@ ++xcrun --sdk appletvos${TVOS_SDK_VERSION} clang -target arm64-apple-tvos -E $@ --- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-ar @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk appletvsimulator ar $@ ++xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} ar $@ --- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-clang @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk appletvsimulator clang -target arm64-apple-tvos-simulator $@ ++xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target arm64-apple-tvos-simulator $@ --- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-cpp @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk appletvsimulator clang -target arm64-apple-tvos-simulator -E $@ ++xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target arm64-apple-tvos-simulator -E $@ --- /dev/null +++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-ar @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk appletvsimulator ar $@ ++xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} ar $@ --- /dev/null +++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-clang @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk appletvsimulator clang -target x86_64-apple-tvos-simulator $@ ++xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target x86_64-apple-tvos-simulator $@ --- /dev/null +++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-cpp @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk appletvsimulator clang -target x86_64-apple-tvos-simulator -E $@ ++xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target x86_64-apple-tvos-simulator -E $@ --- /dev/null +++ b/tvOS/Resources/dylib-Info-template.plist @@ -0,0 +1,26 @@ @@ -5644,12 +1930,12 @@ index cd69f0ede5..0a3321d9f5 100644 +++ b/watchOS/Resources/bin/arm64-apple-watchos-simulator-ar @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk watchsimulator ar $@ ++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 clang -target arm64-apple-watchos-simulator $@ ++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 @@ @@ -5659,32 +1945,32 @@ index cd69f0ede5..0a3321d9f5 100644 +++ b/watchOS/Resources/bin/arm64_32-apple-watchos-ar @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk watchos ar $@ ++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 clang -target arm64_32-apple-watchos $@ ++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 clang -target arm64_32-apple-watchos -E $@ ++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 ar $@ ++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 clang -target x86_64-apple-watchos-simulator $@ ++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 clang -target x86_64-apple-watchos-simulator -E $@ ++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 @@ diff --git a/patch/Python/release.macOS.exclude b/patch/Python/release.macOS.exclude index 13854545..f45bf7f8 100644 --- a/patch/Python/release.macOS.exclude +++ b/patch/Python/release.macOS.exclude @@ -2,7 +2,13 @@ # when building macOS Python-Apple-support tarballs from the official Framework # It is used by `tar -X` during the Makefile build. # -./Versions/*/Resources/Python.app -./Versions/*/bin -./Versions/*/etc -./Versions/*/share +Resources/Python.app +Versions/*/bin +Versions/*/etc +Versions/*/Frameworks +Versions/*/lib/python*/idlelib +Versions/*/lib/python*/lib-dynload/_tkinter.* +Versions/*/lib/python*/tkinter +Versions/*/lib/python*/turtle.py +Versions/*/lib/python*/turtledemo +Versions/*/share diff --git a/patch/make-macho-standalone.py b/patch/make-macho-standalone.py deleted file mode 100644 index 975765d8..00000000 --- a/patch/make-macho-standalone.py +++ /dev/null @@ -1,10 +0,0 @@ -import os -import sys -from macholib.MachOStandalone import MachOStandalone - - -if __name__ == "__main__": - MachOStandalone( - sys.argv[1], - os.path.abspath(f"{sys.argv[1]}/") - ).run(contents="@rpath/") diff --git a/patch/make-relocatable.sh b/patch/make-relocatable.sh new file mode 100755 index 00000000..f268cd70 --- /dev/null +++ b/patch/make-relocatable.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +FRAMEWORK_BASEDIR=$1 +echo "Making $1 relocatable" +PYTHON_VER=${FRAMEWORK_BASEDIR##*/} +echo "Python version ${PYTHON_VER}" + +pushd ${FRAMEWORK_BASEDIR} + +echo "Rewrite ID of Python library" +install_name_tool -id @rpath/Python.framework/Versions/${PYTHON_VER}/Python Python > /dev/null +for dylib in `ls lib/*.*.dylib`; do + # lib + if [ "${dylib}" != "lib/libpython${PYTHON_VER}.dylib" ] ; then + echo Rewrite ID of ${dylib} + install_name_tool -id @rpath/Python.framework/Versions/${PYTHON_VER}/${dylib} ${FRAMEWORK_BASEDIR}/${dylib} + fi +done +for module in `find . -name "*.dylib" -type f -o -name "*.so" -type f`; do + if [ "$(otool -L ${module} | grep -c /Library/Frameworks/Python.framework)" != "0" ]; then + for dylib in `ls lib/*.*.dylib`; do + echo Rewrite references to ${dylib} in ${module} + install_name_tool -change /Library/Frameworks/Python.framework/Versions/${PYTHON_VER}/${dylib} @rpath/Python.framework/Versions/${PYTHON_VER}/${dylib} ${module} + done + fi +done +popd diff --git a/patch/make-xcrun-alias b/patch/make-xcrun-alias deleted file mode 100755 index 50340e14..00000000 --- a/patch/make-xcrun-alias +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -# A script that writes an executable xcrun alias. -# Arg 1: The name of the file to output -# Arg 2: The arguments to pass to xcrun -mkdir -p $(dirname $1) -cat << EOF > $1 -#!/bin/bash -xcrun $2 \$@ -EOF -chmod +x $1 From ad578a9c265a992f92ac8065af41b70012c31a58 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 30 Jul 2024 09:27:34 +0800 Subject: [PATCH 037/113] Use a branch version of Briefcase. --- .github/workflows/ci.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7f86e857..615c7234 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -68,8 +68,10 @@ jobs: - name: Install dependencies if: matrix.run-tests run: | + # TODO - Revert to the development version of Briefcase # Use the development version of Briefcase - python -m pip install git+https://github.com/beeware/briefcase.git + # python -m pip install git+https://github.com/beeware/briefcase.git + python -m pip install git+https://github.com/freakboy3742/briefcase.git@version-bumps - name: Run support testbed check if: matrix.run-tests working-directory: Python-support-testbed From 8a0cd421b3a1f5ce39028328efd9f5b1b76685b2 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 30 Jul 2024 11:12:08 +0800 Subject: [PATCH 038/113] Add a timeout for iOS testing. --- .github/workflows/ci.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 615c7234..a9b1b6f4 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -33,13 +33,14 @@ jobs: run-tests: true steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4.1.7 - name: Extract config variables id: config-vars run: | PYTHON_VER=$(make config | grep "PYTHON_VER=" | cut -d "=" -f 2) echo "PYTHON_VER=${PYTHON_VER}" | tee -a ${GITHUB_OUTPUT} + - name: Set up Python uses: actions/setup-python@v5.1.1 with: @@ -51,6 +52,7 @@ jobs: run: | # Do the build for the requested target. make ${{ matrix.target }} + - name: Upload build artefacts uses: actions/upload-artifact@v4.3.4 with: @@ -72,8 +74,10 @@ jobs: # Use the development version of Briefcase # python -m pip install git+https://github.com/beeware/briefcase.git python -m pip install git+https://github.com/freakboy3742/briefcase.git@version-bumps + - name: Run support testbed check if: matrix.run-tests + timeout-minutes: 10 working-directory: Python-support-testbed # TODO - remove the template_branch option. run: briefcase run ${{ matrix.target }} Xcode --test ${{ matrix.briefcase-run-args }} -C support_package=\'../dist/Python-${{ steps.config-vars.outputs.PYTHON_VER }}-${{ matrix.target }}-support.custom.tar.gz\' -C template_branch=\'framework-lib\' From 554a58ad1400489ee59125742f1cba0409e04c68 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 4 Aug 2024 20:41:11 +0000 Subject: [PATCH 039/113] Bump actions/upload-artifact from 4.3.4 to 4.3.5 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.4 to 4.3.5. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.3.4...v4.3.5) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yaml | 2 +- .github/workflows/release.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a9b1b6f4..9688eb23 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -54,7 +54,7 @@ jobs: make ${{ matrix.target }} - name: Upload build artefacts - uses: actions/upload-artifact@v4.3.4 + uses: actions/upload-artifact@v4.3.5 with: name: Python-${{ steps.config-vars.outputs.PYTHON_VER }}-${{ matrix.target }}-support.custom.tar.gz path: dist/Python-${{ steps.config-vars.outputs.PYTHON_VER }}-${{ matrix.target }}-support.custom.tar.gz diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index b7495ab8..7abd4202 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -66,7 +66,7 @@ jobs: echo "LIBFFI_VERSION=${LIBFFI_VERSION}" | tee -a ${GITHUB_OUTPUT} - name: Upload Build Artifact - uses: actions/upload-artifact@v4.3.4 + uses: actions/upload-artifact@v4.3.5 with: name: dist-${{ matrix.target }} path: dist From d4978e36a69d8e4988ea4730ea5412fd1b705b3e Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 5 Aug 2024 13:36:38 +0800 Subject: [PATCH 040/113] Update patch to 3.13.0rc1 --- Makefile | 2 +- patch/Python/Python.patch | 493 +++++++++----------------------------- 2 files changed, 109 insertions(+), 386 deletions(-) diff --git a/Makefile b/Makefile index 5d1c4b7c..e036dfa4 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ BUILD_NUMBER=custom # PYTHON_VERSION is the full version number (e.g., 3.10.0b3) # 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.13.0b4 +PYTHON_VERSION=3.13.0rc1 PYTHON_MICRO_VERSION=$(shell echo $(PYTHON_VERSION) | grep -Eo "\d+\.\d+\.\d+") PYTHON_VER=$(basename $(PYTHON_VERSION)) diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index aca98f65..3317a84a 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1,109 +1,3 @@ -diff --git a/Doc/library/urllib.parse.rst b/Doc/library/urllib.parse.rst -index 27909b763e9..fb5353e1895 100644 ---- a/Doc/library/urllib.parse.rst -+++ b/Doc/library/urllib.parse.rst -@@ -22,11 +22,19 @@ - - The module has been designed to match the internet RFC on Relative Uniform - Resource Locators. It supports the following URL schemes: ``file``, ``ftp``, --``gopher``, ``hdl``, ``http``, ``https``, ``imap``, ``mailto``, ``mms``, -+``gopher``, ``hdl``, ``http``, ``https``, ``imap``, ``itms-services``, ``mailto``, ``mms``, - ``news``, ``nntp``, ``prospero``, ``rsync``, ``rtsp``, ``rtsps``, ``rtspu``, - ``sftp``, ``shttp``, ``sip``, ``sips``, ``snews``, ``svn``, ``svn+ssh``, - ``telnet``, ``wais``, ``ws``, ``wss``. - -+.. impl-detail:: -+ -+ The inclusion of the ``itms-services`` URL scheme can prevent an app from -+ passing Apple's App Store review process for the macOS and iOS App Stores. -+ Handling for the ``itms-services`` scheme is always removed on iOS; on -+ macOS, it *may* be removed if CPython has been built with the -+ :option:`--with-app-store-compliance` option. -+ - The :mod:`urllib.parse` module defines functions that fall into two broad - categories: URL parsing and URL quoting. These are covered in detail in - the following sections. -diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst -index 8eaba84e159..2c73c224e4e 100644 ---- a/Doc/using/configure.rst -+++ b/Doc/using/configure.rst -@@ -945,6 +945,17 @@ - Specify the name for the python framework on macOS only valid when - :option:`--enable-framework` is set (default: ``Python``). - -+.. option:: --with-app-store-compliance -+.. option:: --with-app-store-compliance=PATCH-FILE -+ -+ The Python standard library contains strings that are known to trigger -+ automated inspection tool errors when submitted for distribution by -+ the macOS and iOS App Stores. If enabled, this option will apply the list of -+ patches that are known to correct app store compliance. A custom patch -+ file can also be specified. This option is disabled by default. -+ -+ .. versionadded:: 3.13 -+ - iOS Options - ----------- - -diff --git a/Doc/using/ios.rst b/Doc/using/ios.rst -index 70a81fd8a53..5d0924be8d9 100644 ---- a/Doc/using/ios.rst -+++ b/Doc/using/ios.rst -@@ -323,3 +323,21 @@ - - * If you're using a separate folder for third-party packages, ensure that folder - is included as part of the ``PYTHONPATH`` configuration in step 10. -+ -+App Store Compliance -+==================== -+ -+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. -+ -+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 -+review rules cannot be challenged; so, it is necessary to modify the Python -+standard library for an app to pass App Store review. -+ -+The Python source tree contains -+: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. -diff --git a/Doc/using/mac.rst b/Doc/using/mac.rst -index 31d37aad2a7..44fb00de373 100644 ---- a/Doc/using/mac.rst -+++ b/Doc/using/mac.rst -@@ -188,6 +188,28 @@ - * `PyInstaller `__: A cross-platform packaging tool that creates - a single file or folder as a distributable artifact. - -+App Store Compliance -+-------------------- -+ -+Apps submitted for distribution through the macOS App Store 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. -+ -+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 -+review rules cannot be challenged. Therefore, it is necessary to modify the -+Python standard library for an app to pass App Store review. -+ -+The Python source tree contains -+: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 CPython is configured with the -+:option:`--with-app-store-compliance` option. -+ -+This patch is not normally required to use CPython on a Mac; nor is it required -+if you are distributing an app *outside* the macOS App Store. It is *only* -+required if you are using the macOS App Store as a distribution channel. -+ - Other Resources - =============== - diff --git a/Lib/platform.py b/Lib/platform.py index 5958382276e..5db5eb276a2 100755 --- a/Lib/platform.py @@ -219,113 +113,24 @@ index 5958382276e..5db5eb276a2 100755 macos_release = mac_ver()[0] if macos_release: diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py -index 83e057c177f..d18a7564866 100644 +index 80aef344711..d18a7564866 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py -@@ -642,7 +642,15 @@ - release = m.group() - elif osname[:6] == "darwin": - if sys.platform == "ios": -- release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "12.0") -+ release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0") -+ osname = sys.platform -+ machine = sys.implementation._multiarch +@@ -645,6 +645,14 @@ + 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 ++ osname = sys.platform ++ machine = sys.implementation._multiarch else: ---- /dev/null -+++ b/Mac/Resources/app-store-compliance.patch -@@ -0,0 +1,29 @@ -+diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py -+index d6c83a75c1c..19ed4e01091 100644 -+--- a/Lib/test/test_urlparse.py -++++ b/Lib/test/test_urlparse.py -+@@ -237,11 +237,6 @@ def test_roundtrips(self): -+ '','',''), -+ ('git+ssh', 'git@github.com','/user/project.git', -+ '', '')), -+- ('itms-services://?action=download-manifest&url=https://example.com/app', -+- ('itms-services', '', '', '', -+- 'action=download-manifest&url=https://example.com/app', ''), -+- ('itms-services', '', '', -+- 'action=download-manifest&url=https://example.com/app', '')), -+ ('+scheme:path/to/file', -+ ('', '', '+scheme:path/to/file', '', '', ''), -+ ('', '', '+scheme:path/to/file', '', '')), -+diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py -+index 8f724f907d4..148caf742c9 100644 -+--- a/Lib/urllib/parse.py -++++ b/Lib/urllib/parse.py -+@@ -59,7 +59,7 @@ -+ 'imap', 'wais', 'file', 'mms', 'https', 'shttp', -+ 'snews', 'prospero', 'rtsp', 'rtsps', 'rtspu', 'rsync', -+ 'svn', 'svn+ssh', 'sftp', 'nfs', 'git', 'git+ssh', -+- 'ws', 'wss', 'itms-services'] -++ 'ws', 'wss'] -+ -+ uses_params = ['', 'ftp', 'hdl', 'prospero', 'http', 'imap', -+ 'https', 'shttp', 'rtsp', 'rtsps', 'rtspu', 'sip', -diff --git a/Makefile.pre.in b/Makefile.pre.in -index 76653f399f4..ab2a67040d1 100644 ---- a/Makefile.pre.in -+++ b/Makefile.pre.in -@@ -179,6 +179,9 @@ - EXE= @EXEEXT@ - BUILDEXE= @BUILDEXEEXT@ - -+# Name of the patch file to apply for app store compliance -+APP_STORE_COMPLIANCE_PATCH=@APP_STORE_COMPLIANCE_PATCH@ -+ - # Short name and location for Mac OS X Python framework - UNIVERSALSDK=@UNIVERSALSDK@ - PYTHONFRAMEWORK= @PYTHONFRAMEWORK@ -@@ -692,7 +695,7 @@ - @grep -E '^[A-Za-z][-A-Za-z0-9]+:' Makefile | awk -F : '{print $$1}' - - .PHONY: build_all --build_all: check-clean-src $(BUILDPYTHON) platform sharedmods \ -+build_all: check-clean-src check-app-store-compliance $(BUILDPYTHON) platform sharedmods \ - gdbhooks Programs/_testembed scripts checksharedmods rundsymutil - - .PHONY: build_wasm -@@ -715,6 +718,16 @@ - exit 1; \ - fi - -+# Check that the app store compliance patch can be applied (if configured). -+# This is checked as a dry-run against the original library sources; -+# the patch will be actually applied during the install phase. -+.PHONY: check-app-store-compliance -+check-app-store-compliance: -+ @if [ "$(APP_STORE_COMPLIANCE_PATCH)" != "" ]; then \ -+ patch --dry-run --quiet --force --strip 1 --directory "$(abs_srcdir)" --input "$(abs_srcdir)/$(APP_STORE_COMPLIANCE_PATCH)"; \ -+ echo "App store compliance patch can be applied."; \ -+ fi -+ - # Profile generation build must start from a clean tree. - profile-clean-stamp: - $(MAKE) clean -@@ -2568,6 +2581,14 @@ - $(INSTALL_DATA) `cat pybuilddir.txt`/_sysconfigdata_$(ABIFLAGS)_$(MACHDEP)_$(MULTIARCH).py \ - $(DESTDIR)$(LIBDEST); \ - $(INSTALL_DATA) $(srcdir)/LICENSE $(DESTDIR)$(LIBDEST)/LICENSE.txt -+ @ # If app store compliance has been configured, apply the patch to the -+ @ # installed library code. The patch has been previously validated against -+ @ # the original source tree, so we can ignore any errors that are raised -+ @ # due to files that are missing because of --disable-test-modules etc. -+ @if [ "$(APP_STORE_COMPLIANCE_PATCH)" != "" ]; then \ -+ echo "Applying app store compliance patch"; \ -+ patch --force --reject-file "$(abs_builddir)/app-store-compliance.rej" --strip 2 --directory "$(DESTDIR)$(LIBDEST)" --input "$(abs_srcdir)/$(APP_STORE_COMPLIANCE_PATCH)" || true ; \ -+ fi - @ # Build PYC files for the 3 optimization levels (0, 1, 2) - -PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ - $(PYTHON_FOR_BUILD) -Wi $(DESTDIR)$(LIBDEST)/compileall.py \ + import _osx_support + osname, release, machine = _osx_support.get_platform_osx( diff --git a/Misc/platform_triplet.c b/Misc/platform_triplet.c index ec0857a4a99..2350e9dc821 100644 --- a/Misc/platform_triplet.c @@ -358,10 +163,10 @@ index ec0857a4a99..2350e9dc821 100644 # elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX PLATFORM_TRIPLET=darwin diff --git a/configure b/configure -index bff4f6ceb4a..1ce51fcffeb 100755 +index beffc1fd76c..b2daf3467cd 100755 --- a/configure +++ b/configure -@@ -978,10 +978,13 @@ +@@ -978,6 +978,8 @@ CFLAGS CC HAS_XCRUN @@ -370,31 +175,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 IPHONEOS_DEPLOYMENT_TARGET EXPORT_MACOSX_DEPLOYMENT_TARGET CONFIGURE_MACOSX_DEPLOYMENT_TARGET - _PYTHON_HOST_PLATFORM -+APP_STORE_COMPLIANCE_PATCH - INSTALLTARGETS - FRAMEWORKINSTALLAPPSPREFIX - FRAMEWORKUNIXTOOLSPREFIX -@@ -1077,6 +1080,7 @@ - with_universal_archs - with_framework_name - enable_framework -+with_app_store_compliance - with_emscripten_target - enable_wasm_dynamic_linking - enable_wasm_pthreads -@@ -1856,6 +1860,10 @@ - specify the name for the python framework on macOS - only valid when --enable-framework is set. see - Mac/README.rst (default is 'Python') -+ --with-app-store-compliance=[PATCH-FILE] -+ Enable any patches required for compiliance with app -+ stores. Optional PATCH-FILE specifies the custom -+ patch to apply. - --with-emscripten-target=[browser|node] - Emscripten platform - --with-suffix=SUFFIX set executable suffix to SUFFIX (default is empty, -@@ -4048,6 +4056,12 @@ +@@ -4054,6 +4056,12 @@ *-apple-ios*) ac_sys_system=iOS ;; @@ -407,7 +188,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 *-*-vxworks*) ac_sys_system=VxWorks ;; -@@ -4102,7 +4116,7 @@ +@@ -4108,7 +4116,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. # @@ -416,7 +197,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 # 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 -@@ -4117,6 +4131,14 @@ +@@ -4123,6 +4131,14 @@ 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 ;; @@ -431,7 +212,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 *) esac fi -@@ -4125,6 +4147,14 @@ +@@ -4131,6 +4147,14 @@ 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 ;; @@ -446,7 +227,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 *) esac fi -@@ -4133,6 +4163,14 @@ +@@ -4139,6 +4163,14 @@ 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 ;; @@ -461,7 +242,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 *) esac fi -@@ -4141,6 +4179,14 @@ +@@ -4147,6 +4179,14 @@ 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 ;; @@ -476,7 +257,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 *) esac fi -@@ -4261,8 +4307,10 @@ +@@ -4267,8 +4307,10 @@ case $enableval in yes) case $ac_sys_system in @@ -489,7 +270,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 esac esac -@@ -4271,6 +4319,8 @@ +@@ -4277,6 +4319,8 @@ no) case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -498,7 +279,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4377,6 +4427,36 @@ +@@ -4383,6 +4427,36 @@ ac_config_files="$ac_config_files iOS/Resources/Info.plist" @@ -535,7 +316,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 ;; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 -@@ -4388,6 +4468,8 @@ +@@ -4394,6 +4468,8 @@ case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -544,61 +325,29 @@ index bff4f6ceb4a..1ce51fcffeb 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4431,6 +4513,53 @@ - printf "%s\n" "#define _PYTHONFRAMEWORK \"${PYTHONFRAMEWORK}\"" >>confdefs.h - - -+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --with-app-store-compliance" >&5 -+printf %s "checking for --with-app-store-compliance... " >&6; } -+ -+# Check whether --with-app_store_compliance was given. -+if test ${with_app_store_compliance+y} -+then : -+ withval=$with_app_store_compliance; -+ case "$withval" in -+ yes) -+ case $ac_sys_system in +@@ -4447,8 +4523,8 @@ + case "$withval" in + yes) + case $ac_sys_system in +- Darwin|iOS) +- # iOS is able to share the macOS patch + Darwin|iOS|tvOS|watchOS) + # iOS/tvOS/watchOS is able to share the macOS patch -+ APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" -+ ;; -+ *) as_fn_error $? "no default app store compliance patch available for $ac_sys_system" "$LINENO" 5 ;; -+ esac -+ { 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; } -+ ;; -+ *) -+ APP_STORE_COMPLIANCE_PATCH="${withval}" -+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: applying custom app store compliance patch" >&5 -+printf "%s\n" "applying custom app store compliance patch" >&6; } -+ ;; -+ esac -+ -+else $as_nop -+ -+ case $ac_sys_system in + 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 ;; +@@ -4466,8 +4542,8 @@ + else $as_nop + + case $ac_sys_system in +- iOS) +- # Always apply the compliance patch on iOS; we can use the macOS patch + iOS|tvOS|watchOS) + # Always apply the compliance patch on iOS/tvOS/watchOS; we can use the macOS patch -+ APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" -+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: applying default app store compliance patch" >&5 -+printf "%s\n" "applying default app store compliance patch" >&6; } -+ ;; -+ *) -+ # No default app compliance patching on any other platform -+ APP_STORE_COMPLIANCE_PATCH= -+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: not patching for app store compliance" >&5 -+printf "%s\n" "not patching for app store compliance" >&6; } -+ ;; -+ esac -+ -+fi -+ -+ -+ - - if test "$cross_compiling" = yes; then - case "$host" in -@@ -4468,6 +4597,50 @@ + 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; } +@@ -4521,6 +4597,50 @@ ;; esac ;; @@ -649,7 +398,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 *-*-vxworks*) _host_ident=$host_cpu ;; -@@ -4546,9 +4719,13 @@ +@@ -4599,9 +4719,13 @@ define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; @@ -664,7 +413,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -4611,7 +4788,10 @@ +@@ -4664,7 +4788,10 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' @@ -676,7 +425,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 # checks for alternative programs -@@ -4652,6 +4832,16 @@ +@@ -4705,6 +4832,16 @@ as_fn_append CFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" as_fn_append LDFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" ;; #( @@ -693,7 +442,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 *) : ;; esac -@@ -6953,6 +7143,10 @@ +@@ -7006,6 +7143,10 @@ MULTIARCH="" ;; #( iOS) : MULTIARCH="" ;; #( @@ -704,7 +453,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -6973,7 +7167,7 @@ +@@ -7026,7 +7167,7 @@ printf "%s\n" "$MULTIARCH" >&6; } case $ac_sys_system in #( @@ -713,7 +462,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2` ;; #( *) : SOABI_PLATFORM=$PLATFORM_TRIPLET -@@ -7024,6 +7218,14 @@ +@@ -7077,6 +7218,14 @@ PY_SUPPORT_TIER=3 ;; #( aarch64-apple-ios*/clang) : PY_SUPPORT_TIER=3 ;; #( @@ -728,7 +477,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 aarch64-*-linux-android/clang) : PY_SUPPORT_TIER=3 ;; #( x86_64-*-linux-android/clang) : -@@ -7494,7 +7696,7 @@ +@@ -7547,7 +7696,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; @@ -737,7 +486,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5;; -@@ -7560,7 +7762,7 @@ +@@ -7613,7 +7762,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -746,7 +495,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -12895,7 +13097,7 @@ +@@ -12948,7 +13097,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -755,7 +504,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -13028,7 +13230,7 @@ +@@ -13081,7 +13230,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -764,7 +513,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -13052,7 +13254,7 @@ +@@ -13105,7 +13254,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -773,7 +522,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -14451,7 +14653,7 @@ +@@ -14504,7 +14653,7 @@ ctypes_malloc_closure=yes ;; #( @@ -782,7 +531,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 ctypes_malloc_closure=yes ;; #( -@@ -17902,12 +18104,6 @@ +@@ -17955,12 +18104,6 @@ then : printf "%s\n" "#define HAVE_DUP3 1" >>confdefs.h @@ -795,7 +544,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 fi ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes -@@ -17968,18 +18164,6 @@ +@@ -18021,18 +18164,6 @@ then : printf "%s\n" "#define HAVE_FEXECVE 1" >>confdefs.h @@ -814,7 +563,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 fi ac_fn_c_check_func "$LINENO" "fpathconf" "ac_cv_func_fpathconf" if test "x$ac_cv_func_fpathconf" = xyes -@@ -18406,24 +18590,6 @@ +@@ -18459,24 +18590,6 @@ then : printf "%s\n" "#define HAVE_POSIX_OPENPT 1" >>confdefs.h @@ -839,7 +588,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 fi ac_fn_c_check_func "$LINENO" "pread" "ac_cv_func_pread" if test "x$ac_cv_func_pread" = xyes -@@ -18712,12 +18878,6 @@ +@@ -18765,12 +18878,6 @@ then : printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h @@ -852,7 +601,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 fi ac_fn_c_check_func "$LINENO" "sigfillset" "ac_cv_func_sigfillset" if test "x$ac_cv_func_sigfillset" = xyes -@@ -18986,11 +19146,11 @@ +@@ -19039,11 +19146,11 @@ fi @@ -866,7 +615,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" if test "x$ac_cv_func_getentropy" = xyes then : -@@ -19012,6 +19172,53 @@ +@@ -19065,6 +19172,53 @@ fi @@ -920,7 +669,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 { 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} -@@ -21808,7 +22015,8 @@ +@@ -21861,7 +22015,8 @@ # check for openpty, login_tty, and forkpty @@ -930,7 +679,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 for ac_func in openpty do : -@@ -21904,7 +22112,7 @@ +@@ -21957,7 +22112,7 @@ fi done @@ -939,7 +688,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 printf %s "checking for library containing login_tty... " >&6; } if test ${ac_cv_search_login_tty+y} then : -@@ -22061,6 +22269,7 @@ +@@ -22114,6 +22269,7 @@ fi done @@ -947,7 +696,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 # check for long file support functions ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" -@@ -22307,10 +22516,10 @@ +@@ -22360,10 +22516,10 @@ done @@ -960,7 +709,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 then for ac_func in clock_settime -@@ -24537,8 +24746,8 @@ +@@ -24590,8 +24746,8 @@ MODULE_LDFLAGS="\$(BLDLIBRARY)" fi @@ -971,7 +720,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -27238,7 +27447,7 @@ +@@ -27239,7 +27395,7 @@ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for device files" >&5 printf "%s\n" "$as_me: checking for device files" >&6;} @@ -980,7 +729,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -27671,7 +27880,7 @@ +@@ -27672,7 +27828,7 @@ with_ensurepip=no ;; #( WASI) : with_ensurepip=no ;; #( @@ -989,7 +738,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 with_ensurepip=no ;; #( *) : with_ensurepip=upgrade -@@ -28698,7 +28907,7 @@ +@@ -28699,7 +28855,7 @@ ;; #( Darwin) : ;; #( @@ -998,7 +747,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 -@@ -32463,6 +32672,8 @@ +@@ -32464,6 +32620,8 @@ "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" ;; @@ -1008,7 +757,7 @@ index bff4f6ceb4a..1ce51fcffeb 100755 "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 65975732f72..940136f3311 100644 +index 5842bd24c45..f97279331f6 100644 --- a/configure.ac +++ b/configure.ac @@ -330,6 +330,12 @@ @@ -1159,55 +908,29 @@ index 65975732f72..940136f3311 100644 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -695,6 +767,47 @@ - AC_DEFINE_UNQUOTED([_PYTHONFRAMEWORK], ["${PYTHONFRAMEWORK}"], - [framework name]) - -+dnl quadrigraphs "@<:@" and "@:>@" produce "[" and "]" in the output -+AC_MSG_CHECKING([for --with-app-store-compliance]) -+AC_ARG_WITH( -+ [app_store_compliance], -+ [AS_HELP_STRING( -+ [--with-app-store-compliance=@<:@PATCH-FILE@:>@], -+ [Enable any patches required for compiliance with app stores. -+ Optional PATCH-FILE specifies the custom patch to apply.] -+ )],[ -+ case "$withval" in -+ yes) -+ case $ac_sys_system in +@@ -707,8 +779,8 @@ + case "$withval" in + yes) + case $ac_sys_system in +- Darwin|iOS) +- # iOS is able to share the macOS patch + Darwin|iOS|tvOS|watchOS) + # iOS/tvOS/watchOS is able to share the macOS patch -+ APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" -+ ;; -+ *) AC_MSG_ERROR([no default app store compliance patch available for $ac_sys_system]) ;; -+ esac -+ AC_MSG_RESULT([applying default app store compliance patch]) -+ ;; -+ *) -+ APP_STORE_COMPLIANCE_PATCH="${withval}" -+ AC_MSG_RESULT([applying custom app store compliance patch]) -+ ;; -+ esac -+ ],[ -+ case $ac_sys_system in + APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" + ;; + *) AC_MSG_ERROR([no default app store compliance patch available for $ac_sys_system]) ;; +@@ -722,8 +794,8 @@ + esac + ],[ + case $ac_sys_system in +- iOS) +- # Always apply the compliance patch on iOS; we can use the macOS patch + iOS|tvOS|watchOS) + # Always apply the compliance patch on iOS/tvOS/watchOS; we can use the macOS patch -+ APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" -+ AC_MSG_RESULT([applying default app store compliance patch]) -+ ;; -+ *) -+ # No default app compliance patching on any other platform -+ APP_STORE_COMPLIANCE_PATCH= -+ AC_MSG_RESULT([not patching for app store compliance]) -+ ;; -+ esac -+]) -+AC_SUBST([APP_STORE_COMPLIANCE_PATCH]) -+ - AC_SUBST([_PYTHON_HOST_PLATFORM]) - if test "$cross_compiling" = yes; then - case "$host" in -@@ -730,6 +843,46 @@ + APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" + AC_MSG_RESULT([applying default app store compliance patch]) + ;; +@@ -771,6 +843,46 @@ ;; esac ;; @@ -1254,7 +977,7 @@ index 65975732f72..940136f3311 100644 *-*-vxworks*) _host_ident=$host_cpu ;; -@@ -807,9 +960,13 @@ +@@ -848,9 +960,13 @@ define_xopen_source=no;; Darwin/@<:@[12]@:>@@<:@0-9@:>@.*) define_xopen_source=no;; @@ -1269,7 +992,7 @@ index 65975732f72..940136f3311 100644 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -868,8 +1025,11 @@ +@@ -909,8 +1025,11 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' @@ -1282,7 +1005,7 @@ index 65975732f72..940136f3311 100644 # checks for alternative programs -@@ -903,11 +1063,17 @@ +@@ -944,11 +1063,17 @@ ], ) @@ -1301,7 +1024,7 @@ index 65975732f72..940136f3311 100644 ], ) -@@ -1095,6 +1261,8 @@ +@@ -1136,6 +1261,8 @@ AS_CASE([$ac_sys_system], [Darwin*], [MULTIARCH=""], [iOS], [MULTIARCH=""], @@ -1310,7 +1033,7 @@ index 65975732f72..940136f3311 100644 [FreeBSD*], [MULTIARCH=""], [MULTIARCH=$($CC --print-multiarch 2>/dev/null)] ) -@@ -1116,7 +1284,7 @@ +@@ -1157,7 +1284,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], @@ -1319,7 +1042,7 @@ index 65975732f72..940136f3311 100644 [SOABI_PLATFORM=$PLATFORM_TRIPLET] ) -@@ -1150,6 +1318,10 @@ +@@ -1191,6 +1318,10 @@ [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 @@ -1330,7 +1053,7 @@ index 65975732f72..940136f3311 100644 [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 -@@ -1481,7 +1653,7 @@ +@@ -1522,7 +1653,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; @@ -1339,7 +1062,7 @@ index 65975732f72..940136f3311 100644 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) AC_MSG_ERROR([Unknown platform for framework build]);; -@@ -1546,7 +1718,7 @@ +@@ -1587,7 +1718,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -1348,7 +1071,7 @@ index 65975732f72..940136f3311 100644 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -3417,7 +3589,7 @@ +@@ -3458,7 +3589,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -1357,7 +1080,7 @@ index 65975732f72..940136f3311 100644 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -3541,7 +3713,7 @@ +@@ -3582,7 +3713,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -1366,7 +1089,7 @@ index 65975732f72..940136f3311 100644 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -3565,7 +3737,7 @@ +@@ -3606,7 +3737,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -1375,7 +1098,7 @@ index 65975732f72..940136f3311 100644 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -3949,7 +4121,7 @@ +@@ -3990,7 +4121,7 @@ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], @@ -1384,7 +1107,7 @@ index 65975732f72..940136f3311 100644 ctypes_malloc_closure=yes ], [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] -@@ -5043,9 +5215,9 @@ +@@ -5084,9 +5215,9 @@ # checks for library functions AC_CHECK_FUNCS([ \ accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ @@ -1396,7 +1119,7 @@ index 65975732f72..940136f3311 100644 gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \ getgrnam_r getgrouplist gethostname getitimer getloadavg getlogin \ getpeername getpgid getpid getppid getpriority _getpty \ -@@ -5053,15 +5225,14 @@ +@@ -5094,15 +5225,14 @@ 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 \ @@ -1414,7 +1137,7 @@ index 65975732f72..940136f3311 100644 sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ -@@ -5076,12 +5247,20 @@ +@@ -5117,12 +5247,20 @@ AC_CHECK_FUNCS([lchmod]) fi @@ -1438,7 +1161,7 @@ index 65975732f72..940136f3311 100644 fi AC_CHECK_DECL([dirfd], -@@ -5332,20 +5511,22 @@ +@@ -5373,20 +5511,22 @@ ]) # check for openpty, login_tty, and forkpty @@ -1475,7 +1198,7 @@ index 65975732f72..940136f3311 100644 # check for long file support functions AC_CHECK_FUNCS([fseek64 fseeko fstatvfs ftell64 ftello statvfs]) -@@ -5384,10 +5565,10 @@ +@@ -5425,10 +5565,10 @@ ]) ]) @@ -1488,7 +1211,7 @@ index 65975732f72..940136f3311 100644 then AC_CHECK_FUNCS([clock_settime], [], [ AC_CHECK_LIB([rt], [clock_settime], [ -@@ -6135,8 +6316,8 @@ +@@ -6176,8 +6316,8 @@ MODULE_LDFLAGS="\$(BLDLIBRARY)" fi @@ -1499,7 +1222,7 @@ index 65975732f72..940136f3311 100644 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -6772,7 +6953,7 @@ +@@ -6785,7 +6925,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. @@ -1508,7 +1231,7 @@ index 65975732f72..940136f3311 100644 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -7029,7 +7210,7 @@ +@@ -7042,7 +7182,7 @@ AS_CASE([$ac_sys_system], [Emscripten], [with_ensurepip=no], [WASI], [with_ensurepip=no], @@ -1517,7 +1240,7 @@ index 65975732f72..940136f3311 100644 [with_ensurepip=upgrade] ) ]) -@@ -7439,7 +7620,7 @@ +@@ -7452,7 +7592,7 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], From 912f2d9753677ea7946c8919a872a11cd9b24b30 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 5 Aug 2024 13:49:21 +0800 Subject: [PATCH 041/113] Include the app store compliance patch. --- Makefile | 3 +++ README.rst | 3 ++- patch/Python/app-store-compliance.patch | 29 +++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 patch/Python/app-store-compliance.patch diff --git a/Makefile b/Makefile index e036dfa4..59828993 100644 --- a/Makefile +++ b/Makefile @@ -542,6 +542,9 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ mkdir -p $$(PYTHON_FRAMEWORK-macosx) tar zxf build/macOS/macosx/python-$(PYTHON_VERSION)/Python_Framework.pkgPython_Framework.pkg/PayloadPython_Framework.pkgPython_Framework.pkg/PayloadPython_Framework.pkgPython_Framework.pkg/Payload -C $$(PYTHON_FRAMEWORK-macosx) -X patch/Python/release.macOS.exclude + # Apply the App Store compliance patch + patch --strip 2 --directory $$(PYTHON_INSTALL_VERSION-macosx)/lib/python$(PYTHON_VER) --input $(PROJECT_DIR)/patch/Python/app-store-compliance.patch + # Rewrite the framework to make it standalone patch/make-relocatable.sh $$(PYTHON_INSTALL_VERSION-macosx) 2>&1 > /dev/null diff --git a/README.rst b/README.rst index 88b52ab9..b1b3147c 100644 --- a/README.rst +++ b/README.rst @@ -20,7 +20,8 @@ incorporated into an XCode project. The binary modules in the Python standard library are distributed as binaries that can be dynamically loaded at runtime. The macOS package is a re-bundling of the official macOS binary, modified so that -it is relocatable, with the IDLE, Tkinter and turtle packages removed. +it is relocatable, with the IDLE, Tkinter and turtle packages removed, and the +App Store compliance patch applied. The iOS, tvOS and watchOS packages compiled by this project use the official `PEP 730 `__ code that is part of Python 3.13 diff --git a/patch/Python/app-store-compliance.patch b/patch/Python/app-store-compliance.patch new file mode 100644 index 00000000..f4b7decc --- /dev/null +++ b/patch/Python/app-store-compliance.patch @@ -0,0 +1,29 @@ +diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py +index d6c83a75c1c..19ed4e01091 100644 +--- a/Lib/test/test_urlparse.py ++++ b/Lib/test/test_urlparse.py +@@ -237,11 +237,6 @@ def test_roundtrips(self): + '','',''), + ('git+ssh', 'git@github.com','/user/project.git', + '', '')), +- ('itms-services://?action=download-manifest&url=https://example.com/app', +- ('itms-services', '', '', '', +- 'action=download-manifest&url=https://example.com/app', ''), +- ('itms-services', '', '', +- 'action=download-manifest&url=https://example.com/app', '')), + ('+scheme:path/to/file', + ('', '', '+scheme:path/to/file', '', '', ''), + ('', '', '+scheme:path/to/file', '', '')), +diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py +index 8f724f907d4..148caf742c9 100644 +--- a/Lib/urllib/parse.py ++++ b/Lib/urllib/parse.py +@@ -59,7 +59,7 @@ + 'imap', 'wais', 'file', 'mms', 'https', 'shttp', + 'snews', 'prospero', 'rtsp', 'rtsps', 'rtspu', 'rsync', + 'svn', 'svn+ssh', 'sftp', 'nfs', 'git', 'git+ssh', +- 'ws', 'wss', 'itms-services'] ++ 'ws', 'wss'] + + uses_params = ['', 'ftp', 'hdl', 'prospero', 'http', 'imap', + 'https', 'shttp', 'rtsp', 'rtsps', 'rtspu', 'sip', From ff5adbbc1092f8525105325075d8cf712d82aad4 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 5 Aug 2024 15:27:56 +0800 Subject: [PATCH 042/113] Remove tool definitions from configure invocation. --- Makefile | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Makefile b/Makefile index 59828993..8fa2cb2b 100644 --- a/Makefile +++ b/Makefile @@ -124,10 +124,8 @@ ARCH-$(target)=$$(subst .,,$$(suffix $(target))) ifneq ($(os),macOS) ifeq ($$(findstring simulator,$$(SDK-$(target))),) TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(OS_LOWER-$(target))$$(VERSION_MIN-$(os)) -TARGET_TOOL_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(OS_LOWER-$(target)) else TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(OS_LOWER-$(target))$$(VERSION_MIN-$(os))-simulator -TARGET_TOOL_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(OS_LOWER-$(target))-simulator endif endif @@ -280,10 +278,6 @@ $$(PYTHON_SRCDIR-$(target))/Makefile: \ cd $$(PYTHON_SRCDIR-$(target)) && \ PATH="$(PROJECT_DIR)/$$(PYTHON_SRCDIR-$(target))/$(os)/Resources/bin:$(PATH)" \ ./configure \ - AR=$$(TARGET_TOOL_TRIPLE-$(target))-ar \ - CC=$$(TARGET_TOOL_TRIPLE-$(target))-clang \ - CPP=$$(TARGET_TOOL_TRIPLE-$(target))-cpp \ - CXX=$$(TARGET_TOOL_TRIPLE-$(target))-clang \ LIBLZMA_CFLAGS="-I$$(XZ_INSTALL-$(target))/include" \ LIBLZMA_LIBS="-L$$(XZ_INSTALL-$(target))/lib -llzma" \ BZIP2_CFLAGS="-I$$(BZIP2_INSTALL-$(target))/include" \ From 68ea0febaeb929f232b32ddeb7842103231db6ec Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 6 Aug 2024 11:23:25 +0800 Subject: [PATCH 043/113] Differentiate the binary package version number from the source version number. --- .github/workflows/ci.yaml | 4 ++++ Makefile | 13 +++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9688eb23..380a313a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,6 +1,10 @@ name: CI on: pull_request: + push: + branches: + - main + - 3.* env: FORCE_COLOR: "1" diff --git a/Makefile b/Makefile index 8fa2cb2b..e579690f 100644 --- a/Makefile +++ b/Makefile @@ -13,10 +13,15 @@ BUILD_NUMBER=custom # Version of packages that will be compiled by this meta-package # PYTHON_VERSION is the full version number (e.g., 3.10.0b3) +# PYTHON_PKG_VERSION is the version number with binary package releases to use +# for macOS binaries. This will be less than PYTHON_VERSION towards the end +# 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.13.0rc1 +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+") PYTHON_VER=$(basename $(PYTHON_VERSION)) # The binary releases of dependencies, published at: @@ -96,11 +101,11 @@ downloads/Python-$(PYTHON_VERSION).tar.gz: curl $(CURL_FLAGS) -o $@ \ https://www.python.org/ftp/python/$(PYTHON_MICRO_VERSION)/Python-$(PYTHON_VERSION).tgz -downloads/python-$(PYTHON_VERSION)-macos11.pkg: +downloads/python-$(PYTHON_PKG_VERSION)-macos11.pkg: @echo ">>> Download macOS Python package" mkdir -p downloads curl $(CURL_FLAGS) -o $@ \ - https://www.python.org/ftp/python/$(PYTHON_MICRO_VERSION)/python-$(PYTHON_VERSION)-macos11.pkg + https://www.python.org/ftp/python/$(PYTHON_PKG_MICRO_VERSION)/python-$(PYTHON_PKG_VERSION)-macos11.pkg ########################################################################### # Build for specified target (from $(TARGETS-*)) @@ -522,7 +527,7 @@ ifeq ($(os),macOS) PYTHON_FRAMEWORK-$(os)=$$(PYTHON_INSTALL-$(sdk))/Python.framework $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ - downloads/python-$(PYTHON_VERSION)-macos11.pkg + downloads/python-$(PYTHON_PKG_VERSION)-macos11.pkg @echo ">>> Repackage macOS package as XCFramework" # Unpack .pkg file. It turns out .pkg files are readable by tar... although @@ -530,7 +535,7 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ # is a tarball that contains additional tarballs; the inner tarball has the # "payload" that is the framework. mkdir -p build/macOS/macosx/python-$(PYTHON_VERSION) - tar zxf downloads/python-$(PYTHON_VERSION)-macos11.pkg -C build/macOS/macosx/python-$(PYTHON_VERSION) + tar zxf downloads/python-$(PYTHON_PKG_VERSION)-macos11.pkg -C build/macOS/macosx/python-$(PYTHON_VERSION) # Unpack payload inside .pkg file mkdir -p $$(PYTHON_FRAMEWORK-macosx) From 600b9e07e025f44806a0cb67d14a2d6b074dee0b Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 6 Aug 2024 11:26:00 +0800 Subject: [PATCH 044/113] Normalize ordering of dependent libraries. --- .github/workflows/release.yaml | 15 +++++++++------ Makefile | 24 ++++++++++++------------ 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 7abd4202..b49127c5 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -55,15 +55,17 @@ jobs: run: | PYTHON_VERSION=$(grep "Python version:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 3) BZIP2_VERSION=$(grep "BZip2:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) - XZ_VERSION=$(grep "XZ:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) - OPENSSL_VERSION=$(grep "OpenSSL:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) LIBFFI_VERSION=$(grep "libFFI:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) + MPDECIMAL_VERSION=$(grep "mpdecimal:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) + OPENSSL_VERSION=$(grep "OpenSSL:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) + XZ_VERSION=$(grep "XZ:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) echo "PYTHON_VERSION=${PYTHON_VERSION}" | tee -a ${GITHUB_OUTPUT} echo "BZIP2_VERSION=${BZIP2_VERSION}" | tee -a ${GITHUB_OUTPUT} - echo "XZ_VERSION=${XZ_VERSION}" | tee -a ${GITHUB_OUTPUT} - echo "OPENSSL_VERSION=${OPENSSL_VERSION}" | tee -a ${GITHUB_OUTPUT} echo "LIBFFI_VERSION=${LIBFFI_VERSION}" | tee -a ${GITHUB_OUTPUT} + 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} - name: Upload Build Artifact uses: actions/upload-artifact@v4.3.5 @@ -95,8 +97,9 @@ jobs: Includes: * Python ${{ needs.build.outputs.PYTHON_VERSION }} - * OpenSSL ${{ needs.build.outputs.OPENSSL_VERSION }} * BZip2 ${{ needs.build.outputs.BZIP2_VERSION }} + * libFFI ${{ needs.build.outputs.LIBFFI_VERSION }} + * mpdecimal ${{ needs.build.outputs.MPDECIMAL_VERSION }} + * OpenSSL ${{ needs.build.outputs.OPENSSL_VERSION }} * XZ ${{ needs.build.outputs.XZ_VERSION }} - * LibFFI ${{ needs.build.outputs.LIBFFI_VERSION }} artifacts: "dist/*" diff --git a/Makefile b/Makefile index e579690f..a05906cb 100644 --- a/Makefile +++ b/Makefile @@ -27,10 +27,10 @@ PYTHON_VER=$(basename $(PYTHON_VERSION)) # The binary releases of dependencies, published at: # https://github.com/beeware/cpython-apple-source-deps/releases BZIP2_VERSION=1.0.8-1 +LIBFFI_VERSION=3.4.6-1 MPDECIMAL_VERSION=4.0.0-1 OPENSSL_VERSION=3.0.14-1 XZ_VERSION=5.4.7-1 -LIBFFI_VERSION=3.4.6-1 # Supported OS OS_LIST=macOS iOS tvOS watchOS @@ -263,10 +263,10 @@ PYTHON_STDLIB-$(target)=$$(PYTHON_INSTALL-$(target))/lib/python$(PYTHON_VER) $$(PYTHON_SRCDIR-$(target))/configure: \ downloads/Python-$(PYTHON_VERSION).tar.gz \ $$(BZIP2_LIB-$(target)) \ - $$(XZ_LIB-$(target)) \ - $$(OPENSSL_SSL_LIB-$(target)) \ + $$(LIBFFI_LIB-$(target)) \ $$(MPDECIMAL_LIB-$(target)) \ - $$(LIBFFI_LIB-$(target)) + $$(OPENSSL_SSL_LIB-$(target)) \ + $$(XZ_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)) @@ -341,14 +341,14 @@ vars-$(target): @echo "SDK_ROOT-$(target): $$(SDK_ROOT-$(target))" @echo "BZIP2_INSTALL-$(target): $$(BZIP2_INSTALL-$(target))" @echo "BZIP2_LIB-$(target): $$(BZIP2_LIB-$(target))" - @echo "XZ_INSTALL-$(target): $$(XZ_INSTALL-$(target))" - @echo "XZ_LIB-$(target): $$(XZ_LIB-$(target))" - @echo "OPENSSL_INSTALL-$(target): $$(OPENSSL_INSTALL-$(target))" - @echo "OPENSSL_SSL_LIB-$(target): $$(OPENSSL_SSL_LIB-$(target))" - @echo "MPDECIMAL_INSTALL-$(target): $$(MPDECIMAL_INSTALL-$(target))" - @echo "MPDECIMAL_LIB-$(target): $$(MPDECIMAL_LIB-$(target))" @echo "LIBFFI_INSTALL-$(target): $$(LIBFFI_INSTALL-$(target))" @echo "LIBFFI_LIB-$(target): $$(LIBFFI_LIB-$(target))" + @echo "MPDECIMAL_INSTALL-$(target): $$(MPDECIMAL_INSTALL-$(target))" + @echo "MPDECIMAL_LIB-$(target): $$(MPDECIMAL_LIB-$(target))" + @echo "OPENSSL_INSTALL-$(target): $$(OPENSSL_INSTALL-$(target))" + @echo "OPENSSL_SSL_LIB-$(target): $$(OPENSSL_SSL_LIB-$(target))" + @echo "XZ_INSTALL-$(target): $$(XZ_INSTALL-$(target))" + @echo "XZ_LIB-$(target): $$(XZ_LIB-$(target))" @echo "PYTHON_SRCDIR-$(target): $$(PYTHON_SRCDIR-$(target))" @echo "PYTHON_INSTALL-$(target): $$(PYTHON_INSTALL-$(target))" @echo "PYTHON_FRAMEWORK-$(target): $$(PYTHON_FRAMEWORK-$(target))" @@ -600,10 +600,10 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ echo "Build: $(BUILD_NUMBER)" >> support/$(PYTHON_VER)/$(os)/VERSIONS echo "Min $(os) version: $$(VERSION_MIN-$(os))" >> support/$(PYTHON_VER)/$(os)/VERSIONS echo "---------------------" >> support/$(PYTHON_VER)/$(os)/VERSIONS - echo "libFFI: $(LIBFFI_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS echo "BZip2: $(BZIP2_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS - echo "OpenSSL: $(OPENSSL_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS + echo "libFFI: $(LIBFFI_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS 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 dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz: \ From 5e37c102b012a0a43f73213897658647b153d00e Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 6 Aug 2024 14:12:15 +0800 Subject: [PATCH 045/113] Rework CI and release workflows to maximize reuse. --- .github/workflows/ci.yaml | 85 +++++++++++++++++++++++++++++----- .github/workflows/release.yaml | 78 ++++++++----------------------- Makefile | 5 +- 3 files changed, 96 insertions(+), 72 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 380a313a..416507af 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -5,6 +5,34 @@ on: branches: - main - 3.* + workflow_call: + inputs: + build-number: + description: "The build number to add to the built package" + default: "custom" + type: "string" + outputs: + PYTHON_VER: + description: "The Python major.minor version." + value: ${{ jobs.config.outputs.PYTHON_VER }} + PYTHON_VERSION: + description: "The full Python version." + value: ${{ jobs.config.outputs.PYTHON_VERSION }} + BZIP2_VERSION: + description: "The BZip2 version used for the build." + value: ${{ jobs.config.outputs.BZIP2_VERSION }} + LIBFFI_VERSION: + description: "The libFFI version used for the build." + value: ${{ jobs.config.outputs.LIBFFI_VERSION }} + MPDECIMAL_VERSION: + description: "The mpdecimal version used for the build." + value: ${{ jobs.config.outputs.MPDECIMAL_VERSION }} + OPENSSL_VERSION: + description: "The OpenSSL version used for the build." + value: ${{ jobs.config.outputs.OPENSSL_VERSION }} + XZ_VERSION: + description: "The XZ version used for the build." + value: ${{ jobs.config.outputs.XZ_VERSION }} env: FORCE_COLOR: "1" @@ -19,8 +47,49 @@ concurrency: cancel-in-progress: true jobs: + config: + runs-on: macOS-latest + outputs: + PYTHON_VER: ${{ steps.extract.outputs.PYTHON_VER }} + PYTHON_VERSION: ${{ steps.extract.outputs.PYTHON_VERSION }} + BUILD_NUMBER: ${{ steps.extract.outputs.BUILD_NUMBER }} + BZIP2_VERSION: ${{ steps.extract.outputs.BZIP2_VERSION }} + LIBFFI_VERSION: ${{ steps.extract.outputs.LIBFFI_VERSION }} + MPDECIMAL_VERSION: ${{ steps.extract.outputs.MPDECIMAL_VERSION }} + OPENSSL_VERSION: ${{ steps.extract.outputs.OPENSSL_VERSION }} + XZ_VERSION: ${{ steps.extract.outputs.XZ_VERSION }} + + steps: + - uses: actions/checkout@v4.1.7 + + - name: Extract config variables + id: extract + run: | + PYTHON_VER=$(make config | grep "PYTHON_VER=" | cut -d "=" -f 2) + PYTHON_VERSION=$(make config | grep "PYTHON_VERSION=" | cut -d "=" -f 2) + BZIP2_VERSION=$(make config | grep "BZIP2_VERSION=" | cut -d "=" -f 2) + LIBFFI_VERSION=$(make config | grep "LIBFFI_VERSION=" | cut -d "=" -f 2) + 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) + if [ -z "${{ inputs.build-number }}" ]; then + BUILD_NUMBER=custom + else + BUILD_NUMBER=${{ inputs.build-number }} + fi + + echo "PYTHON_VER=${PYTHON_VER}" | tee -a ${GITHUB_OUTPUT} + echo "PYTHON_VERSION=${PYTHON_VERSION}" | tee -a ${GITHUB_OUTPUT} + echo "BUILD_NUMBER=${BUILD_NUMBER}" | tee -a ${GITHUB_OUTPUT} + echo "BZIP2_VERSION=${BZIP2_VERSION}" | tee -a ${GITHUB_OUTPUT} + echo "LIBFFI_VERSION=${LIBFFI_VERSION}" | tee -a ${GITHUB_OUTPUT} + 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} + build: runs-on: macOS-latest + needs: [ config ] strategy: fail-fast: false matrix: @@ -39,29 +108,23 @@ jobs: steps: - uses: actions/checkout@v4.1.7 - - name: Extract config variables - id: config-vars - run: | - PYTHON_VER=$(make config | grep "PYTHON_VER=" | cut -d "=" -f 2) - echo "PYTHON_VER=${PYTHON_VER}" | tee -a ${GITHUB_OUTPUT} - - name: Set up Python uses: actions/setup-python@v5.1.1 with: # Appending -dev ensures that we can always build the dev release. # It's a no-op for versions that have been published. - python-version: ${{ steps.config-vars.outputs.PYTHON_VER }}-dev + python-version: ${{ needs.config.outputs.PYTHON_VER }}-dev - name: Build ${{ matrix.target }} run: | # Do the build for the requested target. - make ${{ matrix.target }} + make ${{ matrix.target }} BUILD_NUMBER=${{ needs.config.outputs.BUILD_NUMBER }} - name: Upload build artefacts uses: actions/upload-artifact@v4.3.5 with: - name: Python-${{ steps.config-vars.outputs.PYTHON_VER }}-${{ matrix.target }}-support.custom.tar.gz - path: dist/Python-${{ steps.config-vars.outputs.PYTHON_VER }}-${{ matrix.target }}-support.custom.tar.gz + name: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz + path: dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz - uses: actions/checkout@v4.1.7 if: matrix.run-tests @@ -84,4 +147,4 @@ jobs: timeout-minutes: 10 working-directory: Python-support-testbed # TODO - remove the template_branch option. - run: briefcase run ${{ matrix.target }} Xcode --test ${{ matrix.briefcase-run-args }} -C support_package=\'../dist/Python-${{ steps.config-vars.outputs.PYTHON_VER }}-${{ matrix.target }}-support.custom.tar.gz\' -C template_branch=\'framework-lib\' + run: briefcase run ${{ matrix.target }} Xcode --test ${{ matrix.briefcase-run-args }} -C support_package=\'../dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz\' -C template_branch=\'framework-lib\' diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index b49127c5..d03a0913 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -8,76 +8,36 @@ on: - '*-b*' jobs: - build: - name: Build + config: + name: Build vars runs-on: macOS-latest outputs: TAG: ${{ steps.build-vars.outputs.TAG }} - PYTHON_VER: ${{ steps.build-vars.outputs.PYTHON_VER }} BUILD_NUMBER: ${{ steps.build-vars.outputs.BUILD_NUMBER }} - PYTHON_VERSION: ${{ steps.version-details.outputs.PYTHON_VERSION }} - BZIP2_VERSION: ${{ steps.version-details.outputs.BZIP2_VERSION }} - XZ_VERSION: ${{ steps.version-details.outputs.XZ_VERSION }} - LIBFFI_VERSION: ${{ steps.version-details.outputs.LIBFFI_VERSION }} - OPENSSL_VERSION: ${{ steps.version-details.outputs.OPENSSL_VERSION }} - strategy: - matrix: - target: [ "macOS", "iOS", "tvOS", "watchOS" ] - steps: - - name: Checkout - uses: actions/checkout@v4.1.1 + steps: - name: Set Build Variables id: build-vars env: TAG_NAME: ${{ github.ref }} run: | export TAG=$(basename $TAG_NAME) - export PYTHON_VER="${TAG%-*}" export BUILD_NUMBER="${TAG#*-}" echo "TAG=${TAG}" | tee -a ${GITHUB_OUTPUT} - echo "PYTHON_VER=${PYTHON_VER}" | tee -a ${GITHUB_OUTPUT} echo "BUILD_NUMBER=${BUILD_NUMBER}" | tee -a ${GITHUB_OUTPUT} - - name: Set up Python - uses: actions/setup-python@v5.1.1 - with: - python-version: "${{ steps.build-vars.outputs.PYTHON_VER }}-dev" - - - name: Build ${{ matrix.target }} - run: | - # Do the build for the requested target. - make ${{ matrix.target }} BUILD_NUMBER=${{ steps.build-vars.outputs.BUILD_NUMBER }} - - - name: Extract Version Details - id: version-details - run: | - PYTHON_VERSION=$(grep "Python version:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 3) - BZIP2_VERSION=$(grep "BZip2:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) - LIBFFI_VERSION=$(grep "libFFI:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) - MPDECIMAL_VERSION=$(grep "mpdecimal:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) - OPENSSL_VERSION=$(grep "OpenSSL:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) - XZ_VERSION=$(grep "XZ:" support/${{ steps.build-vars.outputs.PYTHON_VER }}/${{ matrix.target }}/VERSIONS | cut -d " " -f 2) - - echo "PYTHON_VERSION=${PYTHON_VERSION}" | tee -a ${GITHUB_OUTPUT} - echo "BZIP2_VERSION=${BZIP2_VERSION}" | tee -a ${GITHUB_OUTPUT} - echo "LIBFFI_VERSION=${LIBFFI_VERSION}" | tee -a ${GITHUB_OUTPUT} - 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} - - - name: Upload Build Artifact - uses: actions/upload-artifact@v4.3.5 - with: - name: dist-${{ matrix.target }} - path: dist - if-no-files-found: error + ci: + name: CI + needs: [ config ] + uses: ./.github/workflows/ci.yaml + with: + build-number: ${{ needs.config.outputs.BUILD_NUMBER }} make-release: name: Make Release runs-on: ubuntu-latest - needs: build + needs: [ config, ci ] steps: - name: Get build artifacts uses: actions/download-artifact@v4.1.8 @@ -89,17 +49,17 @@ jobs: - name: Create Release uses: ncipollo/release-action@v1.14.0 with: - name: ${{ needs.build.outputs.PYTHON_VER }}-${{ needs.build.outputs.BUILD_NUMBER }} - tag: ${{ needs.build.outputs.PYTHON_VER }}-${{ needs.build.outputs.BUILD_NUMBER }} + name: ${{ needs.ci.outputs.PYTHON_VER }}-${{ needs.config.outputs.BUILD_NUMBER }} + tag: ${{ needs.ci.outputs.PYTHON_VER }}-${{ needs.config.outputs.BUILD_NUMBER }} draft: true body: | - Build ${{ needs.build.outputs.BUILD_NUMBER }} of the BeeWare support package for Python ${{ needs.build.outputs.PYTHON_VER }}. + Build ${{ needs.config.outputs.BUILD_NUMBER }} of the BeeWare support package for Python ${{ needs.ci.outputs.PYTHON_VER }}. Includes: - * Python ${{ needs.build.outputs.PYTHON_VERSION }} - * BZip2 ${{ needs.build.outputs.BZIP2_VERSION }} - * libFFI ${{ needs.build.outputs.LIBFFI_VERSION }} - * mpdecimal ${{ needs.build.outputs.MPDECIMAL_VERSION }} - * OpenSSL ${{ needs.build.outputs.OPENSSL_VERSION }} - * XZ ${{ needs.build.outputs.XZ_VERSION }} + * Python ${{ needs.ci.outputs.PYTHON_VERSION }} + * BZip2 ${{ needs.ci.outputs.BZIP2_VERSION }} + * libFFI ${{ needs.ci.outputs.LIBFFI_VERSION }} + * mpdecimal ${{ needs.ci.outputs.MPDECIMAL_VERSION }} + * OpenSSL ${{ needs.ci.outputs.OPENSSL_VERSION }} + * XZ ${{ needs.ci.outputs.XZ_VERSION }} artifacts: "dist/*" diff --git a/Makefile b/Makefile index a05906cb..3ed4feae 100644 --- a/Makefile +++ b/Makefile @@ -669,9 +669,10 @@ config: @echo "PYTHON_VER=$(PYTHON_VER)" @echo "BUILD_NUMBER=$(BUILD_NUMBER)" @echo "BZIP2_VERSION=$(BZIP2_VERSION)" - @echo "XZ_VERSION=$(XZ_VERSION)" - @echo "OPENSSL_VERSION=$(OPENSSL_VERSION)" @echo "LIBFFI_VERSION=$(LIBFFI_VERSION)" + @echo "MPDECIMAL_VERSION=$(MPDECIMAL_VERSION)" + @echo "OPENSSL_VERSION=$(OPENSSL_VERSION)" + @echo "XZ_VERSION=$(XZ_VERSION)" # Expand cross-platform build and clean targets for each output product clean: $(foreach os,$(OS_LIST),clean-$(os)) From 723d5160fcd65f6cb627c73e04bfb1a998ae0fc1 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 7 Aug 2024 09:35:01 +0800 Subject: [PATCH 046/113] Correct artefact pattern for releases. --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index d03a0913..5e5ef751 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -42,7 +42,7 @@ jobs: - name: Get build artifacts uses: actions/download-artifact@v4.1.8 with: - pattern: dist-* + pattern: Python-* path: dist merge-multiple: true From 8b5fb4e1c469353a1662fabdece469b8b53cfdab Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 7 Aug 2024 09:55:40 +0800 Subject: [PATCH 047/113] Correct Python version variable in publication script. --- .github/workflows/publish.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 0cde5616..e1b09da7 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -37,14 +37,14 @@ jobs: python -m pip install -U setuptools python -m pip install awscli # macOS build - curl -o macOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.outputs.TAG }}/Python-${{ steps.build-vars.outputs.PY_VERSION }}-macOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz - aws s3 cp macOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.outputs.PY_VERSION }}/macOS/Python-${{ steps.build-vars.outputs.PY_VERSION }}-macOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + curl -o macOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.outputs.TAG }}/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-macOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + aws s3 cp macOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.outputs.PYTHON_VER }}/macOS/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-macOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz # iOS build - curl -o iOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.outputs.TAG }}/Python-${{ steps.build-vars.outputs.PY_VERSION }}-iOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz - aws s3 cp iOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.outputs.PY_VERSION }}/iOS/Python-${{ steps.build-vars.outputs.PY_VERSION }}-iOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + curl -o iOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.outputs.TAG }}/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-iOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + aws s3 cp iOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.outputs.PYTHON_VER }}/iOS/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-iOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz # tvOS build - curl -o tvOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.outputs.TAG }}/Python-${{ steps.build-vars.outputs.PY_VERSION }}-tvOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz - aws s3 cp tvOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.outputs.PY_VERSION }}/tvOS/Python-${{ steps.build-vars.outputs.PY_VERSION }}-tvOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + curl -o tvOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.outputs.TAG }}/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-tvOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + aws s3 cp tvOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.outputs.PYTHON_VER }}/tvOS/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-tvOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz # watchOS build - curl -o watchOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.outputs.TAG }}/Python-${{ steps.build-vars.outputs.PY_VERSION }}-watchOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz - aws s3 cp watchOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.outputs.PY_VERSION }}/watchOS/Python-${{ steps.build-vars.outputs.PY_VERSION }}-watchOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + curl -o watchOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.outputs.TAG }}/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-watchOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + aws s3 cp watchOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.outputs.PYTHON_VER }}/watchOS/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-watchOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz From 6be4cfff14252db2aa8b3d5f8a44107293555f24 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 11 Aug 2024 20:26:56 +0000 Subject: [PATCH 048/113] Bump actions/upload-artifact from 4.3.5 to 4.3.6 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.5 to 4.3.6. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.3.5...v4.3.6) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 416507af..a60f28a3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -121,7 +121,7 @@ jobs: make ${{ matrix.target }} BUILD_NUMBER=${{ needs.config.outputs.BUILD_NUMBER }} - name: Upload build artefacts - uses: actions/upload-artifact@v4.3.5 + uses: actions/upload-artifact@v4.3.6 with: name: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz path: dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz From 070ee964cacb5818d33f5393a75f8e7d7208f30f Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 16 Aug 2024 11:46:08 +0800 Subject: [PATCH 049/113] Revert TODOs related to in-progress briefcase work. --- .github/workflows/ci.yaml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a60f28a3..b048fb28 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -131,20 +131,15 @@ jobs: with: repository: beeware/Python-support-testbed path: Python-support-testbed - # TODO - remove the py3.13 reference option. - ref: py3.13-support - name: Install dependencies if: matrix.run-tests run: | - # TODO - Revert to the development version of Briefcase # Use the development version of Briefcase - # python -m pip install git+https://github.com/beeware/briefcase.git - python -m pip install git+https://github.com/freakboy3742/briefcase.git@version-bumps + python -m pip install git+https://github.com/beeware/briefcase.git - name: Run support testbed check if: matrix.run-tests timeout-minutes: 10 working-directory: Python-support-testbed - # TODO - remove the template_branch option. - run: briefcase run ${{ matrix.target }} Xcode --test ${{ matrix.briefcase-run-args }} -C support_package=\'../dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz\' -C template_branch=\'framework-lib\' + run: briefcase run ${{ matrix.target }} Xcode --test ${{ matrix.briefcase-run-args }} -C support_package=\'../dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz\' From 132f9dc9c4c6c927e4597623c7dfdee8046311b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 20:41:03 +0000 Subject: [PATCH 050/113] Bump actions/setup-python from 5.1.1 to 5.2.0 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.1.1 to 5.2.0. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5.1.1...v5.2.0) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yaml | 2 +- .github/workflows/publish.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a60f28a3..8d19618b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -109,7 +109,7 @@ jobs: - uses: actions/checkout@v4.1.7 - name: Set up Python - uses: actions/setup-python@v5.1.1 + uses: actions/setup-python@v5.2.0 with: # Appending -dev ensures that we can always build the dev release. # It's a no-op for versions that have been published. diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index e1b09da7..e90bf318 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python environment - uses: actions/setup-python@v5.1.1 + uses: actions/setup-python@v5.2.0 with: python-version: "3.X" From 3b95db42590a7a4c367049b83cde87a8827faebd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 20:41:06 +0000 Subject: [PATCH 051/113] Bump actions/upload-artifact from 4.3.6 to 4.4.0 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.6 to 4.4.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.3.6...v4.4.0) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a60f28a3..c23c01df 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -121,7 +121,7 @@ jobs: make ${{ matrix.target }} BUILD_NUMBER=${{ needs.config.outputs.BUILD_NUMBER }} - name: Upload build artefacts - uses: actions/upload-artifact@v4.3.6 + uses: actions/upload-artifact@v4.4.0 with: name: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz path: dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz From a0f789d7e66d1d6ae0a5ba1058d8bced18dbc33d Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 9 Sep 2024 08:44:53 +0800 Subject: [PATCH 052/113] Update patch for v3.13.0rc2. Also updates: - XZ 5.6.2 - OpenSSL 3.0.15 --- Makefile | 6 +-- README.rst | 2 +- patch/Python/Python.patch | 92 ++++++++++++++++++++++++++------------- 3 files changed, 65 insertions(+), 35 deletions(-) diff --git a/Makefile b/Makefile index 3ed4feae..b98908ed 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,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.13.0rc1 +PYTHON_VERSION=3.13.0rc2 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+") @@ -29,8 +29,8 @@ PYTHON_VER=$(basename $(PYTHON_VERSION)) BZIP2_VERSION=1.0.8-1 LIBFFI_VERSION=3.4.6-1 MPDECIMAL_VERSION=4.0.0-1 -OPENSSL_VERSION=3.0.14-1 -XZ_VERSION=5.4.7-1 +OPENSSL_VERSION=3.0.15-1 +XZ_VERSION=5.6.2-1 # Supported OS OS_LIST=macOS iOS tvOS watchOS diff --git a/README.rst b/README.rst index b1b3147c..cd395fc7 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,7 @@ Python Apple Support This is a meta-package for building a version of Python that can be embedded into a macOS, iOS, tvOS or watchOS project. -**This branch builds a packaged version of Python 3.13.0**. +**This branch builds a packaged version of Python 3.13.0rc2**. Other Python versions are available by cloning other branches of the main repository: diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 3317a84a..7ec76ce7 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -163,7 +163,7 @@ index ec0857a4a99..2350e9dc821 100644 # elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX PLATFORM_TRIPLET=darwin diff --git a/configure b/configure -index beffc1fd76c..b2daf3467cd 100755 +index 7e1e5e594ca..39a050c1451 100755 --- a/configure +++ b/configure @@ -978,6 +978,8 @@ @@ -243,17 +243,17 @@ index beffc1fd76c..b2daf3467cd 100755 esac fi @@ -4147,6 +4179,14 @@ - 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-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-tvos*-simulator) CXX=arm64-apple-tvos-simulator-clang++ ;; ++ aarch64-apple-tvos*) CXX=arm64-apple-tvos-clang++ ;; ++ x86_64-apple-tvos*-simulator) CXX=x86_64-apple-tvos-simulator-clang++ ;; + -+ aarch64-apple-watchos*-simulator) CXX=arm64-apple-watchos-simulator-clang ;; -+ aarch64-apple-watchos*) CXX=arm64_32-apple-watchos-clang ;; -+ x86_64-apple-watchos*-simulator) CXX=x86_64-apple-watchos-simulator-clang ;; ++ aarch64-apple-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 @@ -709,8 +709,8 @@ index beffc1fd76c..b2daf3467cd 100755 then for ac_func in clock_settime -@@ -24590,8 +24746,8 @@ - MODULE_LDFLAGS="\$(BLDLIBRARY)" +@@ -24593,8 +24749,8 @@ + LIBPYTHON="\$(BLDLIBRARY)" fi -# On iOS the shared libraries must be linked with the Python framework @@ -720,7 +720,7 @@ index beffc1fd76c..b2daf3467cd 100755 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -27239,7 +27395,7 @@ +@@ -27242,7 +27398,7 @@ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for device files" >&5 printf "%s\n" "$as_me: checking for device files" >&6;} @@ -729,7 +729,7 @@ index beffc1fd76c..b2daf3467cd 100755 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -27672,7 +27828,7 @@ +@@ -27675,7 +27831,7 @@ with_ensurepip=no ;; #( WASI) : with_ensurepip=no ;; #( @@ -738,7 +738,7 @@ index beffc1fd76c..b2daf3467cd 100755 with_ensurepip=no ;; #( *) : with_ensurepip=upgrade -@@ -28699,7 +28855,7 @@ +@@ -28700,7 +28856,7 @@ ;; #( Darwin) : ;; #( @@ -747,7 +747,7 @@ index beffc1fd76c..b2daf3467cd 100755 -@@ -32464,6 +32620,8 @@ +@@ -32465,6 +32621,8 @@ "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" ;; @@ -757,7 +757,7 @@ index beffc1fd76c..b2daf3467cd 100755 "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 5842bd24c45..f97279331f6 100644 +index 58f54076ff2..4825d131142 100644 --- a/configure.ac +++ b/configure.ac @@ -330,6 +330,12 @@ @@ -828,17 +828,17 @@ index 5842bd24c45..f97279331f6 100644 esac fi @@ -421,6 +451,14 @@ - 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-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-tvos*-simulator) CXX=arm64-apple-tvos-simulator-clang++ ;; ++ aarch64-apple-tvos*) CXX=arm64-apple-tvos-clang++ ;; ++ x86_64-apple-tvos*-simulator) CXX=x86_64-apple-tvos-simulator-clang++ ;; + -+ aarch64-apple-watchos*-simulator) CXX=arm64-apple-watchos-simulator-clang ;; -+ aarch64-apple-watchos*) CXX=arm64_32-apple-watchos-clang ;; -+ x86_64-apple-watchos*-simulator) CXX=x86_64-apple-watchos-simulator-clang ;; ++ aarch64-apple-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 @@ -1211,8 +1211,8 @@ index 5842bd24c45..f97279331f6 100644 then AC_CHECK_FUNCS([clock_settime], [], [ AC_CHECK_LIB([rt], [clock_settime], [ -@@ -6176,8 +6316,8 @@ - MODULE_LDFLAGS="\$(BLDLIBRARY)" +@@ -6179,8 +6319,8 @@ + LIBPYTHON="\$(BLDLIBRARY)" fi -# On iOS the shared libraries must be linked with the Python framework @@ -1222,7 +1222,7 @@ index 5842bd24c45..f97279331f6 100644 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -6785,7 +6925,7 @@ +@@ -6788,7 +6928,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. @@ -1231,7 +1231,7 @@ index 5842bd24c45..f97279331f6 100644 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -7042,7 +7182,7 @@ +@@ -7045,7 +7185,7 @@ AS_CASE([$ac_sys_system], [Emscripten], [with_ensurepip=no], [WASI], [with_ensurepip=no], @@ -1240,7 +1240,7 @@ index 5842bd24c45..f97279331f6 100644 [with_ensurepip=upgrade] ) ]) -@@ -7452,7 +7592,7 @@ +@@ -7454,7 +7594,7 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], @@ -1428,6 +1428,11 @@ index c3e261ecd9e..26ef7a95de4 100644 +#!/bin/bash +xcrun --sdk appletvos${TVOS_SDK_VERSION} clang -target arm64-apple-tvos $@ --- /dev/null ++++ b/tvOS/Resources/bin/arm64-apple-tvos-clang++ +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk appletvos${TVOS_SDK_VERSION} clang++ -target arm64-apple-tvos $@ +--- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-cpp @@ -0,0 +1,2 @@ +#!/bin/bash @@ -1443,6 +1448,11 @@ index c3e261ecd9e..26ef7a95de4 100644 +#!/bin/bash +xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target arm64-apple-tvos-simulator $@ --- /dev/null ++++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-clang++ +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang++ -target arm64-apple-tvos-simulator $@ +--- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-cpp @@ -0,0 +1,2 @@ +#!/bin/bash @@ -1458,6 +1468,11 @@ index c3e261ecd9e..26ef7a95de4 100644 +#!/bin/bash +xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target x86_64-apple-tvos-simulator $@ --- /dev/null ++++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-clang++ +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang++ -target x86_64-apple-tvos-simulator $@ +--- /dev/null +++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-cpp @@ -0,0 +1,2 @@ +#!/bin/bash @@ -1660,6 +1675,11 @@ index c3e261ecd9e..26ef7a95de4 100644 +#!/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 @@ -1675,6 +1695,11 @@ index c3e261ecd9e..26ef7a95de4 100644 +#!/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 @@ -1690,6 +1715,11 @@ index c3e261ecd9e..26ef7a95de4 100644 +#!/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 From e742508fc6d1aed96c692e15b881bdc794ffc786 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 9 Sep 2024 10:49:21 +0800 Subject: [PATCH 053/113] Ensure dylibs and macOS packaging confirmation files aren't included in builds. --- Makefile | 10 +++++----- patch/Python/release.macOS.exclude | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index b98908ed..f7f1bc29 100644 --- a/Makefile +++ b/Makefile @@ -152,7 +152,7 @@ downloads/bzip2-$(BZIP2_VERSION)-$(target).tar.gz: $$(BZIP2_LIB-$(target)): downloads/bzip2-$(BZIP2_VERSION)-$(target).tar.gz @echo ">>> Install BZip2 for $(target)" mkdir -p $$(BZIP2_INSTALL-$(target)) - cd $$(BZIP2_INSTALL-$(target)) && tar zxvf $(PROJECT_DIR)/downloads/bzip2-$(BZIP2_VERSION)-$(target).tar.gz + cd $$(BZIP2_INSTALL-$(target)) && tar zxvf $(PROJECT_DIR)/downloads/bzip2-$(BZIP2_VERSION)-$(target).tar.gz --exclude="*.dylib" # Ensure the target is marked as clean. touch $$(BZIP2_LIB-$(target)) @@ -172,7 +172,7 @@ downloads/xz-$(XZ_VERSION)-$(target).tar.gz: $$(XZ_LIB-$(target)): downloads/xz-$(XZ_VERSION)-$(target).tar.gz @echo ">>> Install XZ for $(target)" mkdir -p $$(XZ_INSTALL-$(target)) - cd $$(XZ_INSTALL-$(target)) && tar zxvf $(PROJECT_DIR)/downloads/xz-$(XZ_VERSION)-$(target).tar.gz + cd $$(XZ_INSTALL-$(target)) && tar zxvf $(PROJECT_DIR)/downloads/xz-$(XZ_VERSION)-$(target).tar.gz --exclude="*.dylib" # Ensure the target is marked as clean. touch $$(XZ_LIB-$(target)) @@ -192,7 +192,7 @@ downloads/mpdecimal-$(MPDECIMAL_VERSION)-$(target).tar.gz: $$(MPDECIMAL_LIB-$(target)): downloads/mpdecimal-$(MPDECIMAL_VERSION)-$(target).tar.gz @echo ">>> Install mpdecimal for $(target)" mkdir -p $$(MPDECIMAL_INSTALL-$(target)) - cd $$(MPDECIMAL_INSTALL-$(target)) && tar zxvf $(PROJECT_DIR)/downloads/mpdecimal-$(MPDECIMAL_VERSION)-$(target).tar.gz + cd $$(MPDECIMAL_INSTALL-$(target)) && tar zxvf $(PROJECT_DIR)/downloads/mpdecimal-$(MPDECIMAL_VERSION)-$(target).tar.gz --exclude="*.dylib" # Ensure the target is marked as clean. touch $$(MPDECIMAL_LIB-$(target)) @@ -212,7 +212,7 @@ downloads/openssl-$(OPENSSL_VERSION)-$(target).tar.gz: $$(OPENSSL_SSL_LIB-$(target)): downloads/openssl-$(OPENSSL_VERSION)-$(target).tar.gz @echo ">>> Install OpenSSL for $(target)" mkdir -p $$(OPENSSL_INSTALL-$(target)) - cd $$(OPENSSL_INSTALL-$(target)) && tar zxvf $(PROJECT_DIR)/downloads/openssl-$(OPENSSL_VERSION)-$(target).tar.gz + cd $$(OPENSSL_INSTALL-$(target)) && tar zxvf $(PROJECT_DIR)/downloads/openssl-$(OPENSSL_VERSION)-$(target).tar.gz --exclude="*.dylib" # Ensure the target is marked as clean. touch $$(OPENSSL_SSL_LIB-$(target)) @@ -237,7 +237,7 @@ downloads/libffi-$(LIBFFI_VERSION)-$(target).tar.gz: $$(LIBFFI_LIB-$(target)): downloads/libffi-$(LIBFFI_VERSION)-$(target).tar.gz @echo ">>> Install libFFI for $(target)" mkdir -p $$(LIBFFI_INSTALL-$(target)) - cd $$(LIBFFI_INSTALL-$(target)) && tar zxvf $(PROJECT_DIR)/downloads/libffi-$(LIBFFI_VERSION)-$(target).tar.gz + cd $$(LIBFFI_INSTALL-$(target)) && tar zxvf $(PROJECT_DIR)/downloads/libffi-$(LIBFFI_VERSION)-$(target).tar.gz --exclude="*.dylib" # Ensure the target is marked as clean. touch $$(LIBFFI_LIB-$(target)) diff --git a/patch/Python/release.macOS.exclude b/patch/Python/release.macOS.exclude index f45bf7f8..3bc247c1 100644 --- a/patch/Python/release.macOS.exclude +++ b/patch/Python/release.macOS.exclude @@ -2,7 +2,21 @@ # when building macOS Python-Apple-support tarballs from the official Framework # It is used by `tar -X` during the Makefile build. # +._Headers +._Python +._Resources +Resources/._Python.app Resources/Python.app +Versions/._Current +Versions/*/.__CodeSignature +Versions/*/._bin +Versions/*/._etc +Versions/*/._Frameworks +Versions/*/._Headers +Versions/*/._include +Versions/*/._lib +Versions/*/._Resources +Versions/*/._share Versions/*/bin Versions/*/etc Versions/*/Frameworks From dd2c44bd73b239ac0a929902608ec9b526d4bb4e Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 9 Sep 2024 11:30:26 +0800 Subject: [PATCH 054/113] Remove micro version from README note. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index cd395fc7..d388d0b5 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,7 @@ Python Apple Support This is a meta-package for building a version of Python that can be embedded into a macOS, iOS, tvOS or watchOS project. -**This branch builds a packaged version of Python 3.13.0rc2**. +**This branch builds a packaged version of Python 3.13**. Other Python versions are available by cloning other branches of the main repository: From d8fdaaec55b845e6e3bfcee9439d4a462c0ab9a6 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 10 Sep 2024 08:04:32 +0800 Subject: [PATCH 055/113] Strip xattrs from macOS bundle before archiving. --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index f7f1bc29..9aa0e7ba 100644 --- a/Makefile +++ b/Makefile @@ -574,6 +574,8 @@ dist/Python-$(PYTHON_VER)-macOS-support.$(BUILD_NUMBER).tar.gz: \ @echo ">>> Create final distribution artefact for macOS" mkdir -p dist + # Strip xattrs from the support files + xattr -cr support/$(PYTHON_VER)/macOS # Build a distributable tarball tar zcvf $$@ -C support/$(PYTHON_VER)/macOS `ls -A support/$(PYTHON_VER)/macOS/` From eae365ed3059daf80bc9059ada0fef29f8112e19 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 9 Oct 2024 10:10:11 +0800 Subject: [PATCH 056/113] Bump to 3.13.0. --- Makefile | 2 +- patch/Python/Python.patch | 68 +++++++++++++++++++-------------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/Makefile b/Makefile index 9aa0e7ba..538bb61c 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,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.13.0rc2 +PYTHON_VERSION=3.13.0 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+") diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 7ec76ce7..02dbce85 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -163,7 +163,7 @@ index ec0857a4a99..2350e9dc821 100644 # elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX PLATFORM_TRIPLET=darwin diff --git a/configure b/configure -index 7e1e5e594ca..39a050c1451 100755 +index 7cdd386c387..5e968a26b17 100755 --- a/configure +++ b/configure @@ -978,6 +978,8 @@ @@ -495,7 +495,7 @@ index 7e1e5e594ca..39a050c1451 100755 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -12948,7 +13097,7 @@ +@@ -12952,7 +13101,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -504,7 +504,7 @@ index 7e1e5e594ca..39a050c1451 100755 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -13081,7 +13230,7 @@ +@@ -13085,7 +13234,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -513,7 +513,7 @@ index 7e1e5e594ca..39a050c1451 100755 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -13105,7 +13254,7 @@ +@@ -13109,7 +13258,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -522,7 +522,7 @@ index 7e1e5e594ca..39a050c1451 100755 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -14504,7 +14653,7 @@ +@@ -14508,7 +14657,7 @@ ctypes_malloc_closure=yes ;; #( @@ -531,7 +531,7 @@ index 7e1e5e594ca..39a050c1451 100755 ctypes_malloc_closure=yes ;; #( -@@ -17955,12 +18104,6 @@ +@@ -17959,12 +18108,6 @@ then : printf "%s\n" "#define HAVE_DUP3 1" >>confdefs.h @@ -544,7 +544,7 @@ index 7e1e5e594ca..39a050c1451 100755 fi ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes -@@ -18021,18 +18164,6 @@ +@@ -18025,18 +18168,6 @@ then : printf "%s\n" "#define HAVE_FEXECVE 1" >>confdefs.h @@ -563,7 +563,7 @@ index 7e1e5e594ca..39a050c1451 100755 fi ac_fn_c_check_func "$LINENO" "fpathconf" "ac_cv_func_fpathconf" if test "x$ac_cv_func_fpathconf" = xyes -@@ -18459,24 +18590,6 @@ +@@ -18463,24 +18594,6 @@ then : printf "%s\n" "#define HAVE_POSIX_OPENPT 1" >>confdefs.h @@ -588,7 +588,7 @@ index 7e1e5e594ca..39a050c1451 100755 fi ac_fn_c_check_func "$LINENO" "pread" "ac_cv_func_pread" if test "x$ac_cv_func_pread" = xyes -@@ -18765,12 +18878,6 @@ +@@ -18769,12 +18882,6 @@ then : printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h @@ -601,7 +601,7 @@ index 7e1e5e594ca..39a050c1451 100755 fi ac_fn_c_check_func "$LINENO" "sigfillset" "ac_cv_func_sigfillset" if test "x$ac_cv_func_sigfillset" = xyes -@@ -19039,11 +19146,11 @@ +@@ -19043,11 +19150,11 @@ fi @@ -615,7 +615,7 @@ index 7e1e5e594ca..39a050c1451 100755 ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" if test "x$ac_cv_func_getentropy" = xyes then : -@@ -19065,6 +19172,53 @@ +@@ -19069,6 +19176,53 @@ fi @@ -669,7 +669,7 @@ index 7e1e5e594ca..39a050c1451 100755 { 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} -@@ -21861,7 +22015,8 @@ +@@ -21865,7 +22019,8 @@ # check for openpty, login_tty, and forkpty @@ -679,7 +679,7 @@ index 7e1e5e594ca..39a050c1451 100755 for ac_func in openpty do : -@@ -21957,7 +22112,7 @@ +@@ -21961,7 +22116,7 @@ fi done @@ -688,7 +688,7 @@ index 7e1e5e594ca..39a050c1451 100755 printf %s "checking for library containing login_tty... " >&6; } if test ${ac_cv_search_login_tty+y} then : -@@ -22114,6 +22269,7 @@ +@@ -22118,6 +22273,7 @@ fi done @@ -696,7 +696,7 @@ index 7e1e5e594ca..39a050c1451 100755 # check for long file support functions ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" -@@ -22360,10 +22516,10 @@ +@@ -22364,10 +22520,10 @@ done @@ -709,7 +709,7 @@ index 7e1e5e594ca..39a050c1451 100755 then for ac_func in clock_settime -@@ -24593,8 +24749,8 @@ +@@ -24597,8 +24753,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -720,7 +720,7 @@ index 7e1e5e594ca..39a050c1451 100755 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -27242,7 +27398,7 @@ +@@ -27246,7 +27402,7 @@ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for device files" >&5 printf "%s\n" "$as_me: checking for device files" >&6;} @@ -729,7 +729,7 @@ index 7e1e5e594ca..39a050c1451 100755 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -27675,7 +27831,7 @@ +@@ -27679,7 +27835,7 @@ with_ensurepip=no ;; #( WASI) : with_ensurepip=no ;; #( @@ -738,7 +738,7 @@ index 7e1e5e594ca..39a050c1451 100755 with_ensurepip=no ;; #( *) : with_ensurepip=upgrade -@@ -28700,7 +28856,7 @@ +@@ -28704,7 +28860,7 @@ ;; #( Darwin) : ;; #( @@ -747,7 +747,7 @@ index 7e1e5e594ca..39a050c1451 100755 -@@ -32465,6 +32621,8 @@ +@@ -32469,6 +32625,8 @@ "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" ;; @@ -757,7 +757,7 @@ index 7e1e5e594ca..39a050c1451 100755 "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 58f54076ff2..4825d131142 100644 +index 24e28a1e2de..3e008102154 100644 --- a/configure.ac +++ b/configure.ac @@ -330,6 +330,12 @@ @@ -1071,7 +1071,7 @@ index 58f54076ff2..4825d131142 100644 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -3458,7 +3589,7 @@ +@@ -3462,7 +3593,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -1080,7 +1080,7 @@ index 58f54076ff2..4825d131142 100644 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -3582,7 +3713,7 @@ +@@ -3586,7 +3717,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -1089,7 +1089,7 @@ index 58f54076ff2..4825d131142 100644 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -3606,7 +3737,7 @@ +@@ -3610,7 +3741,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -1098,7 +1098,7 @@ index 58f54076ff2..4825d131142 100644 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -3990,7 +4121,7 @@ +@@ -3994,7 +4125,7 @@ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], @@ -1107,7 +1107,7 @@ index 58f54076ff2..4825d131142 100644 ctypes_malloc_closure=yes ], [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] -@@ -5084,9 +5215,9 @@ +@@ -5088,9 +5219,9 @@ # checks for library functions AC_CHECK_FUNCS([ \ accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ @@ -1119,7 +1119,7 @@ index 58f54076ff2..4825d131142 100644 gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \ getgrnam_r getgrouplist gethostname getitimer getloadavg getlogin \ getpeername getpgid getpid getppid getpriority _getpty \ -@@ -5094,15 +5225,14 @@ +@@ -5098,15 +5229,14 @@ 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 \ @@ -1137,7 +1137,7 @@ index 58f54076ff2..4825d131142 100644 sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ -@@ -5117,12 +5247,20 @@ +@@ -5121,12 +5251,20 @@ AC_CHECK_FUNCS([lchmod]) fi @@ -1161,7 +1161,7 @@ index 58f54076ff2..4825d131142 100644 fi AC_CHECK_DECL([dirfd], -@@ -5373,20 +5511,22 @@ +@@ -5377,20 +5515,22 @@ ]) # check for openpty, login_tty, and forkpty @@ -1198,7 +1198,7 @@ index 58f54076ff2..4825d131142 100644 # check for long file support functions AC_CHECK_FUNCS([fseek64 fseeko fstatvfs ftell64 ftello statvfs]) -@@ -5425,10 +5565,10 @@ +@@ -5429,10 +5569,10 @@ ]) ]) @@ -1211,7 +1211,7 @@ index 58f54076ff2..4825d131142 100644 then AC_CHECK_FUNCS([clock_settime], [], [ AC_CHECK_LIB([rt], [clock_settime], [ -@@ -6179,8 +6319,8 @@ +@@ -6183,8 +6323,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -1222,7 +1222,7 @@ index 58f54076ff2..4825d131142 100644 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -6788,7 +6928,7 @@ +@@ -6792,7 +6932,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. @@ -1231,7 +1231,7 @@ index 58f54076ff2..4825d131142 100644 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -7045,7 +7185,7 @@ +@@ -7049,7 +7189,7 @@ AS_CASE([$ac_sys_system], [Emscripten], [with_ensurepip=no], [WASI], [with_ensurepip=no], @@ -1240,7 +1240,7 @@ index 58f54076ff2..4825d131142 100644 [with_ensurepip=upgrade] ) ]) -@@ -7454,7 +7594,7 @@ +@@ -7458,7 +7598,7 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], From e3f0bc0c2e7d24b9c501c09d3d0a6c7cb60d8596 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 13 Oct 2024 20:08:05 +0000 Subject: [PATCH 057/113] Bump actions/upload-artifact from 4.4.0 to 4.4.3 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.4.0 to 4.4.3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.4.0...v4.4.3) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8fd9eb96..fc4a013b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -121,7 +121,7 @@ jobs: make ${{ matrix.target }} BUILD_NUMBER=${{ needs.config.outputs.BUILD_NUMBER }} - name: Upload build artefacts - uses: actions/upload-artifact@v4.4.0 + uses: actions/upload-artifact@v4.4.3 with: name: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz path: dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz From 75dd5e3919657725cb172bb2d792b56c858eac92 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 28 Oct 2024 08:31:43 +0800 Subject: [PATCH 058/113] Add ios_ver and _multiarch shims for pip 24.3 support. --- Makefile | 5 +++++ patch/Python/sitecustomize.iOS.py | 17 ++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 538bb61c..28d053cf 100644 --- a/Makefile +++ b/Makefile @@ -129,8 +129,10 @@ ARCH-$(target)=$$(subst .,,$$(suffix $(target))) ifneq ($(os),macOS) ifeq ($$(findstring simulator,$$(SDK-$(target))),) TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(OS_LOWER-$(target))$$(VERSION_MIN-$(os)) +IS_SIMULATOR-$(target)="False" else TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(OS_LOWER-$(target))$$(VERSION_MIN-$(os))-simulator +IS_SIMULATOR-$(target)="True" endif endif @@ -324,6 +326,9 @@ $$(PYTHON_SITECUSTOMIZE-$(target)): cat $(PROJECT_DIR)/patch/Python/sitecustomize.$(os).py \ | sed -e "s/{{os}}/$(os)/g" \ | sed -e "s/{{arch}}/$$(ARCH-$(target))/g" \ + | sed -e "s/{{version_min}}/$$(VERSION_MIN-$(os))/g" \ + | sed -e "s/{{is_simulator}}/$$(IS_SIMULATOR-$(target))/g" \ + | sed -e "s/{{multiarch}}/$$(ARCH-$(target))-$$(SDK-$(target))/g" \ | sed -e "s/{{tag}}/$$(OS_LOWER-$(target))-$$(VERSION_MIN-$(os))-$$(ARCH-$(target))-$$(SDK-$(target))/g" \ > $$(PYTHON_SITECUSTOMIZE-$(target)) diff --git a/patch/Python/sitecustomize.iOS.py b/patch/Python/sitecustomize.iOS.py index d7d86e36..ccc291f3 100644 --- a/patch/Python/sitecustomize.iOS.py +++ b/patch/Python/sitecustomize.iOS.py @@ -2,6 +2,7 @@ # packages cross-platform. If the folder containing this file is on # your PYTHONPATH when you invoke pip, pip will behave as if it were # running on {{os}}. +import collections import distutils.ccompiler import distutils.unixccompiler import os @@ -16,7 +17,21 @@ def custom_system(): platform.system = custom_system -# Make sysconfig.get_platform() return "{{tag}}" +# Make platform.ios_ver() return an appropriate namedtuple +IOSVersionInfo = collections.namedtuple( + "IOSVersionInfo", + ["system", "release", "model", "is_simulator"] +) + +def custom_ios_ver(system="", release="", model="", is_simulator=False): + return IOSVersionInfo("{{os}}", "{{version_min}}", "iPhone", {{is_simulator}}) + +platform.ios_ver = custom_ios_ver + +# Make sys.implementation._multiarch return the multiarch description +sys.implementation._multiarch = "{{multiarch}}" + +# Make sysconfig.get_platform() return the platform tag def custom_get_platform(): return "{{tag}}" From 187c64abd08b455cbc06a33b0acc17ed7e77e7ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Oct 2024 01:14:06 +0000 Subject: [PATCH 059/113] Bump actions/setup-python from 5.2.0 to 5.3.0 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.2.0 to 5.3.0. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5.2.0...v5.3.0) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yaml | 2 +- .github/workflows/publish.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index fc4a013b..2a78d775 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -109,7 +109,7 @@ jobs: - uses: actions/checkout@v4.1.7 - name: Set up Python - uses: actions/setup-python@v5.2.0 + uses: actions/setup-python@v5.3.0 with: # Appending -dev ensures that we can always build the dev release. # It's a no-op for versions that have been published. diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index e90bf318..a9f69ed4 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python environment - uses: actions/setup-python@v5.2.0 + uses: actions/setup-python@v5.3.0 with: python-version: "3.X" From 6f5fa58885abbfebbdd1d2c0af4b3379ca15e205 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 13 Dec 2024 12:19:08 +0800 Subject: [PATCH 060/113] Switch main branch to Python 3.14. --- Makefile | 2 +- README.rst | 2 +- patch/Python/Python.patch | 1440 ++++++++++++++++++++++++++++++++++--- 3 files changed, 1335 insertions(+), 109 deletions(-) diff --git a/Makefile b/Makefile index 28d053cf..d77f285e 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,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.13.0 +PYTHON_VERSION=3.14.0a2 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+") diff --git a/README.rst b/README.rst index d388d0b5..94c0ae94 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,7 @@ Python Apple Support This is a meta-package for building a version of Python that can be embedded into a macOS, iOS, tvOS or watchOS project. -**This branch builds a packaged version of Python 3.13**. +**This branch builds a packaged version of Python 3.14**. Other Python versions are available by cloning other branches of the main repository: diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 02dbce85..36d93a03 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1,8 +1,202 @@ +diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst +index 6194d7446c7..55a9dd1f25f 100644 +--- a/Doc/c-api/init_config.rst ++++ b/Doc/c-api/init_config.rst +@@ -1279,6 +1279,17 @@ + + Default: ``1`` in Python config and ``0`` in isolated config. + ++ .. c:member:: int use_system_logger ++ ++ If non-zero, ``stdout`` and ``stderr`` will be redirected to the system ++ log. ++ ++ Only available on macOS 10.12 and later, and on iOS. ++ ++ Default: ``0`` (don't use system log). ++ ++ .. versionadded:: 3.13.2 ++ + .. c:member:: int user_site_directory + + If non-zero, add the user site directory to :data:`sys.path`. +diff --git a/Doc/using/ios.rst b/Doc/using/ios.rst +index 4d4eb2031ee..aa43f75ec35 100644 +--- a/Doc/using/ios.rst ++++ b/Doc/using/ios.rst +@@ -292,10 +292,12 @@ + 10. Add Objective C code to initialize and use a Python interpreter in embedded + mode. You should ensure that: + +- * :c:member:`UTF-8 mode ` is *enabled*; +- * :c:member:`Buffered stdio ` is *disabled*; +- * :c:member:`Writing bytecode ` is *disabled*; +- * :c:member:`Signal handlers ` are *enabled*; ++ * UTF-8 mode (:c:member:`PyPreConfig.utf8_mode`) is *enabled*; ++ * Buffered stdio (:c:member:`PyConfig.buffered_stdio`) is *disabled*; ++ * Writing bytecode (:c:member:`PyConfig.write_bytecode`) is *disabled*; ++ * Signal handlers (:c:member:`PyConfig.install_signal_handlers`) are *enabled*; ++ * System logging (:c:member:`PyConfig.use_system_logger`) is *enabled* ++ (optional, but strongly recommended); + * ``PYTHONHOME`` for the interpreter is configured to point at the + ``python`` subfolder of your app's bundle; and + * The ``PYTHONPATH`` for the interpreter includes: +@@ -324,6 +326,49 @@ + * If you're using a separate folder for third-party packages, ensure that folder + is included as part of the ``PYTHONPATH`` configuration in step 10. + ++Testing a Python package ++------------------------ ++ ++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: ++ ++.. code-block:: bash ++ ++ $ python iOS/testbed clone --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). ++ ++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 ++could run: ++ ++.. code-block:: bash ++ ++ $ python app-testbed run -- module1.tests ++ ++This is the equivalent of running ``python -m module1.tests`` on a desktop ++Python build. Any arguments after the ``--`` will be passed to the testbed as ++if they were arguments to ``python -m`` on a desktop machine. ++ ++You can also open the testbed project in Xcode by running: ++ ++.. code-block:: bash ++ ++ $ open app-testbed/iOSTestbed.xcodeproj ++ ++This will allow you to use the full Xcode suite of tools for debugging. ++ + App Store Compliance + ==================== + +diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst +index 3eabf22e499..b509f2a0607 100644 +--- a/Doc/whatsnew/3.14.rst ++++ b/Doc/whatsnew/3.14.rst +@@ -205,6 +205,13 @@ + making it a :term:`generic type`. + (Contributed by Brian Schubert in :gh:`126012`.) + ++* iOS and macOS apps can now be configured to redirect ``stdout`` and ++ ``stderr`` content to the system log. (Contributed by Russell Keith-Magee in ++ :gh:`127592`.) ++ ++* The iOS testbed is now able to stream test output while the test is running. ++ The testbed can also be used to run the test suite of projects other than ++ CPython itself. (Contributed by Russell Keith-Magee in :gh:`127592`.) + + New modules + =========== +diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h +index f69c586a4f9..8ef19f67706 100644 +--- a/Include/cpython/initconfig.h ++++ b/Include/cpython/initconfig.h +@@ -179,6 +179,9 @@ + int use_frozen_modules; + int safe_path; + int int_max_str_digits; ++#ifdef __APPLE__ ++ int use_system_logger; ++#endif + + int cpu_count; + #ifdef Py_GIL_DISABLED +--- /dev/null ++++ b/Lib/_apple_support.py +@@ -0,0 +1,66 @@ ++import io ++import sys ++ ++ ++def init_streams(log_write, stdout_level, stderr_level): ++ # Redirect stdout and stderr to the Apple system log. This method is ++ # invoked by init_apple_streams() (initconfig.c) if config->use_system_logger ++ # is enabled. ++ sys.stdout = SystemLog(log_write, stdout_level, errors=sys.stderr.errors) ++ sys.stderr = SystemLog(log_write, stderr_level, errors=sys.stderr.errors) ++ ++ ++class SystemLog(io.TextIOWrapper): ++ def __init__(self, log_write, level, **kwargs): ++ kwargs.setdefault("encoding", "UTF-8") ++ kwargs.setdefault("line_buffering", True) ++ super().__init__(LogStream(log_write, level), **kwargs) ++ ++ def __repr__(self): ++ return f"" ++ ++ def write(self, s): ++ if not isinstance(s, str): ++ raise TypeError( ++ f"write() argument must be str, not {type(s).__name__}") ++ ++ # In case `s` is a str subclass that writes itself to stdout or stderr ++ # when we call its methods, convert it to an actual str. ++ s = str.__str__(s) ++ ++ # We want to emit one log message per line, so split ++ # the string before sending it to the superclass. ++ for line in s.splitlines(keepends=True): ++ super().write(line) ++ ++ return len(s) ++ ++ ++class LogStream(io.RawIOBase): ++ def __init__(self, log_write, level): ++ self.log_write = log_write ++ self.level = level ++ ++ def __repr__(self): ++ return f"" ++ ++ def writable(self): ++ return True ++ ++ def write(self, b): ++ if type(b) is not bytes: ++ try: ++ b = bytes(memoryview(b)) ++ except TypeError: ++ raise TypeError( ++ f"write() argument must be bytes-like, not {type(b).__name__}" ++ ) from None ++ ++ # Writing an empty string to the stream should have no effect. ++ if b: ++ # Encode null bytes using "modified UTF-8" to avoid truncating the ++ # message. This should not affect the return value, as the caller ++ # may be expecting it to match the length of the input. ++ self.log_write(self.level, b.replace(b"\x00", b"\xc0\x80")) ++ ++ return len(b) diff --git a/Lib/platform.py b/Lib/platform.py -index 5958382276e..5db5eb276a2 100755 +index 239e660cd16..8e007c3c3b5 100644 --- a/Lib/platform.py +++ b/Lib/platform.py -@@ -521,6 +521,54 @@ +@@ -520,6 +520,54 @@ return IOSVersionInfo(system, release, model, is_simulator) @@ -57,7 +251,7 @@ index 5958382276e..5db5eb276a2 100755 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 -@@ -884,14 +932,25 @@ +@@ -883,14 +931,25 @@ csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0) return 'Alpha' if cpu_number >= 128 else 'VAX' @@ -86,7 +280,7 @@ index 5958382276e..5db5eb276a2 100755 def from_subprocess(): """ Fall back to `uname -p` -@@ -1051,9 +1110,13 @@ +@@ -1050,9 +1109,13 @@ system = 'Android' release = android_ver().release @@ -101,7 +295,7 @@ index 5958382276e..5db5eb276a2 100755 vals = system, node, release, version, machine # Replace 'unknown' values with the more portable '' -@@ -1343,6 +1406,10 @@ +@@ -1342,6 +1405,10 @@ # macOS and iOS both report as a "Darwin" kernel if sys.platform == "ios": system, release, _, _ = ios_ver() @@ -113,10 +307,10 @@ index 5958382276e..5db5eb276a2 100755 macos_release = mac_ver()[0] if macos_release: diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py -index 80aef344711..d18a7564866 100644 +index 67a071963d8..eefcac66cb5 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py -@@ -645,6 +645,14 @@ +@@ -669,6 +669,14 @@ release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0") osname = sys.platform machine = sys.implementation._multiarch @@ -131,6 +325,238 @@ index 80aef344711..d18a7564866 100644 else: import _osx_support osname, release, machine = _osx_support.get_platform_osx( +--- /dev/null ++++ b/Lib/test/test_apple.py +@@ -0,0 +1,155 @@ ++import unittest ++from _apple_support import SystemLog ++from test.support import is_apple ++from unittest.mock import Mock, call ++ ++if not is_apple: ++ raise unittest.SkipTest("Apple-specific") ++ ++ ++# Test redirection of stdout and stderr to the Apple system log. ++class TestAppleSystemLogOutput(unittest.TestCase): ++ maxDiff = None ++ ++ def assert_writes(self, output): ++ self.assertEqual( ++ self.log_write.mock_calls, ++ [ ++ call(self.log_level, line) ++ for line in output ++ ] ++ ) ++ ++ self.log_write.reset_mock() ++ ++ def setUp(self): ++ self.log_write = Mock() ++ self.log_level = 42 ++ self.log = SystemLog(self.log_write, self.log_level, errors="replace") ++ ++ def test_repr(self): ++ self.assertEqual(repr(self.log), "") ++ self.assertEqual(repr(self.log.buffer), "") ++ ++ def test_log_config(self): ++ self.assertIs(self.log.writable(), True) ++ self.assertIs(self.log.readable(), False) ++ ++ self.assertEqual("UTF-8", self.log.encoding) ++ self.assertEqual("replace", self.log.errors) ++ ++ self.assertIs(self.log.line_buffering, True) ++ self.assertIs(self.log.write_through, False) ++ ++ def test_empty_str(self): ++ self.log.write("") ++ self.log.flush() ++ ++ self.assert_writes([]) ++ ++ def test_simple_str(self): ++ self.log.write("hello world\n") ++ ++ self.assert_writes([b"hello world\n"]) ++ ++ def test_buffered_str(self): ++ self.log.write("h") ++ self.log.write("ello") ++ self.log.write(" ") ++ self.log.write("world\n") ++ self.log.write("goodbye.") ++ self.log.flush() ++ ++ self.assert_writes([b"hello world\n", b"goodbye."]) ++ ++ def test_manual_flush(self): ++ self.log.write("Hello") ++ ++ self.assert_writes([]) ++ ++ self.log.write(" world\nHere for a while...\nGoodbye") ++ self.assert_writes([b"Hello world\n", b"Here for a while...\n"]) ++ ++ self.log.write(" world\nHello again") ++ self.assert_writes([b"Goodbye world\n"]) ++ ++ self.log.flush() ++ self.assert_writes([b"Hello again"]) ++ ++ def test_non_ascii(self): ++ # Spanish ++ self.log.write("ol\u00e9\n") ++ self.assert_writes([b"ol\xc3\xa9\n"]) ++ ++ # Chinese ++ self.log.write("\u4e2d\u6587\n") ++ self.assert_writes([b"\xe4\xb8\xad\xe6\x96\x87\n"]) ++ ++ # Printing Non-BMP emoji ++ self.log.write("\U0001f600\n") ++ self.assert_writes([b"\xf0\x9f\x98\x80\n"]) ++ ++ # Non-encodable surrogates are replaced ++ self.log.write("\ud800\udc00\n") ++ self.assert_writes([b"??\n"]) ++ ++ def test_modified_null(self): ++ # Null characters are logged using "modified UTF-8". ++ self.log.write("\u0000\n") ++ self.assert_writes([b"\xc0\x80\n"]) ++ self.log.write("a\u0000\n") ++ self.assert_writes([b"a\xc0\x80\n"]) ++ self.log.write("\u0000b\n") ++ self.assert_writes([b"\xc0\x80b\n"]) ++ self.log.write("a\u0000b\n") ++ self.assert_writes([b"a\xc0\x80b\n"]) ++ ++ def test_nonstandard_str(self): ++ # String subclasses are accepted, but they should be converted ++ # to a standard str without calling any of their methods. ++ class CustomStr(str): ++ def splitlines(self, *args, **kwargs): ++ raise AssertionError() ++ ++ def __len__(self): ++ raise AssertionError() ++ ++ def __str__(self): ++ raise AssertionError() ++ ++ self.log.write(CustomStr("custom\n")) ++ self.assert_writes([b"custom\n"]) ++ ++ def test_non_str(self): ++ # Non-string classes are not accepted. ++ for obj in [b"", b"hello", None, 42]: ++ with self.subTest(obj=obj): ++ with self.assertRaisesRegex( ++ TypeError, ++ fr"write\(\) argument must be str, not " ++ fr"{type(obj).__name__}" ++ ): ++ self.log.write(obj) ++ ++ def test_byteslike_in_buffer(self): ++ # The underlying buffer *can* accept bytes-like objects ++ self.log.buffer.write(bytearray(b"hello")) ++ self.log.flush() ++ ++ self.log.buffer.write(b"") ++ self.log.flush() ++ ++ self.log.buffer.write(b"goodbye") ++ self.log.flush() ++ ++ self.assert_writes([b"hello", b"goodbye"]) ++ ++ def test_non_byteslike_in_buffer(self): ++ for obj in ["hello", None, 42]: ++ with self.subTest(obj=obj): ++ with self.assertRaisesRegex( ++ TypeError, ++ fr"write\(\) argument must be bytes-like, not " ++ fr"{type(obj).__name__}" ++ ): ++ self.log.buffer.write(obj) +diff --git a/Lib/test/test_capi/test_config.py b/Lib/test/test_capi/test_config.py +index 77730ad2f32..a3179efe4a8 100644 +--- a/Lib/test/test_capi/test_config.py ++++ b/Lib/test/test_capi/test_config.py +@@ -110,6 +110,10 @@ + options.extend(( + ("_pystats", bool, None), + )) ++ if support.is_apple: ++ options.extend(( ++ ("use_system_logger", bool, None), ++ )) + + for name, option_type, sys_attr in options: + with self.subTest(name=name, option_type=option_type, +diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py +index bf861ef06ee..468f821370e 100644 +--- a/Lib/test/test_embed.py ++++ b/Lib/test/test_embed.py +@@ -649,6 +649,8 @@ + CONFIG_COMPAT.update({ + 'legacy_windows_stdio': False, + }) ++ if support.is_apple: ++ CONFIG_COMPAT['use_system_logger'] = False + + CONFIG_PYTHON = dict(CONFIG_COMPAT, + _config_init=API_PYTHON, +diff --git a/Makefile.pre.in b/Makefile.pre.in +index 8d94ba361fd..6c1c95d4dd9 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -2132,7 +2132,6 @@ + # 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) +-XCRESULT=$(XCFOLDER)/$(MULTIARCH).xcresult + .PHONY: testios + testios: + @if test "$(MACHDEP)" != "ios"; then \ +@@ -2151,29 +2150,12 @@ + echo "Cannot find a finalized iOS Python.framework. Have you run 'make install' to finalize the framework build?"; \ + exit 1;\ + fi +- # Copy the testbed project into the build folder +- cp -r $(srcdir)/iOS/testbed $(XCFOLDER) +- # Copy the framework from the install location to the testbed project. +- cp -r $(PYTHONFRAMEWORKPREFIX)/* $(XCFOLDER)/Python.xcframework/ios-arm64_x86_64-simulator +- +- # Run the test suite for the Xcode project, targeting the iOS simulator. +- # If the suite fails, touch a file in the test folder as a marker +- if ! xcodebuild test -project $(XCFOLDER)/iOSTestbed.xcodeproj -scheme "iOSTestbed" -destination "platform=iOS Simulator,name=iPhone SE (3rd Generation)" -resultBundlePath $(XCRESULT) -derivedDataPath $(XCFOLDER)/DerivedData ; then \ +- touch $(XCFOLDER)/failed; \ +- fi + +- # Regardless of success or failure, extract and print the test output +- xcrun xcresulttool get --path $(XCRESULT) \ +- --id $$( \ +- xcrun xcresulttool get --path $(XCRESULT) --format json | \ +- $(PYTHON_FOR_BUILD) -c "import sys, json; result = json.load(sys.stdin); print(result['actions']['_values'][0]['actionResult']['logRef']['id']['_value'])" \ +- ) \ +- --format json | \ +- $(PYTHON_FOR_BUILD) -c "import sys, json; result = json.load(sys.stdin); print(result['subsections']['_values'][1]['subsections']['_values'][0]['emittedOutput']['_value'])" ++ # Clone the testbed project into the XCFOLDER ++ $(PYTHON_FOR_BUILD) $(srcdir)/iOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER)" + +- @if test -e $(XCFOLDER)/failed ; then \ +- exit 1; \ +- fi ++ # Run the testbed project ++ $(PYTHON_FOR_BUILD) "$(XCFOLDER)" 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 ec0857a4a99..2350e9dc821 100644 --- a/Misc/platform_triplet.c @@ -162,11 +588,192 @@ index ec0857a4a99..2350e9dc821 100644 // Older macOS SDKs do not define TARGET_OS_OSX # elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX PLATFORM_TRIPLET=darwin +diff --git a/Python/initconfig.c b/Python/initconfig.c +index 438f8a5c1cf..7851b86db1f 100644 +--- a/Python/initconfig.c ++++ b/Python/initconfig.c +@@ -168,6 +168,9 @@ + SPEC(tracemalloc, UINT, READ_ONLY, NO_SYS), + SPEC(use_frozen_modules, BOOL, READ_ONLY, NO_SYS), + SPEC(use_hash_seed, BOOL, READ_ONLY, NO_SYS), ++#ifdef __APPLE__ ++ SPEC(use_system_logger, BOOL, PUBLIC, NO_SYS), ++#endif + SPEC(user_site_directory, BOOL, READ_ONLY, NO_SYS), // sys.flags.no_user_site + SPEC(warn_default_encoding, BOOL, READ_ONLY, NO_SYS), + +@@ -884,6 +887,9 @@ + assert(config->cpu_count != 0); + // config->use_frozen_modules is initialized later + // by _PyConfig_InitImportConfig(). ++#ifdef __APPLE__ ++ assert(config->use_system_logger >= 0); ++#endif + #ifdef Py_STATS + assert(config->_pystats >= 0); + #endif +@@ -986,6 +992,9 @@ + config->_is_python_build = 0; + config->code_debug_ranges = 1; + config->cpu_count = -1; ++#ifdef __APPLE__ ++ config->use_system_logger = 0; ++#endif + #ifdef Py_GIL_DISABLED + config->enable_gil = _PyConfig_GIL_DEFAULT; + config->tlbc_enabled = 1; +@@ -1015,6 +1024,9 @@ + #ifdef MS_WINDOWS + config->legacy_windows_stdio = 0; + #endif ++#ifdef __APPLE__ ++ config->use_system_logger = 0; ++#endif + } + + +@@ -1049,6 +1061,9 @@ + #ifdef MS_WINDOWS + config->legacy_windows_stdio = 0; + #endif ++#ifdef __APPLE__ ++ config->use_system_logger = 0; ++#endif + } + + +diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c +index 23882d08384..64985527606 100644 +--- a/Python/pylifecycle.c ++++ b/Python/pylifecycle.c +@@ -45,7 +45,9 @@ + #endif + + #if defined(__APPLE__) ++# include + # include ++# include + #endif + + #ifdef HAVE_SIGNAL_H +@@ -75,6 +77,9 @@ + #ifdef __ANDROID__ + static PyStatus init_android_streams(PyThreadState *tstate); + #endif ++#if defined(__APPLE__) ++static PyStatus init_apple_streams(PyThreadState *tstate); ++#endif + static void wait_for_thread_shutdown(PyThreadState *tstate); + static void finalize_subinterpreters(void); + static void call_ll_exitfuncs(_PyRuntimeState *runtime); +@@ -1257,6 +1262,14 @@ + return status; + } + #endif ++#if defined(__APPLE__) ++ if (config->use_system_logger) { ++ status = init_apple_streams(tstate); ++ if (_PyStatus_EXCEPTION(status)) { ++ return status; ++ } ++ } ++#endif + + #ifdef Py_DEBUG + run_presite(tstate); +@@ -2931,6 +2944,75 @@ + + #endif // __ANDROID__ + ++#if defined(__APPLE__) ++ ++static PyObject * ++apple_log_write_impl(PyObject *self, PyObject *args) ++{ ++ int logtype = 0; ++ const char *text = NULL; ++ if (!PyArg_ParseTuple(args, "iy", &logtype, &text)) { ++ return NULL; ++ } ++ ++ // Call the underlying Apple logging API. The os_log unified logging APIs ++ // were introduced in macOS 10.12, iOS 10.0, tvOS 10.0, and watchOS 3.0; ++ // this call is a no-op on older versions. ++ #if TARGET_OS_IPHONE || (TARGET_OS_OSX && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) ++ // Pass the user-provided text through explicit %s formatting ++ // to avoid % literals being interpreted as a formatting directive. ++ os_log_with_type(OS_LOG_DEFAULT, logtype, "%s", text); ++ #endif ++ Py_RETURN_NONE; ++} ++ ++ ++static PyMethodDef apple_log_write_method = { ++ "apple_log_write", apple_log_write_impl, METH_VARARGS ++}; ++ ++ ++static PyStatus ++init_apple_streams(PyThreadState *tstate) ++{ ++ PyStatus status = _PyStatus_OK(); ++ PyObject *_apple_support = NULL; ++ PyObject *apple_log_write = NULL; ++ PyObject *result = NULL; ++ ++ _apple_support = PyImport_ImportModule("_apple_support"); ++ if (_apple_support == NULL) { ++ goto error; ++ } ++ ++ apple_log_write = PyCFunction_New(&apple_log_write_method, NULL); ++ if (apple_log_write == NULL) { ++ goto error; ++ } ++ ++ // Initialize the logging streams, sending stdout -> Default; stderr -> Error ++ result = PyObject_CallMethod( ++ _apple_support, "init_streams", "Oii", ++ apple_log_write, OS_LOG_TYPE_DEFAULT, OS_LOG_TYPE_ERROR); ++ if (result == NULL) { ++ goto error; ++ } ++ ++ goto done; ++ ++error: ++ _PyErr_Print(tstate); ++ status = _PyStatus_ERR("failed to initialize Apple log streams"); ++ ++done: ++ Py_XDECREF(result); ++ Py_XDECREF(apple_log_write); ++ Py_XDECREF(_apple_support); ++ return status; ++} ++ ++#endif // __APPLE__ ++ + + static void + _Py_FatalError_DumpTracebacks(int fd, PyInterpreterState *interp, +diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h +index c8cdb933bb1..584b050fc4b 100644 +--- a/Python/stdlib_module_names.h ++++ b/Python/stdlib_module_names.h +@@ -6,6 +6,7 @@ + "_abc", + "_aix_support", + "_android_support", ++"_apple_support", + "_ast", + "_asyncio", + "_bisect", diff --git a/configure b/configure -index 7cdd386c387..5e968a26b17 100755 +index 5b44a3d6992..83803f12853 100755 --- a/configure +++ b/configure -@@ -978,6 +978,8 @@ +@@ -979,6 +979,8 @@ CFLAGS CC HAS_XCRUN @@ -175,7 +782,7 @@ index 7cdd386c387..5e968a26b17 100755 IPHONEOS_DEPLOYMENT_TARGET EXPORT_MACOSX_DEPLOYMENT_TARGET CONFIGURE_MACOSX_DEPLOYMENT_TARGET -@@ -4054,6 +4056,12 @@ +@@ -4053,6 +4055,12 @@ *-apple-ios*) ac_sys_system=iOS ;; @@ -188,7 +795,7 @@ index 7cdd386c387..5e968a26b17 100755 *-*-vxworks*) ac_sys_system=VxWorks ;; -@@ -4108,7 +4116,7 @@ +@@ -4130,7 +4138,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. # @@ -197,7 +804,7 @@ index 7cdd386c387..5e968a26b17 100755 # 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 -@@ -4123,6 +4131,14 @@ +@@ -4145,6 +4153,14 @@ 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 ;; @@ -212,7 +819,7 @@ index 7cdd386c387..5e968a26b17 100755 *) esac fi -@@ -4131,6 +4147,14 @@ +@@ -4153,6 +4169,14 @@ 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 ;; @@ -227,7 +834,7 @@ index 7cdd386c387..5e968a26b17 100755 *) esac fi -@@ -4139,6 +4163,14 @@ +@@ -4161,6 +4185,14 @@ 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 ;; @@ -242,7 +849,7 @@ index 7cdd386c387..5e968a26b17 100755 *) esac fi -@@ -4147,6 +4179,14 @@ +@@ -4169,6 +4201,14 @@ 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++ ;; @@ -257,7 +864,7 @@ index 7cdd386c387..5e968a26b17 100755 *) esac fi -@@ -4267,8 +4307,10 @@ +@@ -4289,8 +4329,10 @@ case $enableval in yes) case $ac_sys_system in @@ -270,7 +877,7 @@ index 7cdd386c387..5e968a26b17 100755 *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 esac esac -@@ -4277,6 +4319,8 @@ +@@ -4299,6 +4341,8 @@ no) case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -279,7 +886,7 @@ index 7cdd386c387..5e968a26b17 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4383,6 +4427,36 @@ +@@ -4405,6 +4449,36 @@ ac_config_files="$ac_config_files iOS/Resources/Info.plist" @@ -316,7 +923,7 @@ index 7cdd386c387..5e968a26b17 100755 ;; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 -@@ -4394,6 +4468,8 @@ +@@ -4416,6 +4490,8 @@ case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -325,7 +932,7 @@ index 7cdd386c387..5e968a26b17 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4447,8 +4523,8 @@ +@@ -4469,8 +4545,8 @@ case "$withval" in yes) case $ac_sys_system in @@ -336,7 +943,7 @@ index 7cdd386c387..5e968a26b17 100755 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 ;; -@@ -4466,8 +4542,8 @@ +@@ -4488,8 +4564,8 @@ else $as_nop case $ac_sys_system in @@ -347,7 +954,7 @@ index 7cdd386c387..5e968a26b17 100755 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; } -@@ -4521,6 +4597,50 @@ +@@ -4543,6 +4619,50 @@ ;; esac ;; @@ -398,7 +1005,7 @@ index 7cdd386c387..5e968a26b17 100755 *-*-vxworks*) _host_ident=$host_cpu ;; -@@ -4599,9 +4719,13 @@ +@@ -4621,9 +4741,13 @@ define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; @@ -413,7 +1020,7 @@ index 7cdd386c387..5e968a26b17 100755 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -4664,7 +4788,10 @@ +@@ -4686,7 +4810,10 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' @@ -425,7 +1032,7 @@ index 7cdd386c387..5e968a26b17 100755 # checks for alternative programs -@@ -4705,6 +4832,16 @@ +@@ -4727,6 +4854,16 @@ as_fn_append CFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" as_fn_append LDFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" ;; #( @@ -442,7 +1049,7 @@ index 7cdd386c387..5e968a26b17 100755 *) : ;; esac -@@ -7006,6 +7143,10 @@ +@@ -7031,6 +7168,10 @@ MULTIARCH="" ;; #( iOS) : MULTIARCH="" ;; #( @@ -453,7 +1060,7 @@ index 7cdd386c387..5e968a26b17 100755 FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -7026,7 +7167,7 @@ +@@ -7051,7 +7192,7 @@ printf "%s\n" "$MULTIARCH" >&6; } case $ac_sys_system in #( @@ -462,7 +1069,7 @@ index 7cdd386c387..5e968a26b17 100755 SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2` ;; #( *) : SOABI_PLATFORM=$PLATFORM_TRIPLET -@@ -7077,6 +7218,14 @@ +@@ -7102,6 +7243,14 @@ PY_SUPPORT_TIER=3 ;; #( aarch64-apple-ios*/clang) : PY_SUPPORT_TIER=3 ;; #( @@ -477,7 +1084,7 @@ index 7cdd386c387..5e968a26b17 100755 aarch64-*-linux-android/clang) : PY_SUPPORT_TIER=3 ;; #( x86_64-*-linux-android/clang) : -@@ -7547,7 +7696,7 @@ +@@ -7531,7 +7680,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; @@ -486,7 +1093,7 @@ index 7cdd386c387..5e968a26b17 100755 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5;; -@@ -7613,7 +7762,7 @@ +@@ -7597,7 +7746,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -495,7 +1102,7 @@ index 7cdd386c387..5e968a26b17 100755 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -12952,7 +13101,7 @@ +@@ -13150,7 +13299,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -504,7 +1111,7 @@ index 7cdd386c387..5e968a26b17 100755 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -13085,7 +13234,7 @@ +@@ -13283,7 +13432,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -513,7 +1120,7 @@ index 7cdd386c387..5e968a26b17 100755 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -13109,7 +13258,7 @@ +@@ -13307,7 +13456,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -522,7 +1129,7 @@ index 7cdd386c387..5e968a26b17 100755 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -14508,7 +14657,7 @@ +@@ -14759,7 +14908,7 @@ ctypes_malloc_closure=yes ;; #( @@ -531,7 +1138,7 @@ index 7cdd386c387..5e968a26b17 100755 ctypes_malloc_closure=yes ;; #( -@@ -17959,12 +18108,6 @@ +@@ -18262,12 +18411,6 @@ then : printf "%s\n" "#define HAVE_DUP3 1" >>confdefs.h @@ -544,7 +1151,7 @@ index 7cdd386c387..5e968a26b17 100755 fi ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes -@@ -18025,18 +18168,6 @@ +@@ -18328,18 +18471,6 @@ then : printf "%s\n" "#define HAVE_FEXECVE 1" >>confdefs.h @@ -563,7 +1170,7 @@ index 7cdd386c387..5e968a26b17 100755 fi ac_fn_c_check_func "$LINENO" "fpathconf" "ac_cv_func_fpathconf" if test "x$ac_cv_func_fpathconf" = xyes -@@ -18463,24 +18594,6 @@ +@@ -18766,24 +18897,6 @@ then : printf "%s\n" "#define HAVE_POSIX_OPENPT 1" >>confdefs.h @@ -588,7 +1195,7 @@ index 7cdd386c387..5e968a26b17 100755 fi ac_fn_c_check_func "$LINENO" "pread" "ac_cv_func_pread" if test "x$ac_cv_func_pread" = xyes -@@ -18769,12 +18882,6 @@ +@@ -19072,12 +19185,6 @@ then : printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h @@ -601,7 +1208,7 @@ index 7cdd386c387..5e968a26b17 100755 fi ac_fn_c_check_func "$LINENO" "sigfillset" "ac_cv_func_sigfillset" if test "x$ac_cv_func_sigfillset" = xyes -@@ -19043,11 +19150,11 @@ +@@ -19346,11 +19453,11 @@ fi @@ -615,7 +1222,7 @@ index 7cdd386c387..5e968a26b17 100755 ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" if test "x$ac_cv_func_getentropy" = xyes then : -@@ -19069,6 +19176,53 @@ +@@ -19372,6 +19479,53 @@ fi @@ -669,7 +1276,7 @@ index 7cdd386c387..5e968a26b17 100755 { 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} -@@ -21865,7 +22019,8 @@ +@@ -22247,7 +22401,8 @@ # check for openpty, login_tty, and forkpty @@ -679,7 +1286,7 @@ index 7cdd386c387..5e968a26b17 100755 for ac_func in openpty do : -@@ -21961,7 +22116,7 @@ +@@ -22343,7 +22498,7 @@ fi done @@ -688,7 +1295,7 @@ index 7cdd386c387..5e968a26b17 100755 printf %s "checking for library containing login_tty... " >&6; } if test ${ac_cv_search_login_tty+y} then : -@@ -22118,6 +22273,7 @@ +@@ -22500,6 +22655,7 @@ fi done @@ -696,7 +1303,7 @@ index 7cdd386c387..5e968a26b17 100755 # check for long file support functions ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" -@@ -22364,10 +22520,10 @@ +@@ -22746,10 +22902,10 @@ done @@ -709,7 +1316,7 @@ index 7cdd386c387..5e968a26b17 100755 then for ac_func in clock_settime -@@ -24597,8 +24753,8 @@ +@@ -24977,8 +25133,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -720,7 +1327,7 @@ index 7cdd386c387..5e968a26b17 100755 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -27246,7 +27402,7 @@ +@@ -27730,7 +27886,7 @@ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for device files" >&5 printf "%s\n" "$as_me: checking for device files" >&6;} @@ -729,7 +1336,7 @@ index 7cdd386c387..5e968a26b17 100755 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -27679,7 +27835,7 @@ +@@ -28162,7 +28318,7 @@ with_ensurepip=no ;; #( WASI) : with_ensurepip=no ;; #( @@ -738,7 +1345,7 @@ index 7cdd386c387..5e968a26b17 100755 with_ensurepip=no ;; #( *) : with_ensurepip=upgrade -@@ -28704,7 +28860,7 @@ +@@ -29091,7 +29247,7 @@ ;; #( Darwin) : ;; #( @@ -747,7 +1354,7 @@ index 7cdd386c387..5e968a26b17 100755 -@@ -32469,6 +32625,8 @@ +@@ -32989,6 +33145,8 @@ "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" ;; @@ -757,7 +1364,7 @@ index 7cdd386c387..5e968a26b17 100755 "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 24e28a1e2de..3e008102154 100644 +index 7904f8990c4..f2367cd47d3 100644 --- a/configure.ac +++ b/configure.ac @@ -330,6 +330,12 @@ @@ -773,7 +1380,7 @@ index 24e28a1e2de..3e008102154 100644 *-*-vxworks*) ac_sys_system=VxWorks ;; -@@ -382,7 +388,7 @@ +@@ -401,7 +407,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. # @@ -782,7 +1389,7 @@ index 24e28a1e2de..3e008102154 100644 # 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 -@@ -397,6 +403,14 @@ +@@ -416,6 +422,14 @@ 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 ;; @@ -797,7 +1404,7 @@ index 24e28a1e2de..3e008102154 100644 *) esac fi -@@ -405,6 +419,14 @@ +@@ -424,6 +438,14 @@ 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 ;; @@ -812,7 +1419,7 @@ index 24e28a1e2de..3e008102154 100644 *) esac fi -@@ -413,6 +435,14 @@ +@@ -432,6 +454,14 @@ 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 ;; @@ -827,7 +1434,7 @@ index 24e28a1e2de..3e008102154 100644 *) esac fi -@@ -421,6 +451,14 @@ +@@ -440,6 +470,14 @@ 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++ ;; @@ -842,7 +1449,7 @@ index 24e28a1e2de..3e008102154 100644 *) esac fi -@@ -535,8 +573,10 @@ +@@ -554,8 +592,10 @@ case $enableval in yes) case $ac_sys_system in @@ -855,7 +1462,7 @@ index 24e28a1e2de..3e008102154 100644 *) AC_MSG_ERROR([Unknown platform for framework build]) esac esac -@@ -545,6 +585,8 @@ +@@ -564,6 +604,8 @@ no) case $ac_sys_system in iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;; @@ -864,7 +1471,7 @@ index 24e28a1e2de..3e008102154 100644 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -647,6 +689,34 @@ +@@ -666,6 +708,34 @@ AC_CONFIG_FILES([iOS/Resources/Info.plist]) ;; @@ -899,7 +1506,7 @@ index 24e28a1e2de..3e008102154 100644 *) AC_MSG_ERROR([Unknown platform for framework build]) ;; -@@ -655,6 +725,8 @@ +@@ -674,6 +744,8 @@ ],[ case $ac_sys_system in iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;; @@ -908,7 +1515,7 @@ index 24e28a1e2de..3e008102154 100644 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -707,8 +779,8 @@ +@@ -726,8 +798,8 @@ case "$withval" in yes) case $ac_sys_system in @@ -919,7 +1526,7 @@ index 24e28a1e2de..3e008102154 100644 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" ;; *) AC_MSG_ERROR([no default app store compliance patch available for $ac_sys_system]) ;; -@@ -722,8 +794,8 @@ +@@ -741,8 +813,8 @@ esac ],[ case $ac_sys_system in @@ -930,7 +1537,7 @@ index 24e28a1e2de..3e008102154 100644 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" AC_MSG_RESULT([applying default app store compliance patch]) ;; -@@ -771,6 +843,46 @@ +@@ -790,6 +862,46 @@ ;; esac ;; @@ -977,7 +1584,7 @@ index 24e28a1e2de..3e008102154 100644 *-*-vxworks*) _host_ident=$host_cpu ;; -@@ -848,9 +960,13 @@ +@@ -867,9 +979,13 @@ define_xopen_source=no;; Darwin/@<:@[12]@:>@@<:@0-9@:>@.*) define_xopen_source=no;; @@ -992,7 +1599,7 @@ index 24e28a1e2de..3e008102154 100644 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -909,8 +1025,11 @@ +@@ -928,8 +1044,11 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' @@ -1005,7 +1612,7 @@ index 24e28a1e2de..3e008102154 100644 # checks for alternative programs -@@ -944,11 +1063,17 @@ +@@ -963,11 +1082,17 @@ ], ) @@ -1024,7 +1631,7 @@ index 24e28a1e2de..3e008102154 100644 ], ) -@@ -1136,6 +1261,8 @@ +@@ -1156,6 +1281,8 @@ AS_CASE([$ac_sys_system], [Darwin*], [MULTIARCH=""], [iOS], [MULTIARCH=""], @@ -1033,7 +1640,7 @@ index 24e28a1e2de..3e008102154 100644 [FreeBSD*], [MULTIARCH=""], [MULTIARCH=$($CC --print-multiarch 2>/dev/null)] ) -@@ -1157,7 +1284,7 @@ +@@ -1177,7 +1304,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], @@ -1042,7 +1649,7 @@ index 24e28a1e2de..3e008102154 100644 [SOABI_PLATFORM=$PLATFORM_TRIPLET] ) -@@ -1191,6 +1318,10 @@ +@@ -1211,6 +1338,10 @@ [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 @@ -1053,7 +1660,7 @@ index 24e28a1e2de..3e008102154 100644 [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 -@@ -1522,7 +1653,7 @@ +@@ -1520,7 +1651,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; @@ -1062,7 +1669,7 @@ index 24e28a1e2de..3e008102154 100644 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) AC_MSG_ERROR([Unknown platform for framework build]);; -@@ -1587,7 +1718,7 @@ +@@ -1585,7 +1716,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -1071,7 +1678,7 @@ index 24e28a1e2de..3e008102154 100644 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -3462,7 +3593,7 @@ +@@ -3407,7 +3538,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -1080,7 +1687,7 @@ index 24e28a1e2de..3e008102154 100644 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -3586,7 +3717,7 @@ +@@ -3531,7 +3662,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -1089,7 +1696,7 @@ index 24e28a1e2de..3e008102154 100644 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -3610,7 +3741,7 @@ +@@ -3555,7 +3686,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -1098,7 +1705,7 @@ index 24e28a1e2de..3e008102154 100644 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -3994,7 +4125,7 @@ +@@ -3975,7 +4106,7 @@ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], @@ -1107,7 +1714,7 @@ index 24e28a1e2de..3e008102154 100644 ctypes_malloc_closure=yes ], [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] -@@ -5088,9 +5219,9 @@ +@@ -5093,9 +5224,9 @@ # checks for library functions AC_CHECK_FUNCS([ \ accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ @@ -1119,7 +1726,7 @@ index 24e28a1e2de..3e008102154 100644 gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \ getgrnam_r getgrouplist gethostname getitimer getloadavg getlogin \ getpeername getpgid getpid getppid getpriority _getpty \ -@@ -5098,15 +5229,14 @@ +@@ -5103,15 +5234,14 @@ 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 \ @@ -1137,7 +1744,7 @@ index 24e28a1e2de..3e008102154 100644 sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ -@@ -5121,12 +5251,20 @@ +@@ -5126,12 +5256,20 @@ AC_CHECK_FUNCS([lchmod]) fi @@ -1161,7 +1768,7 @@ index 24e28a1e2de..3e008102154 100644 fi AC_CHECK_DECL([dirfd], -@@ -5377,20 +5515,22 @@ +@@ -5385,20 +5523,22 @@ ]) # check for openpty, login_tty, and forkpty @@ -1198,7 +1805,7 @@ index 24e28a1e2de..3e008102154 100644 # check for long file support functions AC_CHECK_FUNCS([fseek64 fseeko fstatvfs ftell64 ftello statvfs]) -@@ -5429,10 +5569,10 @@ +@@ -5437,10 +5577,10 @@ ]) ]) @@ -1211,7 +1818,7 @@ index 24e28a1e2de..3e008102154 100644 then AC_CHECK_FUNCS([clock_settime], [], [ AC_CHECK_LIB([rt], [clock_settime], [ -@@ -6183,8 +6323,8 @@ +@@ -6191,8 +6331,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -1222,7 +1829,7 @@ index 24e28a1e2de..3e008102154 100644 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -6792,7 +6932,7 @@ +@@ -6856,7 +6996,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. @@ -1231,7 +1838,7 @@ index 24e28a1e2de..3e008102154 100644 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -7049,7 +7189,7 @@ +@@ -7112,7 +7252,7 @@ AS_CASE([$ac_sys_system], [Emscripten], [with_ensurepip=no], [WASI], [with_ensurepip=no], @@ -1240,7 +1847,7 @@ index 24e28a1e2de..3e008102154 100644 [with_ensurepip=upgrade] ) ]) -@@ -7458,7 +7598,7 @@ +@@ -7506,7 +7646,7 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], @@ -1249,6 +1856,85 @@ index 24e28a1e2de..3e008102154 100644 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/README.rst b/iOS/README.rst +index e33455eef8f..13b88514493 100644 +--- a/iOS/README.rst ++++ b/iOS/README.rst +@@ -285,52 +285,42 @@ + * Install the Python iOS framework into the copy of the testbed project; and + * Run the test suite on an "iPhone SE (3rd generation)" simulator. + +-While the test suite is running, Xcode does not display any console output. +-After showing some Xcode build commands, the console output will print ``Testing +-started``, and then appear to stop. It will remain in this state until the test +-suite completes. On a 2022 M1 MacBook Pro, the test suite takes approximately 12 +-minutes to run; a couple of extra minutes is required to boot and prepare the +-iOS simulator. +- + On success, the test suite will exit and report successful completion of the +-test suite. No output of the Python test suite will be displayed. +- +-On failure, the output of the Python test suite *will* be displayed. This will +-show the details of the tests that failed. ++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 + ----------------------- + +-The easiest way to diagnose a single test failure is to open the testbed project +-in Xcode and run the tests from there using the "Product > Test" menu item. +- +-To test in Xcode, you must ensure the testbed project has a copy of a compiled +-framework. If you've configured your build with the default install location of +-``iOS/Frameworks``, you can copy from that location into the test project. To +-test on an ARM64 simulator, run:: +- +- $ rm -rf iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator/* +- $ cp -r iOS/Frameworks/arm64-iphonesimulator/* iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator ++Running ``make test`` generates a standalone version of the ``iOS/testbed`` ++project, and runs the full test suite. It does this using ``iOS/testbed`` ++itself - the folder is an executable module that can be used to create and run ++a clone of the testbed project. + +-To test on an x86-64 simulator, run:: ++You can generate your own standalone testbed instance by running:: + +- $ rm -rf iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator/* +- $ cp -r iOS/Frameworks/x86_64-iphonesimulator/* iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator ++ $ python iOS/testbed clone --framework iOS/Frameworks/arm64-iphonesimulator my-testbed + +-To test on a physical device:: ++This invocation assumes that ``iOS/Frameworks/arm64-iphonesimulator`` is the ++path to the iOS simulator framework for your platform (ARM64 in this case); ++``my-testbed`` is the name of the folder for the new testbed clone. + +- $ rm -rf iOS/testbed/Python.xcframework/ios-arm64/* +- $ cp -r iOS/Frameworks/arm64-iphoneos/* iOS/testbed/Python.xcframework/ios-arm64 ++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:: + +-Alternatively, you can configure your build to install directly into the +-testbed project. For a simulator, use:: ++ $ python my-testbed run -- test -W test_os + +- --enable-framework=$(pwd)/iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator ++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. + +-For a physical device, use:: ++You can also open the testbed project in Xcode by running:: + +- --enable-framework=$(pwd)/iOS/testbed/Python.xcframework/ios-arm64 ++ $ open my-testbed/iOSTestbed.xcodeproj + ++This will allow you to use the full Xcode suite of tools for debugging. + + Testing on an iOS device + ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/iOS/Resources/Info.plist.in b/iOS/Resources/Info.plist.in index c3e261ecd9e..26ef7a95de4 100644 --- a/iOS/Resources/Info.plist.in @@ -1269,6 +1955,546 @@ index c3e261ecd9e..26ef7a95de4 100644 CFBundleSupportedPlatforms iPhoneOS +diff --git a/iOS/Resources/bin/arm64-apple-ios-ar b/iOS/Resources/bin/arm64-apple-ios-ar +index 8122332b9c1..3cf3eb21874 100755 +--- a/iOS/Resources/bin/arm64-apple-ios-ar ++++ b/iOS/Resources/bin/arm64-apple-ios-ar +@@ -1,2 +1,2 @@ + #!/bin/sh +-xcrun --sdk iphoneos${IOS_SDK_VERSION} ar $@ ++xcrun --sdk iphoneos${IOS_SDK_VERSION} ar "$@" +diff --git a/iOS/Resources/bin/arm64-apple-ios-clang b/iOS/Resources/bin/arm64-apple-ios-clang +index 4d525751eba..c39519cd1f8 100755 +--- a/iOS/Resources/bin/arm64-apple-ios-clang ++++ b/iOS/Resources/bin/arm64-apple-ios-clang +@@ -1,2 +1,2 @@ + #!/bin/sh +-xcrun --sdk iphoneos${IOS_SDK_VERSION} clang -target arm64-apple-ios $@ ++xcrun --sdk iphoneos${IOS_SDK_VERSION} clang -target arm64-apple-ios "$@" +diff --git a/iOS/Resources/bin/arm64-apple-ios-clang++ b/iOS/Resources/bin/arm64-apple-ios-clang++ +index f24bec11268..d9b12925f38 100755 +--- a/iOS/Resources/bin/arm64-apple-ios-clang++ ++++ b/iOS/Resources/bin/arm64-apple-ios-clang++ +@@ -1,2 +1,2 @@ + #!/bin/sh +-xcrun --sdk iphoneos${IOS_SDK_VERSION} clang++ -target arm64-apple-ios $@ ++xcrun --sdk iphoneos${IOS_SDK_VERSION} clang++ -target arm64-apple-ios "$@" +diff --git a/iOS/Resources/bin/arm64-apple-ios-cpp b/iOS/Resources/bin/arm64-apple-ios-cpp +index 891bb25bb43..24da23d3448 100755 +--- a/iOS/Resources/bin/arm64-apple-ios-cpp ++++ b/iOS/Resources/bin/arm64-apple-ios-cpp +@@ -1,2 +1,2 @@ + #!/bin/sh +-xcrun --sdk iphoneos${IOS_SDK_VERSION} clang -target arm64-apple-ios -E $@ ++xcrun --sdk iphoneos${IOS_SDK_VERSION} clang -target arm64-apple-ios -E "$@" +diff --git a/iOS/Resources/bin/arm64-apple-ios-simulator-ar b/iOS/Resources/bin/arm64-apple-ios-simulator-ar +index 74ed3bc6df1..b836b6db902 100755 +--- a/iOS/Resources/bin/arm64-apple-ios-simulator-ar ++++ b/iOS/Resources/bin/arm64-apple-ios-simulator-ar +@@ -1,2 +1,2 @@ + #!/bin/sh +-xcrun --sdk iphonesimulator${IOS_SDK_VERSION} ar $@ ++xcrun --sdk iphonesimulator${IOS_SDK_VERSION} ar "$@" +diff --git a/iOS/Resources/bin/arm64-apple-ios-simulator-clang b/iOS/Resources/bin/arm64-apple-ios-simulator-clang +index 32574cad284..92e8d853d6e 100755 +--- a/iOS/Resources/bin/arm64-apple-ios-simulator-clang ++++ b/iOS/Resources/bin/arm64-apple-ios-simulator-clang +@@ -1,2 +1,2 @@ + #!/bin/sh +-xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target arm64-apple-ios-simulator $@ ++xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target arm64-apple-ios-simulator "$@" +diff --git a/iOS/Resources/bin/arm64-apple-ios-simulator-clang++ b/iOS/Resources/bin/arm64-apple-ios-simulator-clang++ +index ef37d05b512..076469cc70c 100755 +--- a/iOS/Resources/bin/arm64-apple-ios-simulator-clang++ ++++ b/iOS/Resources/bin/arm64-apple-ios-simulator-clang++ +@@ -1,2 +1,2 @@ + #!/bin/sh +-xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang++ -target arm64-apple-ios-simulator $@ ++xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang++ -target arm64-apple-ios-simulator "$@" +diff --git a/iOS/Resources/bin/arm64-apple-ios-simulator-cpp b/iOS/Resources/bin/arm64-apple-ios-simulator-cpp +index 6aaf6fbe188..c57f28cee5b 100755 +--- a/iOS/Resources/bin/arm64-apple-ios-simulator-cpp ++++ b/iOS/Resources/bin/arm64-apple-ios-simulator-cpp +@@ -1,2 +1,2 @@ + #!/bin/sh +-xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target arm64-apple-ios-simulator -E $@ ++xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target arm64-apple-ios-simulator -E "$@" +diff --git a/iOS/Resources/bin/x86_64-apple-ios-simulator-ar b/iOS/Resources/bin/x86_64-apple-ios-simulator-ar +index 74ed3bc6df1..b836b6db902 100755 +--- a/iOS/Resources/bin/x86_64-apple-ios-simulator-ar ++++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-ar +@@ -1,2 +1,2 @@ + #!/bin/sh +-xcrun --sdk iphonesimulator${IOS_SDK_VERSION} ar $@ ++xcrun --sdk iphonesimulator${IOS_SDK_VERSION} ar "$@" +diff --git a/iOS/Resources/bin/x86_64-apple-ios-simulator-clang b/iOS/Resources/bin/x86_64-apple-ios-simulator-clang +index bcbe91f6061..17cbe0c8a1e 100755 +--- a/iOS/Resources/bin/x86_64-apple-ios-simulator-clang ++++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-clang +@@ -1,2 +1,2 @@ + #!/bin/sh +-xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target x86_64-apple-ios-simulator $@ ++xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target x86_64-apple-ios-simulator "$@" +diff --git a/iOS/Resources/bin/x86_64-apple-ios-simulator-clang++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-clang++ +index 86f03ea32bc..565d47b24c2 100755 +--- a/iOS/Resources/bin/x86_64-apple-ios-simulator-clang++ ++++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-clang++ +@@ -1,2 +1,2 @@ + #!/bin/sh +-xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang++ -target x86_64-apple-ios-simulator $@ ++xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang++ -target x86_64-apple-ios-simulator "$@" +diff --git a/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp b/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp +index e6a42d9b85d..63fc8e8de2d 100755 +--- a/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp ++++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp +@@ -1,2 +1,2 @@ + #!/bin/sh +-xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target x86_64-apple-ios-simulator -E $@ ++xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target x86_64-apple-ios-simulator -E "$@" +--- /dev/null ++++ b/iOS/testbed/__main__.py +@@ -0,0 +1,395 @@ ++import argparse ++import asyncio ++import json ++import plistlib ++import shutil ++import subprocess ++import sys ++from contextlib import asynccontextmanager ++from datetime import datetime ++from pathlib import Path ++ ++ ++DECODE_ARGS = ("UTF-8", "backslashreplace") ++ ++ ++# Work around a bug involving sys.exit and TaskGroups ++# (https://github.com/python/cpython/issues/101515). ++def exit(*args): ++ raise MySystemExit(*args) ++ ++ ++class MySystemExit(Exception): ++ pass ++ ++ ++# All subprocesses are executed through this context manager so that no matter ++# what happens, they can always be cancelled from another task, and they will ++# always be cleaned up on exit. ++@asynccontextmanager ++async def async_process(*args, **kwargs): ++ process = await asyncio.create_subprocess_exec(*args, **kwargs) ++ try: ++ yield process ++ finally: ++ if process.returncode is None: ++ # Allow a reasonably long time for Xcode to clean itself up, ++ # because we don't want stale emulators left behind. ++ timeout = 10 ++ process.terminate() ++ try: ++ await asyncio.wait_for(process.wait(), timeout) ++ except TimeoutError: ++ print( ++ f"Command {args} did not terminate after {timeout} seconds " ++ f" - sending SIGKILL" ++ ) ++ process.kill() ++ ++ # Even after killing the process we must still wait for it, ++ # otherwise we'll get the warning "Exception ignored in __del__". ++ await asyncio.wait_for(process.wait(), timeout=1) ++ ++ ++async def async_check_output(*args, **kwargs): ++ async with async_process( ++ *args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs ++ ) as process: ++ stdout, stderr = await process.communicate() ++ if process.returncode == 0: ++ return stdout.decode(*DECODE_ARGS) ++ else: ++ raise subprocess.CalledProcessError( ++ process.returncode, ++ args, ++ stdout.decode(*DECODE_ARGS), ++ stderr.decode(*DECODE_ARGS), ++ ) ++ ++ ++# Return a list of UDIDs associated with booted simulators ++async def list_devices(): ++ # 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 iOS simulators ++ return [ ++ simulator["udid"] ++ for runtime, simulators in json_data["devices"].items() ++ for simulator in simulators ++ if runtime.split(".")[-1].startswith("iOS") and simulator["state"] == "Booted" ++ ] ++ ++ ++async def find_device(initial_devices): ++ 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}") ++ return udid ++ else: ++ exit(f"Found more than one new device: {new_devices}") ++ ++ ++async def log_stream_task(initial_devices): ++ # Wait up to 5 minutes for the build to complete and the simulator to boot. ++ udid = await asyncio.wait_for(find_device(initial_devices), 5 * 60) ++ ++ # Stream the iOS 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 "/iOSTestbedTests.xctest/iOSTestbedTests"' ++ ' OR senderImagePath ENDSWITH "/Python.framework/Python"' ++ ), ++ ] ++ ++ async with async_process( ++ *args, ++ stdout=subprocess.PIPE, ++ stderr=subprocess.STDOUT, ++ ) as process: ++ suppress_dupes = False ++ while line := (await process.stdout.readline()).decode(*DECODE_ARGS): ++ # The iOS log streamer can sometimes lag; when it does, it outputs ++ # a warning about messages being dropped... often multiple times. ++ # Only print the first of these duplicated warnings. ++ if line.startswith("=== Messages dropped "): ++ if not suppress_dupes: ++ suppress_dupes = True ++ sys.stdout.write(line) ++ else: ++ suppress_dupes = False ++ sys.stdout.write(line) ++ sys.stdout.flush() ++ ++ ++async def xcode_test(location, simulator, verbose): ++ # Run the test suite on the named simulator ++ print("Starting xcodebuild...") ++ args = [ ++ "xcodebuild", ++ "test", ++ "-project", ++ str(location / "iOSTestbed.xcodeproj"), ++ "-scheme", ++ "iOSTestbed", ++ "-destination", ++ f"platform=iOS Simulator,name={simulator}", ++ "-resultBundlePath", ++ str(location / f"{datetime.now():%Y%m%d-%H%M%S}.xcresult"), ++ "-derivedDataPath", ++ str(location / "DerivedData"), ++ ] ++ if not verbose: ++ args += ["-quiet"] ++ ++ async with async_process( ++ *args, ++ stdout=subprocess.PIPE, ++ stderr=subprocess.STDOUT, ++ ) as process: ++ while line := (await process.stdout.readline()).decode(*DECODE_ARGS): ++ sys.stdout.write(line) ++ sys.stdout.flush() ++ ++ status = await asyncio.wait_for(process.wait(), timeout=1) ++ exit(status) ++ ++ ++def clone_testbed( ++ source: Path, ++ target: Path, ++ framework: Path, ++ apps: list[Path], ++) -> None: ++ if target.exists(): ++ print(f"{target} already exists; aborting without creating project.") ++ sys.exit(10) ++ ++ if framework is None: ++ if not ( ++ source / "Python.xcframework/ios-arm64_x86_64-simulator/bin" ++ ).is_dir(): ++ print( ++ f"The testbed being cloned ({source}) does not contain " ++ f"a simulator framework. Re-run with --framework" ++ ) ++ sys.exit(11) ++ else: ++ if not framework.is_dir(): ++ print(f"{framework} does not exist.") ++ sys.exit(12) ++ elif not ( ++ framework.suffix == ".xcframework" ++ or (framework / "Python.framework").is_dir() ++ ): ++ print( ++ f"{framework} is not an XCframework, " ++ f"or a simulator slice of a framework build." ++ ) ++ sys.exit(13) ++ ++ print("Cloning testbed project:") ++ print(f" Cloning {source}...", end="", flush=True) ++ shutil.copytree(source, target, symlinks=True) ++ print(" done") ++ ++ if framework is not None: ++ if framework.suffix == ".xcframework": ++ print(" Installing XCFramework...", end="", flush=True) ++ xc_framework_path = (target / "Python.xcframework").resolve() ++ if xc_framework_path.is_dir(): ++ shutil.rmtree(xc_framework_path) ++ else: ++ xc_framework_path.unlink() ++ 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) ++ sim_framework_path = ( ++ target / "Python.xcframework" / "ios-arm64_x86_64-simulator" ++ ).resolve() ++ if sim_framework_path.is_dir(): ++ shutil.rmtree(sim_framework_path) ++ else: ++ sim_framework_path.unlink() ++ sim_framework_path.symlink_to( ++ framework.relative_to(sim_framework_path.parent, walk_up=True) ++ ) ++ print(" done") ++ else: ++ print(" Using pre-existing iOS framework.") ++ ++ for app_src in apps: ++ print(f" Installing app {app_src.name!r}...", end="", flush=True) ++ app_target = target / f"iOSTestbed/app/{app_src.name}" ++ if app_target.is_dir(): ++ shutil.rmtree(app_target) ++ shutil.copytree(app_src, app_target) ++ print(" done") ++ ++ print(f"Successfully cloned testbed: {target.resolve()}") ++ ++ ++def update_plist(testbed_path, args): ++ # Add the test runner arguments to the testbed's Info.plist file. ++ info_plist = testbed_path / "iOSTestbed" / "iOSTestbed-Info.plist" ++ with info_plist.open("rb") as f: ++ info = plistlib.load(f) ++ ++ info["TestArgs"] = args ++ ++ with info_plist.open("wb") as f: ++ plistlib.dump(info, f) ++ ++ ++async def run_testbed(simulator: str, args: list[str], verbose: bool=False): ++ location = Path(__file__).parent ++ print("Updating plist...", end="", flush=True) ++ update_plist(location, args) ++ print(" done.") ++ ++ # 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)) ++ 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] ++ ++ ++def main(): ++ parser = argparse.ArgumentParser( ++ description=( ++ "Manages the process of testing a Python project in the iOS simulator." ++ ), ++ ) ++ ++ subcommands = parser.add_subparsers(dest="subcommand") ++ ++ clone = subcommands.add_parser( ++ "clone", ++ description=( ++ "Clone the testbed project, copying in an iOS Python framework and" ++ "any specified application code." ++ ), ++ help="Clone a testbed project to a new location.", ++ ) ++ clone.add_argument( ++ "--framework", ++ help=( ++ "The location of the XCFramework (or simulator-only slice of an " ++ "XCFramework) to use when running the testbed" ++ ), ++ ) ++ clone.add_argument( ++ "--app", ++ dest="apps", ++ action="append", ++ default=[], ++ help="The location of any code to include in the testbed project", ++ ) ++ clone.add_argument( ++ "location", ++ help="The path where the testbed will be cloned.", ++ ) ++ ++ run = subcommands.add_parser( ++ "run", ++ usage="%(prog)s [-h] [--simulator SIMULATOR] -- [ ...]", ++ description=( ++ "Run a testbed project. The arguments provided after `--` will be " ++ "passed to the running iOS process as if they were arguments to " ++ "`python -m`." ++ ), ++ help="Run a testbed project", ++ ) ++ run.add_argument( ++ "--simulator", ++ default="iPhone SE (3rd Generation)", ++ help="The name of the simulator to use (default: 'iPhone SE (3rd Generation)')", ++ ) ++ run.add_argument( ++ "-v", "--verbose", ++ action="store_true", ++ help="Enable verbose output", ++ ) ++ ++ try: ++ pos = sys.argv.index("--") ++ testbed_args = sys.argv[1:pos] ++ test_args = sys.argv[pos + 1 :] ++ except ValueError: ++ testbed_args = sys.argv[1:] ++ test_args = [] ++ ++ context = parser.parse_args(testbed_args) ++ ++ if context.subcommand == "clone": ++ clone_testbed( ++ source=Path(__file__).parent, ++ target=Path(context.location), ++ framework=Path(context.framework).resolve() if context.framework else None, ++ apps=[Path(app) for app in context.apps], ++ ) ++ elif context.subcommand == "run": ++ if test_args: ++ if not ( ++ Path(__file__).parent / "Python.xcframework/ios-arm64_x86_64-simulator/bin" ++ ).is_dir(): ++ print( ++ f"Testbed does not contain a compiled iOS framework. Use " ++ f"`python {sys.argv[0]} clone ...` to create a runnable " ++ f"clone of this testbed." ++ ) ++ sys.exit(20) ++ ++ asyncio.run( ++ run_testbed( ++ simulator=context.simulator, ++ verbose=context.verbose, ++ args=test_args, ++ ) ++ ) ++ else: ++ print(f"Must specify test arguments (e.g., {sys.argv[0]} run -- test)") ++ print() ++ parser.print_help(sys.stderr) ++ sys.exit(21) ++ else: ++ parser.print_help(sys.stderr) ++ sys.exit(1) ++ ++ ++if __name__ == "__main__": ++ main() +diff --git a/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj b/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj +index 6819ac0eeed..c7d63909ee2 100644 +--- a/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj ++++ b/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj +@@ -263,6 +263,7 @@ + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -e\n\nmkdir -p \"$CODESIGNING_FOLDER_PATH/python/lib\"\nif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-iphonesimulator\" ]; then\n echo \"Installing Python modules for iOS Simulator\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64_x86_64-simulator/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nelse\n echo \"Installing Python modules for iOS Device\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nfi\n"; ++ showEnvVarsInLog = 0; + }; + 607A66562B0F06200010BFC8 /* Prepare Python Binary Modules */ = { + isa = PBXShellScriptBuildPhase; +@@ -282,6 +283,7 @@ + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -e\n\ninstall_dylib () {\n INSTALL_BASE=$1\n FULL_EXT=$2\n\n # The name of the extension file\n EXT=$(basename \"$FULL_EXT\")\n # The location of the extension file, relative to the bundle\n RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/} \n # The path to the extension file, relative to the install base\n PYTHON_EXT=${RELATIVE_EXT/$INSTALL_BASE/}\n # The full dotted name of the extension module, constructed from the file path.\n FULL_MODULE_NAME=$(echo $PYTHON_EXT | cut -d \".\" -f 1 | tr \"/\" \".\"); \n # A bundle identifier; not actually used, but required by Xcode framework packaging\n FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr \"_\" \"-\")\n # The name of the framework folder.\n FRAMEWORK_FOLDER=\"Frameworks/$FULL_MODULE_NAME.framework\"\n\n # If the framework folder doesn't exist, create it.\n if [ ! -d \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\" ]; then\n echo \"Creating framework for $RELATIVE_EXT\" \n mkdir -p \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\"\n cp \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleExecutable -string \"$FULL_MODULE_NAME\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleIdentifier -string \"$FRAMEWORK_BUNDLE_ID\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n fi\n \n echo \"Installing binary for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" \n mv \"$FULL_EXT\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\"\n # Create a placeholder .fwork file where the .so was\n echo \"$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" > ${FULL_EXT%.so}.fwork\n # Create a back reference to the .so file location in the framework\n echo \"${RELATIVE_EXT%.so}.fwork\" > \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin\" \n}\n\nPYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/python/lib\")\necho \"Install Python $PYTHON_VER standard library extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib python/lib/$PYTHON_VER/lib-dynload/ \"$FULL_EXT\"\ndone\necho \"Install app package extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app_packages\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app_packages/ \"$FULL_EXT\"\ndone\necho \"Install app extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app/ \"$FULL_EXT\"\ndone\n\n# Clean up dylib template \nrm -f \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\"\necho \"Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)...\"\nfind \"$CODESIGNING_FOLDER_PATH/Frameworks\" -name \"*.framework\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der \"{}\" \\; \n"; ++ showEnvVarsInLog = 0; + }; + /* End PBXShellScriptBuildPhase section */ + +diff --git a/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m b/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m +index db00d43da85..6db38253396 100644 +--- a/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m ++++ b/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m +@@ -24,8 +24,11 @@ + + NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; + +- // Disable all color, as the Xcode log can't display color ++ // 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("PY_COLORS", "0", true); + + // Arguments to pass into the test suite runner. + // argv[0] must identify the process; any subsequent arg +@@ -50,6 +53,8 @@ + // 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 --- /dev/null +++ b/tvOS/README.rst @@ -0,0 +1,108 @@ @@ -1421,62 +2647,62 @@ index c3e261ecd9e..26ef7a95de4 100644 +++ b/tvOS/Resources/bin/arm64-apple-tvos-ar @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk appletvos${TVOS_SDK_VERSION} ar $@ ++xcrun --sdk appletvos${TVOS_SDK_VERSION} ar "$@" --- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-clang @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk appletvos${TVOS_SDK_VERSION} clang -target arm64-apple-tvos $@ ++xcrun --sdk appletvos${TVOS_SDK_VERSION} clang -target arm64-apple-tvos "$@" --- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-clang++ @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk appletvos${TVOS_SDK_VERSION} clang++ -target arm64-apple-tvos $@ ++xcrun --sdk appletvos${TVOS_SDK_VERSION} clang++ -target arm64-apple-tvos "$@" --- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-cpp @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk appletvos${TVOS_SDK_VERSION} clang -target arm64-apple-tvos -E $@ ++xcrun --sdk appletvos${TVOS_SDK_VERSION} clang -target arm64-apple-tvos -E "$@" --- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-ar @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} ar $@ ++xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} ar "$@" --- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-clang @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target arm64-apple-tvos-simulator $@ ++xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target arm64-apple-tvos-simulator "$@" --- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-clang++ @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang++ -target arm64-apple-tvos-simulator $@ ++xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang++ -target arm64-apple-tvos-simulator "$@" --- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-cpp @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target arm64-apple-tvos-simulator -E $@ ++xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target arm64-apple-tvos-simulator -E "$@" --- /dev/null +++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-ar @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} ar $@ ++xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} ar "$@" --- /dev/null +++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-clang @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target x86_64-apple-tvos-simulator $@ ++xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target x86_64-apple-tvos-simulator "$@" --- /dev/null +++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-clang++ @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang++ -target x86_64-apple-tvos-simulator $@ ++xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang++ -target x86_64-apple-tvos-simulator "$@" --- /dev/null +++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-cpp @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target x86_64-apple-tvos-simulator -E $@ ++xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target x86_64-apple-tvos-simulator -E "$@" --- /dev/null +++ b/tvOS/Resources/dylib-Info-template.plist @@ -0,0 +1,26 @@ @@ -1668,62 +2894,62 @@ index c3e261ecd9e..26ef7a95de4 100644 +++ b/watchOS/Resources/bin/arm64-apple-watchos-simulator-ar @@ -0,0 +1,2 @@ +#!/bin/bash -+xcrun --sdk watchsimulator${WATCHOS_SDK_VERSION} ar $@ ++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 $@ ++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 $@ ++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 $@ ++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 $@ ++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 $@ ++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 $@ ++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 $@ ++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 $@ ++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 $@ ++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 $@ ++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 $@ ++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 @@ From 7c9ec06b7037634261b79f1e4d49488ad56e8ec6 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 13 Dec 2024 13:02:13 +0800 Subject: [PATCH 061/113] Fixes #239 - Purge .orig files from release packages. --- Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Makefile b/Makefile index d77f285e..1abcbcf9 100644 --- a/Makefile +++ b/Makefile @@ -316,6 +316,9 @@ $$(PYTHON_LIB-$(target)): $$(PYTHON_SRCDIR-$(target))/python.exe make install \ 2>&1 | tee -a ../python-$(PYTHON_VERSION).install.log + # Remove any .orig files produced by the compliance patching process + find $$(PYTHON_INSTALL-$(target)) -name "*.orig" -exec rm {} \; + endif PYTHON_SITECUSTOMIZE-$(target)=$(PROJECT_DIR)/support/$(PYTHON_VER)/$(os)/platform-site/$(target)/sitecustomize.py @@ -549,6 +552,9 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ # Apply the App Store compliance patch patch --strip 2 --directory $$(PYTHON_INSTALL_VERSION-macosx)/lib/python$(PYTHON_VER) --input $(PROJECT_DIR)/patch/Python/app-store-compliance.patch + # Remove any .orig files produced by the patching process + find $$(PYTHON_INSTALL_VERSION-macosx) -name "*.orig" -exec rm {} \; + # Rewrite the framework to make it standalone patch/make-relocatable.sh $$(PYTHON_INSTALL_VERSION-macosx) 2>&1 > /dev/null From 5eb3316e440418b4a66f70119f3a1635f2c4ca0f Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 13 Dec 2024 13:02:39 +0800 Subject: [PATCH 062/113] Include iOS testbed in iOS release artefacts. --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index 1abcbcf9..04b19588 100644 --- a/Makefile +++ b/Makefile @@ -608,6 +608,11 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ @echo ">>> Create helper links in XCframework for $(os)" $$(foreach sdk,$$(SDKS-$(os)),ln -si $$(SDK_SLICE-$$(sdk)) $$(PYTHON_XCFRAMEWORK-$(os))/$$(sdk); ) +ifeq ($(os),iOS) + @echo ">>> Clone testbed project for $(os)" + $(HOST_PYTHON) $$(PYTHON_SRCDIR-$$(firstword $$(SDK_TARGETS-$$(firstword $$(SDKS-$(os))))))/iOS/testbed clone --framework $$(PYTHON_XCFRAMEWORK-$(os)) support/$(PYTHON_VER)/$(os)/testbed +endif + @echo ">>> Create VERSIONS file for $(os)" echo "Python version: $(PYTHON_VERSION) " > support/$(PYTHON_VER)/$(os)/VERSIONS echo "Build: $(BUILD_NUMBER)" >> support/$(PYTHON_VER)/$(os)/VERSIONS From 5ca1c793a77e76b5cd5966feecf2598eab6d9a69 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 18 Dec 2024 07:11:07 +0800 Subject: [PATCH 063/113] Update build to use 3.14.0a3 --- Makefile | 2 +- patch/Python/Python.patch | 1359 ++----------------------------------- 2 files changed, 68 insertions(+), 1293 deletions(-) diff --git a/Makefile b/Makefile index 04b19588..720461ea 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,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.0a2 +PYTHON_VERSION=3.14.0a3 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+") diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 36d93a03..19768c8e 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1,202 +1,8 @@ -diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst -index 6194d7446c7..55a9dd1f25f 100644 ---- a/Doc/c-api/init_config.rst -+++ b/Doc/c-api/init_config.rst -@@ -1279,6 +1279,17 @@ - - Default: ``1`` in Python config and ``0`` in isolated config. - -+ .. c:member:: int use_system_logger -+ -+ If non-zero, ``stdout`` and ``stderr`` will be redirected to the system -+ log. -+ -+ Only available on macOS 10.12 and later, and on iOS. -+ -+ Default: ``0`` (don't use system log). -+ -+ .. versionadded:: 3.13.2 -+ - .. c:member:: int user_site_directory - - If non-zero, add the user site directory to :data:`sys.path`. -diff --git a/Doc/using/ios.rst b/Doc/using/ios.rst -index 4d4eb2031ee..aa43f75ec35 100644 ---- a/Doc/using/ios.rst -+++ b/Doc/using/ios.rst -@@ -292,10 +292,12 @@ - 10. Add Objective C code to initialize and use a Python interpreter in embedded - mode. You should ensure that: - -- * :c:member:`UTF-8 mode ` is *enabled*; -- * :c:member:`Buffered stdio ` is *disabled*; -- * :c:member:`Writing bytecode ` is *disabled*; -- * :c:member:`Signal handlers ` are *enabled*; -+ * UTF-8 mode (:c:member:`PyPreConfig.utf8_mode`) is *enabled*; -+ * Buffered stdio (:c:member:`PyConfig.buffered_stdio`) is *disabled*; -+ * Writing bytecode (:c:member:`PyConfig.write_bytecode`) is *disabled*; -+ * Signal handlers (:c:member:`PyConfig.install_signal_handlers`) are *enabled*; -+ * System logging (:c:member:`PyConfig.use_system_logger`) is *enabled* -+ (optional, but strongly recommended); - * ``PYTHONHOME`` for the interpreter is configured to point at the - ``python`` subfolder of your app's bundle; and - * The ``PYTHONPATH`` for the interpreter includes: -@@ -324,6 +326,49 @@ - * If you're using a separate folder for third-party packages, ensure that folder - is included as part of the ``PYTHONPATH`` configuration in step 10. - -+Testing a Python package -+------------------------ -+ -+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: -+ -+.. code-block:: bash -+ -+ $ python iOS/testbed clone --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). -+ -+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 -+could run: -+ -+.. code-block:: bash -+ -+ $ python app-testbed run -- module1.tests -+ -+This is the equivalent of running ``python -m module1.tests`` on a desktop -+Python build. Any arguments after the ``--`` will be passed to the testbed as -+if they were arguments to ``python -m`` on a desktop machine. -+ -+You can also open the testbed project in Xcode by running: -+ -+.. code-block:: bash -+ -+ $ open app-testbed/iOSTestbed.xcodeproj -+ -+This will allow you to use the full Xcode suite of tools for debugging. -+ - App Store Compliance - ==================== - -diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst -index 3eabf22e499..b509f2a0607 100644 ---- a/Doc/whatsnew/3.14.rst -+++ b/Doc/whatsnew/3.14.rst -@@ -205,6 +205,13 @@ - making it a :term:`generic type`. - (Contributed by Brian Schubert in :gh:`126012`.) - -+* iOS and macOS apps can now be configured to redirect ``stdout`` and -+ ``stderr`` content to the system log. (Contributed by Russell Keith-Magee in -+ :gh:`127592`.) -+ -+* The iOS testbed is now able to stream test output while the test is running. -+ The testbed can also be used to run the test suite of projects other than -+ CPython itself. (Contributed by Russell Keith-Magee in :gh:`127592`.) - - New modules - =========== -diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h -index f69c586a4f9..8ef19f67706 100644 ---- a/Include/cpython/initconfig.h -+++ b/Include/cpython/initconfig.h -@@ -179,6 +179,9 @@ - int use_frozen_modules; - int safe_path; - int int_max_str_digits; -+#ifdef __APPLE__ -+ int use_system_logger; -+#endif - - int cpu_count; - #ifdef Py_GIL_DISABLED ---- /dev/null -+++ b/Lib/_apple_support.py -@@ -0,0 +1,66 @@ -+import io -+import sys -+ -+ -+def init_streams(log_write, stdout_level, stderr_level): -+ # Redirect stdout and stderr to the Apple system log. This method is -+ # invoked by init_apple_streams() (initconfig.c) if config->use_system_logger -+ # is enabled. -+ sys.stdout = SystemLog(log_write, stdout_level, errors=sys.stderr.errors) -+ sys.stderr = SystemLog(log_write, stderr_level, errors=sys.stderr.errors) -+ -+ -+class SystemLog(io.TextIOWrapper): -+ def __init__(self, log_write, level, **kwargs): -+ kwargs.setdefault("encoding", "UTF-8") -+ kwargs.setdefault("line_buffering", True) -+ super().__init__(LogStream(log_write, level), **kwargs) -+ -+ def __repr__(self): -+ return f"" -+ -+ def write(self, s): -+ if not isinstance(s, str): -+ raise TypeError( -+ f"write() argument must be str, not {type(s).__name__}") -+ -+ # In case `s` is a str subclass that writes itself to stdout or stderr -+ # when we call its methods, convert it to an actual str. -+ s = str.__str__(s) -+ -+ # We want to emit one log message per line, so split -+ # the string before sending it to the superclass. -+ for line in s.splitlines(keepends=True): -+ super().write(line) -+ -+ return len(s) -+ -+ -+class LogStream(io.RawIOBase): -+ def __init__(self, log_write, level): -+ self.log_write = log_write -+ self.level = level -+ -+ def __repr__(self): -+ return f"" -+ -+ def writable(self): -+ return True -+ -+ def write(self, b): -+ if type(b) is not bytes: -+ try: -+ b = bytes(memoryview(b)) -+ except TypeError: -+ raise TypeError( -+ f"write() argument must be bytes-like, not {type(b).__name__}" -+ ) from None -+ -+ # Writing an empty string to the stream should have no effect. -+ if b: -+ # Encode null bytes using "modified UTF-8" to avoid truncating the -+ # message. This should not affect the return value, as the caller -+ # may be expecting it to match the length of the input. -+ self.log_write(self.level, b.replace(b"\x00", b"\xc0\x80")) -+ -+ return len(b) diff --git a/Lib/platform.py b/Lib/platform.py -index 239e660cd16..8e007c3c3b5 100644 +index 1f6baed66d3..235dd98c60a 100644 --- a/Lib/platform.py +++ b/Lib/platform.py -@@ -520,6 +520,54 @@ +@@ -521,6 +521,54 @@ return IOSVersionInfo(system, release, model, is_simulator) @@ -251,7 +57,7 @@ index 239e660cd16..8e007c3c3b5 100644 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 -@@ -883,14 +931,25 @@ +@@ -884,14 +932,25 @@ csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0) return 'Alpha' if cpu_number >= 128 else 'VAX' @@ -280,7 +86,7 @@ index 239e660cd16..8e007c3c3b5 100644 def from_subprocess(): """ Fall back to `uname -p` -@@ -1050,9 +1109,13 @@ +@@ -1051,9 +1110,13 @@ system = 'Android' release = android_ver().release @@ -295,7 +101,7 @@ index 239e660cd16..8e007c3c3b5 100644 vals = system, node, release, version, machine # Replace 'unknown' values with the more portable '' -@@ -1342,6 +1405,10 @@ +@@ -1343,6 +1406,10 @@ # macOS and iOS both report as a "Darwin" kernel if sys.platform == "ios": system, release, _, _ = ios_ver() @@ -307,10 +113,10 @@ index 239e660cd16..8e007c3c3b5 100644 macos_release = mac_ver()[0] if macos_release: diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py -index 67a071963d8..eefcac66cb5 100644 +index ed7b6a335d0..322e6cf1eee 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py -@@ -669,6 +669,14 @@ +@@ -690,6 +690,14 @@ release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0") osname = sys.platform machine = sys.implementation._multiarch @@ -325,238 +131,6 @@ index 67a071963d8..eefcac66cb5 100644 else: import _osx_support osname, release, machine = _osx_support.get_platform_osx( ---- /dev/null -+++ b/Lib/test/test_apple.py -@@ -0,0 +1,155 @@ -+import unittest -+from _apple_support import SystemLog -+from test.support import is_apple -+from unittest.mock import Mock, call -+ -+if not is_apple: -+ raise unittest.SkipTest("Apple-specific") -+ -+ -+# Test redirection of stdout and stderr to the Apple system log. -+class TestAppleSystemLogOutput(unittest.TestCase): -+ maxDiff = None -+ -+ def assert_writes(self, output): -+ self.assertEqual( -+ self.log_write.mock_calls, -+ [ -+ call(self.log_level, line) -+ for line in output -+ ] -+ ) -+ -+ self.log_write.reset_mock() -+ -+ def setUp(self): -+ self.log_write = Mock() -+ self.log_level = 42 -+ self.log = SystemLog(self.log_write, self.log_level, errors="replace") -+ -+ def test_repr(self): -+ self.assertEqual(repr(self.log), "") -+ self.assertEqual(repr(self.log.buffer), "") -+ -+ def test_log_config(self): -+ self.assertIs(self.log.writable(), True) -+ self.assertIs(self.log.readable(), False) -+ -+ self.assertEqual("UTF-8", self.log.encoding) -+ self.assertEqual("replace", self.log.errors) -+ -+ self.assertIs(self.log.line_buffering, True) -+ self.assertIs(self.log.write_through, False) -+ -+ def test_empty_str(self): -+ self.log.write("") -+ self.log.flush() -+ -+ self.assert_writes([]) -+ -+ def test_simple_str(self): -+ self.log.write("hello world\n") -+ -+ self.assert_writes([b"hello world\n"]) -+ -+ def test_buffered_str(self): -+ self.log.write("h") -+ self.log.write("ello") -+ self.log.write(" ") -+ self.log.write("world\n") -+ self.log.write("goodbye.") -+ self.log.flush() -+ -+ self.assert_writes([b"hello world\n", b"goodbye."]) -+ -+ def test_manual_flush(self): -+ self.log.write("Hello") -+ -+ self.assert_writes([]) -+ -+ self.log.write(" world\nHere for a while...\nGoodbye") -+ self.assert_writes([b"Hello world\n", b"Here for a while...\n"]) -+ -+ self.log.write(" world\nHello again") -+ self.assert_writes([b"Goodbye world\n"]) -+ -+ self.log.flush() -+ self.assert_writes([b"Hello again"]) -+ -+ def test_non_ascii(self): -+ # Spanish -+ self.log.write("ol\u00e9\n") -+ self.assert_writes([b"ol\xc3\xa9\n"]) -+ -+ # Chinese -+ self.log.write("\u4e2d\u6587\n") -+ self.assert_writes([b"\xe4\xb8\xad\xe6\x96\x87\n"]) -+ -+ # Printing Non-BMP emoji -+ self.log.write("\U0001f600\n") -+ self.assert_writes([b"\xf0\x9f\x98\x80\n"]) -+ -+ # Non-encodable surrogates are replaced -+ self.log.write("\ud800\udc00\n") -+ self.assert_writes([b"??\n"]) -+ -+ def test_modified_null(self): -+ # Null characters are logged using "modified UTF-8". -+ self.log.write("\u0000\n") -+ self.assert_writes([b"\xc0\x80\n"]) -+ self.log.write("a\u0000\n") -+ self.assert_writes([b"a\xc0\x80\n"]) -+ self.log.write("\u0000b\n") -+ self.assert_writes([b"\xc0\x80b\n"]) -+ self.log.write("a\u0000b\n") -+ self.assert_writes([b"a\xc0\x80b\n"]) -+ -+ def test_nonstandard_str(self): -+ # String subclasses are accepted, but they should be converted -+ # to a standard str without calling any of their methods. -+ class CustomStr(str): -+ def splitlines(self, *args, **kwargs): -+ raise AssertionError() -+ -+ def __len__(self): -+ raise AssertionError() -+ -+ def __str__(self): -+ raise AssertionError() -+ -+ self.log.write(CustomStr("custom\n")) -+ self.assert_writes([b"custom\n"]) -+ -+ def test_non_str(self): -+ # Non-string classes are not accepted. -+ for obj in [b"", b"hello", None, 42]: -+ with self.subTest(obj=obj): -+ with self.assertRaisesRegex( -+ TypeError, -+ fr"write\(\) argument must be str, not " -+ fr"{type(obj).__name__}" -+ ): -+ self.log.write(obj) -+ -+ def test_byteslike_in_buffer(self): -+ # The underlying buffer *can* accept bytes-like objects -+ self.log.buffer.write(bytearray(b"hello")) -+ self.log.flush() -+ -+ self.log.buffer.write(b"") -+ self.log.flush() -+ -+ self.log.buffer.write(b"goodbye") -+ self.log.flush() -+ -+ self.assert_writes([b"hello", b"goodbye"]) -+ -+ def test_non_byteslike_in_buffer(self): -+ for obj in ["hello", None, 42]: -+ with self.subTest(obj=obj): -+ with self.assertRaisesRegex( -+ TypeError, -+ fr"write\(\) argument must be bytes-like, not " -+ fr"{type(obj).__name__}" -+ ): -+ self.log.buffer.write(obj) -diff --git a/Lib/test/test_capi/test_config.py b/Lib/test/test_capi/test_config.py -index 77730ad2f32..a3179efe4a8 100644 ---- a/Lib/test/test_capi/test_config.py -+++ b/Lib/test/test_capi/test_config.py -@@ -110,6 +110,10 @@ - options.extend(( - ("_pystats", bool, None), - )) -+ if support.is_apple: -+ options.extend(( -+ ("use_system_logger", bool, None), -+ )) - - for name, option_type, sys_attr in options: - with self.subTest(name=name, option_type=option_type, -diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py -index bf861ef06ee..468f821370e 100644 ---- a/Lib/test/test_embed.py -+++ b/Lib/test/test_embed.py -@@ -649,6 +649,8 @@ - CONFIG_COMPAT.update({ - 'legacy_windows_stdio': False, - }) -+ if support.is_apple: -+ CONFIG_COMPAT['use_system_logger'] = False - - CONFIG_PYTHON = dict(CONFIG_COMPAT, - _config_init=API_PYTHON, -diff --git a/Makefile.pre.in b/Makefile.pre.in -index 8d94ba361fd..6c1c95d4dd9 100644 ---- a/Makefile.pre.in -+++ b/Makefile.pre.in -@@ -2132,7 +2132,6 @@ - # 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) --XCRESULT=$(XCFOLDER)/$(MULTIARCH).xcresult - .PHONY: testios - testios: - @if test "$(MACHDEP)" != "ios"; then \ -@@ -2151,29 +2150,12 @@ - echo "Cannot find a finalized iOS Python.framework. Have you run 'make install' to finalize the framework build?"; \ - exit 1;\ - fi -- # Copy the testbed project into the build folder -- cp -r $(srcdir)/iOS/testbed $(XCFOLDER) -- # Copy the framework from the install location to the testbed project. -- cp -r $(PYTHONFRAMEWORKPREFIX)/* $(XCFOLDER)/Python.xcframework/ios-arm64_x86_64-simulator -- -- # Run the test suite for the Xcode project, targeting the iOS simulator. -- # If the suite fails, touch a file in the test folder as a marker -- if ! xcodebuild test -project $(XCFOLDER)/iOSTestbed.xcodeproj -scheme "iOSTestbed" -destination "platform=iOS Simulator,name=iPhone SE (3rd Generation)" -resultBundlePath $(XCRESULT) -derivedDataPath $(XCFOLDER)/DerivedData ; then \ -- touch $(XCFOLDER)/failed; \ -- fi - -- # Regardless of success or failure, extract and print the test output -- xcrun xcresulttool get --path $(XCRESULT) \ -- --id $$( \ -- xcrun xcresulttool get --path $(XCRESULT) --format json | \ -- $(PYTHON_FOR_BUILD) -c "import sys, json; result = json.load(sys.stdin); print(result['actions']['_values'][0]['actionResult']['logRef']['id']['_value'])" \ -- ) \ -- --format json | \ -- $(PYTHON_FOR_BUILD) -c "import sys, json; result = json.load(sys.stdin); print(result['subsections']['_values'][1]['subsections']['_values'][0]['emittedOutput']['_value'])" -+ # Clone the testbed project into the XCFOLDER -+ $(PYTHON_FOR_BUILD) $(srcdir)/iOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER)" - -- @if test -e $(XCFOLDER)/failed ; then \ -- exit 1; \ -- fi -+ # Run the testbed project -+ $(PYTHON_FOR_BUILD) "$(XCFOLDER)" 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 ec0857a4a99..2350e9dc821 100644 --- a/Misc/platform_triplet.c @@ -588,192 +162,11 @@ index ec0857a4a99..2350e9dc821 100644 // Older macOS SDKs do not define TARGET_OS_OSX # elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX PLATFORM_TRIPLET=darwin -diff --git a/Python/initconfig.c b/Python/initconfig.c -index 438f8a5c1cf..7851b86db1f 100644 ---- a/Python/initconfig.c -+++ b/Python/initconfig.c -@@ -168,6 +168,9 @@ - SPEC(tracemalloc, UINT, READ_ONLY, NO_SYS), - SPEC(use_frozen_modules, BOOL, READ_ONLY, NO_SYS), - SPEC(use_hash_seed, BOOL, READ_ONLY, NO_SYS), -+#ifdef __APPLE__ -+ SPEC(use_system_logger, BOOL, PUBLIC, NO_SYS), -+#endif - SPEC(user_site_directory, BOOL, READ_ONLY, NO_SYS), // sys.flags.no_user_site - SPEC(warn_default_encoding, BOOL, READ_ONLY, NO_SYS), - -@@ -884,6 +887,9 @@ - assert(config->cpu_count != 0); - // config->use_frozen_modules is initialized later - // by _PyConfig_InitImportConfig(). -+#ifdef __APPLE__ -+ assert(config->use_system_logger >= 0); -+#endif - #ifdef Py_STATS - assert(config->_pystats >= 0); - #endif -@@ -986,6 +992,9 @@ - config->_is_python_build = 0; - config->code_debug_ranges = 1; - config->cpu_count = -1; -+#ifdef __APPLE__ -+ config->use_system_logger = 0; -+#endif - #ifdef Py_GIL_DISABLED - config->enable_gil = _PyConfig_GIL_DEFAULT; - config->tlbc_enabled = 1; -@@ -1015,6 +1024,9 @@ - #ifdef MS_WINDOWS - config->legacy_windows_stdio = 0; - #endif -+#ifdef __APPLE__ -+ config->use_system_logger = 0; -+#endif - } - - -@@ -1049,6 +1061,9 @@ - #ifdef MS_WINDOWS - config->legacy_windows_stdio = 0; - #endif -+#ifdef __APPLE__ -+ config->use_system_logger = 0; -+#endif - } - - -diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c -index 23882d08384..64985527606 100644 ---- a/Python/pylifecycle.c -+++ b/Python/pylifecycle.c -@@ -45,7 +45,9 @@ - #endif - - #if defined(__APPLE__) -+# include - # include -+# include - #endif - - #ifdef HAVE_SIGNAL_H -@@ -75,6 +77,9 @@ - #ifdef __ANDROID__ - static PyStatus init_android_streams(PyThreadState *tstate); - #endif -+#if defined(__APPLE__) -+static PyStatus init_apple_streams(PyThreadState *tstate); -+#endif - static void wait_for_thread_shutdown(PyThreadState *tstate); - static void finalize_subinterpreters(void); - static void call_ll_exitfuncs(_PyRuntimeState *runtime); -@@ -1257,6 +1262,14 @@ - return status; - } - #endif -+#if defined(__APPLE__) -+ if (config->use_system_logger) { -+ status = init_apple_streams(tstate); -+ if (_PyStatus_EXCEPTION(status)) { -+ return status; -+ } -+ } -+#endif - - #ifdef Py_DEBUG - run_presite(tstate); -@@ -2931,6 +2944,75 @@ - - #endif // __ANDROID__ - -+#if defined(__APPLE__) -+ -+static PyObject * -+apple_log_write_impl(PyObject *self, PyObject *args) -+{ -+ int logtype = 0; -+ const char *text = NULL; -+ if (!PyArg_ParseTuple(args, "iy", &logtype, &text)) { -+ return NULL; -+ } -+ -+ // Call the underlying Apple logging API. The os_log unified logging APIs -+ // were introduced in macOS 10.12, iOS 10.0, tvOS 10.0, and watchOS 3.0; -+ // this call is a no-op on older versions. -+ #if TARGET_OS_IPHONE || (TARGET_OS_OSX && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) -+ // Pass the user-provided text through explicit %s formatting -+ // to avoid % literals being interpreted as a formatting directive. -+ os_log_with_type(OS_LOG_DEFAULT, logtype, "%s", text); -+ #endif -+ Py_RETURN_NONE; -+} -+ -+ -+static PyMethodDef apple_log_write_method = { -+ "apple_log_write", apple_log_write_impl, METH_VARARGS -+}; -+ -+ -+static PyStatus -+init_apple_streams(PyThreadState *tstate) -+{ -+ PyStatus status = _PyStatus_OK(); -+ PyObject *_apple_support = NULL; -+ PyObject *apple_log_write = NULL; -+ PyObject *result = NULL; -+ -+ _apple_support = PyImport_ImportModule("_apple_support"); -+ if (_apple_support == NULL) { -+ goto error; -+ } -+ -+ apple_log_write = PyCFunction_New(&apple_log_write_method, NULL); -+ if (apple_log_write == NULL) { -+ goto error; -+ } -+ -+ // Initialize the logging streams, sending stdout -> Default; stderr -> Error -+ result = PyObject_CallMethod( -+ _apple_support, "init_streams", "Oii", -+ apple_log_write, OS_LOG_TYPE_DEFAULT, OS_LOG_TYPE_ERROR); -+ if (result == NULL) { -+ goto error; -+ } -+ -+ goto done; -+ -+error: -+ _PyErr_Print(tstate); -+ status = _PyStatus_ERR("failed to initialize Apple log streams"); -+ -+done: -+ Py_XDECREF(result); -+ Py_XDECREF(apple_log_write); -+ Py_XDECREF(_apple_support); -+ return status; -+} -+ -+#endif // __APPLE__ -+ - - static void - _Py_FatalError_DumpTracebacks(int fd, PyInterpreterState *interp, -diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h -index c8cdb933bb1..584b050fc4b 100644 ---- a/Python/stdlib_module_names.h -+++ b/Python/stdlib_module_names.h -@@ -6,6 +6,7 @@ - "_abc", - "_aix_support", - "_android_support", -+"_apple_support", - "_ast", - "_asyncio", - "_bisect", diff --git a/configure b/configure -index 5b44a3d6992..83803f12853 100755 +index 57be576e3ca..6d4ef3d0e01 100755 --- a/configure +++ b/configure -@@ -979,6 +979,8 @@ +@@ -980,6 +980,8 @@ CFLAGS CC HAS_XCRUN @@ -782,7 +175,7 @@ index 5b44a3d6992..83803f12853 100755 IPHONEOS_DEPLOYMENT_TARGET EXPORT_MACOSX_DEPLOYMENT_TARGET CONFIGURE_MACOSX_DEPLOYMENT_TARGET -@@ -4053,6 +4055,12 @@ +@@ -4052,6 +4054,12 @@ *-apple-ios*) ac_sys_system=iOS ;; @@ -795,7 +188,7 @@ index 5b44a3d6992..83803f12853 100755 *-*-vxworks*) ac_sys_system=VxWorks ;; -@@ -4130,7 +4138,7 @@ +@@ -4129,7 +4137,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. # @@ -804,7 +197,7 @@ index 5b44a3d6992..83803f12853 100755 # 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 -@@ -4145,6 +4153,14 @@ +@@ -4144,6 +4152,14 @@ 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 ;; @@ -819,7 +212,7 @@ index 5b44a3d6992..83803f12853 100755 *) esac fi -@@ -4153,6 +4169,14 @@ +@@ -4152,6 +4168,14 @@ 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 ;; @@ -834,7 +227,7 @@ index 5b44a3d6992..83803f12853 100755 *) esac fi -@@ -4161,6 +4185,14 @@ +@@ -4160,6 +4184,14 @@ 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 ;; @@ -849,7 +242,7 @@ index 5b44a3d6992..83803f12853 100755 *) esac fi -@@ -4169,6 +4201,14 @@ +@@ -4168,6 +4200,14 @@ 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++ ;; @@ -864,7 +257,7 @@ index 5b44a3d6992..83803f12853 100755 *) esac fi -@@ -4289,8 +4329,10 @@ +@@ -4288,8 +4328,10 @@ case $enableval in yes) case $ac_sys_system in @@ -877,7 +270,7 @@ index 5b44a3d6992..83803f12853 100755 *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 esac esac -@@ -4299,6 +4341,8 @@ +@@ -4298,6 +4340,8 @@ no) case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -886,7 +279,7 @@ index 5b44a3d6992..83803f12853 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4405,6 +4449,36 @@ +@@ -4404,6 +4448,36 @@ ac_config_files="$ac_config_files iOS/Resources/Info.plist" @@ -923,7 +316,7 @@ index 5b44a3d6992..83803f12853 100755 ;; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 -@@ -4416,6 +4490,8 @@ +@@ -4415,6 +4489,8 @@ case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -932,7 +325,7 @@ index 5b44a3d6992..83803f12853 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4469,8 +4545,8 @@ +@@ -4468,8 +4544,8 @@ case "$withval" in yes) case $ac_sys_system in @@ -943,7 +336,7 @@ index 5b44a3d6992..83803f12853 100755 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 ;; -@@ -4488,8 +4564,8 @@ +@@ -4487,8 +4563,8 @@ else $as_nop case $ac_sys_system in @@ -954,7 +347,7 @@ index 5b44a3d6992..83803f12853 100755 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; } -@@ -4543,6 +4619,50 @@ +@@ -4542,6 +4618,50 @@ ;; esac ;; @@ -1005,7 +398,7 @@ index 5b44a3d6992..83803f12853 100755 *-*-vxworks*) _host_ident=$host_cpu ;; -@@ -4621,9 +4741,13 @@ +@@ -4620,9 +4740,13 @@ define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; @@ -1020,7 +413,7 @@ index 5b44a3d6992..83803f12853 100755 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -4686,7 +4810,10 @@ +@@ -4685,7 +4809,10 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' @@ -1032,7 +425,7 @@ index 5b44a3d6992..83803f12853 100755 # checks for alternative programs -@@ -4727,6 +4854,16 @@ +@@ -4726,6 +4853,16 @@ as_fn_append CFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" as_fn_append LDFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" ;; #( @@ -1049,7 +442,7 @@ index 5b44a3d6992..83803f12853 100755 *) : ;; esac -@@ -7031,6 +7168,10 @@ +@@ -7030,6 +7167,10 @@ MULTIARCH="" ;; #( iOS) : MULTIARCH="" ;; #( @@ -1060,7 +453,7 @@ index 5b44a3d6992..83803f12853 100755 FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -7051,7 +7192,7 @@ +@@ -7050,7 +7191,7 @@ printf "%s\n" "$MULTIARCH" >&6; } case $ac_sys_system in #( @@ -1069,7 +462,7 @@ index 5b44a3d6992..83803f12853 100755 SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2` ;; #( *) : SOABI_PLATFORM=$PLATFORM_TRIPLET -@@ -7102,6 +7243,14 @@ +@@ -7101,6 +7242,14 @@ PY_SUPPORT_TIER=3 ;; #( aarch64-apple-ios*/clang) : PY_SUPPORT_TIER=3 ;; #( @@ -1084,7 +477,7 @@ index 5b44a3d6992..83803f12853 100755 aarch64-*-linux-android/clang) : PY_SUPPORT_TIER=3 ;; #( x86_64-*-linux-android/clang) : -@@ -7531,7 +7680,7 @@ +@@ -7530,7 +7679,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; @@ -1093,7 +486,7 @@ index 5b44a3d6992..83803f12853 100755 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5;; -@@ -7597,7 +7746,7 @@ +@@ -7596,7 +7745,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -1102,7 +495,7 @@ index 5b44a3d6992..83803f12853 100755 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -13150,7 +13299,7 @@ +@@ -13160,7 +13309,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -1111,7 +504,7 @@ index 5b44a3d6992..83803f12853 100755 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -13283,7 +13432,7 @@ +@@ -13293,7 +13442,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -1120,7 +513,7 @@ index 5b44a3d6992..83803f12853 100755 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -13307,7 +13456,7 @@ +@@ -13317,7 +13466,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -1129,7 +522,7 @@ index 5b44a3d6992..83803f12853 100755 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -14759,7 +14908,7 @@ +@@ -14769,7 +14918,7 @@ ctypes_malloc_closure=yes ;; #( @@ -1138,7 +531,7 @@ index 5b44a3d6992..83803f12853 100755 ctypes_malloc_closure=yes ;; #( -@@ -18262,12 +18411,6 @@ +@@ -18272,12 +18421,6 @@ then : printf "%s\n" "#define HAVE_DUP3 1" >>confdefs.h @@ -1151,7 +544,7 @@ index 5b44a3d6992..83803f12853 100755 fi ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes -@@ -18328,18 +18471,6 @@ +@@ -18338,18 +18481,6 @@ then : printf "%s\n" "#define HAVE_FEXECVE 1" >>confdefs.h @@ -1170,7 +563,7 @@ index 5b44a3d6992..83803f12853 100755 fi ac_fn_c_check_func "$LINENO" "fpathconf" "ac_cv_func_fpathconf" if test "x$ac_cv_func_fpathconf" = xyes -@@ -18766,24 +18897,6 @@ +@@ -18776,24 +18907,6 @@ then : printf "%s\n" "#define HAVE_POSIX_OPENPT 1" >>confdefs.h @@ -1195,7 +588,7 @@ index 5b44a3d6992..83803f12853 100755 fi ac_fn_c_check_func "$LINENO" "pread" "ac_cv_func_pread" if test "x$ac_cv_func_pread" = xyes -@@ -19072,12 +19185,6 @@ +@@ -19094,12 +19207,6 @@ then : printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h @@ -1208,7 +601,7 @@ index 5b44a3d6992..83803f12853 100755 fi ac_fn_c_check_func "$LINENO" "sigfillset" "ac_cv_func_sigfillset" if test "x$ac_cv_func_sigfillset" = xyes -@@ -19346,11 +19453,11 @@ +@@ -19368,11 +19475,11 @@ fi @@ -1222,7 +615,7 @@ index 5b44a3d6992..83803f12853 100755 ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" if test "x$ac_cv_func_getentropy" = xyes then : -@@ -19372,6 +19479,53 @@ +@@ -19394,6 +19501,53 @@ fi @@ -1276,7 +669,7 @@ index 5b44a3d6992..83803f12853 100755 { 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} -@@ -22247,7 +22401,8 @@ +@@ -22269,7 +22423,8 @@ # check for openpty, login_tty, and forkpty @@ -1286,7 +679,7 @@ index 5b44a3d6992..83803f12853 100755 for ac_func in openpty do : -@@ -22343,7 +22498,7 @@ +@@ -22365,7 +22520,7 @@ fi done @@ -1295,7 +688,7 @@ index 5b44a3d6992..83803f12853 100755 printf %s "checking for library containing login_tty... " >&6; } if test ${ac_cv_search_login_tty+y} then : -@@ -22500,6 +22655,7 @@ +@@ -22522,6 +22677,7 @@ fi done @@ -1303,7 +696,7 @@ index 5b44a3d6992..83803f12853 100755 # check for long file support functions ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" -@@ -22746,10 +22902,10 @@ +@@ -22768,10 +22924,10 @@ done @@ -1316,7 +709,7 @@ index 5b44a3d6992..83803f12853 100755 then for ac_func in clock_settime -@@ -24977,8 +25133,8 @@ +@@ -24999,8 +25155,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -1327,7 +720,7 @@ index 5b44a3d6992..83803f12853 100755 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -27730,7 +27886,7 @@ +@@ -27752,7 +27908,7 @@ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for device files" >&5 printf "%s\n" "$as_me: checking for device files" >&6;} @@ -1336,7 +729,7 @@ index 5b44a3d6992..83803f12853 100755 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -28162,7 +28318,7 @@ +@@ -28184,7 +28340,7 @@ with_ensurepip=no ;; #( WASI) : with_ensurepip=no ;; #( @@ -1345,7 +738,7 @@ index 5b44a3d6992..83803f12853 100755 with_ensurepip=no ;; #( *) : with_ensurepip=upgrade -@@ -29091,7 +29247,7 @@ +@@ -29130,7 +29286,7 @@ ;; #( Darwin) : ;; #( @@ -1354,7 +747,7 @@ index 5b44a3d6992..83803f12853 100755 -@@ -32989,6 +33145,8 @@ +@@ -33028,6 +33184,8 @@ "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" ;; @@ -1364,7 +757,7 @@ index 5b44a3d6992..83803f12853 100755 "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 7904f8990c4..f2367cd47d3 100644 +index bd0221481c5..6dca265f3cc 100644 --- a/configure.ac +++ b/configure.ac @@ -330,6 +330,12 @@ @@ -1678,7 +1071,7 @@ index 7904f8990c4..f2367cd47d3 100644 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -3407,7 +3538,7 @@ +@@ -3412,7 +3543,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -1687,7 +1080,7 @@ index 7904f8990c4..f2367cd47d3 100644 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -3531,7 +3662,7 @@ +@@ -3536,7 +3667,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -1696,7 +1089,7 @@ index 7904f8990c4..f2367cd47d3 100644 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -3555,7 +3686,7 @@ +@@ -3560,7 +3691,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -1705,7 +1098,7 @@ index 7904f8990c4..f2367cd47d3 100644 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -3975,7 +4106,7 @@ +@@ -3980,7 +4111,7 @@ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], @@ -1714,7 +1107,7 @@ index 7904f8990c4..f2367cd47d3 100644 ctypes_malloc_closure=yes ], [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] -@@ -5093,9 +5224,9 @@ +@@ -5098,9 +5229,9 @@ # checks for library functions AC_CHECK_FUNCS([ \ accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ @@ -1726,16 +1119,17 @@ index 7904f8990c4..f2367cd47d3 100644 gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \ getgrnam_r getgrouplist gethostname getitimer getloadavg getlogin \ getpeername getpgid getpid getppid getpriority _getpty \ -@@ -5103,15 +5234,14 @@ +@@ -5108,8 +5239,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 ptsname ptsname_r pwrite pwritev pwritev2 readlink readlinkat readv realpath renameat \ - rtpSpawn sched_get_priority_max sched_rr_get_interval sched_setaffinity \ + pread preadv preadv2 process_vm_readv \ + pthread_cond_timedwait_relative_np pthread_condattr_setclock pthread_init \ + pthread_kill pthread_getname_np pthread_setname_np \ +@@ -5118,7 +5248,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 \ @@ -1744,7 +1138,7 @@ index 7904f8990c4..f2367cd47d3 100644 sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ -@@ -5126,12 +5256,20 @@ +@@ -5133,12 +5263,20 @@ AC_CHECK_FUNCS([lchmod]) fi @@ -1768,7 +1162,7 @@ index 7904f8990c4..f2367cd47d3 100644 fi AC_CHECK_DECL([dirfd], -@@ -5385,20 +5523,22 @@ +@@ -5392,20 +5530,22 @@ ]) # check for openpty, login_tty, and forkpty @@ -1805,7 +1199,7 @@ index 7904f8990c4..f2367cd47d3 100644 # check for long file support functions AC_CHECK_FUNCS([fseek64 fseeko fstatvfs ftell64 ftello statvfs]) -@@ -5437,10 +5577,10 @@ +@@ -5444,10 +5584,10 @@ ]) ]) @@ -1818,7 +1212,7 @@ index 7904f8990c4..f2367cd47d3 100644 then AC_CHECK_FUNCS([clock_settime], [], [ AC_CHECK_LIB([rt], [clock_settime], [ -@@ -6191,8 +6331,8 @@ +@@ -6198,8 +6338,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -1829,7 +1223,7 @@ index 7904f8990c4..f2367cd47d3 100644 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -6856,7 +6996,7 @@ +@@ -6863,7 +7003,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. @@ -1838,7 +1232,7 @@ index 7904f8990c4..f2367cd47d3 100644 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -7112,7 +7252,7 @@ +@@ -7119,7 +7259,7 @@ AS_CASE([$ac_sys_system], [Emscripten], [with_ensurepip=no], [WASI], [with_ensurepip=no], @@ -1847,7 +1241,7 @@ index 7904f8990c4..f2367cd47d3 100644 [with_ensurepip=upgrade] ) ]) -@@ -7506,7 +7646,7 @@ +@@ -7529,7 +7669,7 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], @@ -1856,85 +1250,6 @@ index 7904f8990c4..f2367cd47d3 100644 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/README.rst b/iOS/README.rst -index e33455eef8f..13b88514493 100644 ---- a/iOS/README.rst -+++ b/iOS/README.rst -@@ -285,52 +285,42 @@ - * Install the Python iOS framework into the copy of the testbed project; and - * Run the test suite on an "iPhone SE (3rd generation)" simulator. - --While the test suite is running, Xcode does not display any console output. --After showing some Xcode build commands, the console output will print ``Testing --started``, and then appear to stop. It will remain in this state until the test --suite completes. On a 2022 M1 MacBook Pro, the test suite takes approximately 12 --minutes to run; a couple of extra minutes is required to boot and prepare the --iOS simulator. -- - On success, the test suite will exit and report successful completion of the --test suite. No output of the Python test suite will be displayed. -- --On failure, the output of the Python test suite *will* be displayed. This will --show the details of the tests that failed. -+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 - ----------------------- - --The easiest way to diagnose a single test failure is to open the testbed project --in Xcode and run the tests from there using the "Product > Test" menu item. -- --To test in Xcode, you must ensure the testbed project has a copy of a compiled --framework. If you've configured your build with the default install location of --``iOS/Frameworks``, you can copy from that location into the test project. To --test on an ARM64 simulator, run:: -- -- $ rm -rf iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator/* -- $ cp -r iOS/Frameworks/arm64-iphonesimulator/* iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator -+Running ``make test`` generates a standalone version of the ``iOS/testbed`` -+project, and runs the full test suite. It does this using ``iOS/testbed`` -+itself - the folder is an executable module that can be used to create and run -+a clone of the testbed project. - --To test on an x86-64 simulator, run:: -+You can generate your own standalone testbed instance by running:: - -- $ rm -rf iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator/* -- $ cp -r iOS/Frameworks/x86_64-iphonesimulator/* iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator -+ $ python iOS/testbed clone --framework iOS/Frameworks/arm64-iphonesimulator my-testbed - --To test on a physical device:: -+This invocation assumes that ``iOS/Frameworks/arm64-iphonesimulator`` is the -+path to the iOS simulator framework for your platform (ARM64 in this case); -+``my-testbed`` is the name of the folder for the new testbed clone. - -- $ rm -rf iOS/testbed/Python.xcframework/ios-arm64/* -- $ cp -r iOS/Frameworks/arm64-iphoneos/* iOS/testbed/Python.xcframework/ios-arm64 -+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:: - --Alternatively, you can configure your build to install directly into the --testbed project. For a simulator, use:: -+ $ python my-testbed run -- test -W test_os - -- --enable-framework=$(pwd)/iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator -+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. - --For a physical device, use:: -+You can also open the testbed project in Xcode by running:: - -- --enable-framework=$(pwd)/iOS/testbed/Python.xcframework/ios-arm64 -+ $ open my-testbed/iOSTestbed.xcodeproj - -+This will allow you to use the full Xcode suite of tools for debugging. - - Testing on an iOS device - ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/iOS/Resources/Info.plist.in b/iOS/Resources/Info.plist.in index c3e261ecd9e..26ef7a95de4 100644 --- a/iOS/Resources/Info.plist.in @@ -1955,546 +1270,6 @@ index c3e261ecd9e..26ef7a95de4 100644 CFBundleSupportedPlatforms iPhoneOS -diff --git a/iOS/Resources/bin/arm64-apple-ios-ar b/iOS/Resources/bin/arm64-apple-ios-ar -index 8122332b9c1..3cf3eb21874 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-ar -+++ b/iOS/Resources/bin/arm64-apple-ios-ar -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphoneos${IOS_SDK_VERSION} ar $@ -+xcrun --sdk iphoneos${IOS_SDK_VERSION} ar "$@" -diff --git a/iOS/Resources/bin/arm64-apple-ios-clang b/iOS/Resources/bin/arm64-apple-ios-clang -index 4d525751eba..c39519cd1f8 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-clang -+++ b/iOS/Resources/bin/arm64-apple-ios-clang -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphoneos${IOS_SDK_VERSION} clang -target arm64-apple-ios $@ -+xcrun --sdk iphoneos${IOS_SDK_VERSION} clang -target arm64-apple-ios "$@" -diff --git a/iOS/Resources/bin/arm64-apple-ios-clang++ b/iOS/Resources/bin/arm64-apple-ios-clang++ -index f24bec11268..d9b12925f38 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-clang++ -+++ b/iOS/Resources/bin/arm64-apple-ios-clang++ -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphoneos${IOS_SDK_VERSION} clang++ -target arm64-apple-ios $@ -+xcrun --sdk iphoneos${IOS_SDK_VERSION} clang++ -target arm64-apple-ios "$@" -diff --git a/iOS/Resources/bin/arm64-apple-ios-cpp b/iOS/Resources/bin/arm64-apple-ios-cpp -index 891bb25bb43..24da23d3448 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-cpp -+++ b/iOS/Resources/bin/arm64-apple-ios-cpp -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphoneos${IOS_SDK_VERSION} clang -target arm64-apple-ios -E $@ -+xcrun --sdk iphoneos${IOS_SDK_VERSION} clang -target arm64-apple-ios -E "$@" -diff --git a/iOS/Resources/bin/arm64-apple-ios-simulator-ar b/iOS/Resources/bin/arm64-apple-ios-simulator-ar -index 74ed3bc6df1..b836b6db902 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-simulator-ar -+++ b/iOS/Resources/bin/arm64-apple-ios-simulator-ar -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} ar $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} ar "$@" -diff --git a/iOS/Resources/bin/arm64-apple-ios-simulator-clang b/iOS/Resources/bin/arm64-apple-ios-simulator-clang -index 32574cad284..92e8d853d6e 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-simulator-clang -+++ b/iOS/Resources/bin/arm64-apple-ios-simulator-clang -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target arm64-apple-ios-simulator $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target arm64-apple-ios-simulator "$@" -diff --git a/iOS/Resources/bin/arm64-apple-ios-simulator-clang++ b/iOS/Resources/bin/arm64-apple-ios-simulator-clang++ -index ef37d05b512..076469cc70c 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-simulator-clang++ -+++ b/iOS/Resources/bin/arm64-apple-ios-simulator-clang++ -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang++ -target arm64-apple-ios-simulator $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang++ -target arm64-apple-ios-simulator "$@" -diff --git a/iOS/Resources/bin/arm64-apple-ios-simulator-cpp b/iOS/Resources/bin/arm64-apple-ios-simulator-cpp -index 6aaf6fbe188..c57f28cee5b 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-simulator-cpp -+++ b/iOS/Resources/bin/arm64-apple-ios-simulator-cpp -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target arm64-apple-ios-simulator -E $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target arm64-apple-ios-simulator -E "$@" -diff --git a/iOS/Resources/bin/x86_64-apple-ios-simulator-ar b/iOS/Resources/bin/x86_64-apple-ios-simulator-ar -index 74ed3bc6df1..b836b6db902 100755 ---- a/iOS/Resources/bin/x86_64-apple-ios-simulator-ar -+++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-ar -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} ar $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} ar "$@" -diff --git a/iOS/Resources/bin/x86_64-apple-ios-simulator-clang b/iOS/Resources/bin/x86_64-apple-ios-simulator-clang -index bcbe91f6061..17cbe0c8a1e 100755 ---- a/iOS/Resources/bin/x86_64-apple-ios-simulator-clang -+++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-clang -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target x86_64-apple-ios-simulator $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target x86_64-apple-ios-simulator "$@" -diff --git a/iOS/Resources/bin/x86_64-apple-ios-simulator-clang++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-clang++ -index 86f03ea32bc..565d47b24c2 100755 ---- a/iOS/Resources/bin/x86_64-apple-ios-simulator-clang++ -+++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-clang++ -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang++ -target x86_64-apple-ios-simulator $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang++ -target x86_64-apple-ios-simulator "$@" -diff --git a/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp b/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp -index e6a42d9b85d..63fc8e8de2d 100755 ---- a/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp -+++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target x86_64-apple-ios-simulator -E $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target x86_64-apple-ios-simulator -E "$@" ---- /dev/null -+++ b/iOS/testbed/__main__.py -@@ -0,0 +1,395 @@ -+import argparse -+import asyncio -+import json -+import plistlib -+import shutil -+import subprocess -+import sys -+from contextlib import asynccontextmanager -+from datetime import datetime -+from pathlib import Path -+ -+ -+DECODE_ARGS = ("UTF-8", "backslashreplace") -+ -+ -+# Work around a bug involving sys.exit and TaskGroups -+# (https://github.com/python/cpython/issues/101515). -+def exit(*args): -+ raise MySystemExit(*args) -+ -+ -+class MySystemExit(Exception): -+ pass -+ -+ -+# All subprocesses are executed through this context manager so that no matter -+# what happens, they can always be cancelled from another task, and they will -+# always be cleaned up on exit. -+@asynccontextmanager -+async def async_process(*args, **kwargs): -+ process = await asyncio.create_subprocess_exec(*args, **kwargs) -+ try: -+ yield process -+ finally: -+ if process.returncode is None: -+ # Allow a reasonably long time for Xcode to clean itself up, -+ # because we don't want stale emulators left behind. -+ timeout = 10 -+ process.terminate() -+ try: -+ await asyncio.wait_for(process.wait(), timeout) -+ except TimeoutError: -+ print( -+ f"Command {args} did not terminate after {timeout} seconds " -+ f" - sending SIGKILL" -+ ) -+ process.kill() -+ -+ # Even after killing the process we must still wait for it, -+ # otherwise we'll get the warning "Exception ignored in __del__". -+ await asyncio.wait_for(process.wait(), timeout=1) -+ -+ -+async def async_check_output(*args, **kwargs): -+ async with async_process( -+ *args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs -+ ) as process: -+ stdout, stderr = await process.communicate() -+ if process.returncode == 0: -+ return stdout.decode(*DECODE_ARGS) -+ else: -+ raise subprocess.CalledProcessError( -+ process.returncode, -+ args, -+ stdout.decode(*DECODE_ARGS), -+ stderr.decode(*DECODE_ARGS), -+ ) -+ -+ -+# Return a list of UDIDs associated with booted simulators -+async def list_devices(): -+ # 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 iOS simulators -+ return [ -+ simulator["udid"] -+ for runtime, simulators in json_data["devices"].items() -+ for simulator in simulators -+ if runtime.split(".")[-1].startswith("iOS") and simulator["state"] == "Booted" -+ ] -+ -+ -+async def find_device(initial_devices): -+ 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}") -+ return udid -+ else: -+ exit(f"Found more than one new device: {new_devices}") -+ -+ -+async def log_stream_task(initial_devices): -+ # Wait up to 5 minutes for the build to complete and the simulator to boot. -+ udid = await asyncio.wait_for(find_device(initial_devices), 5 * 60) -+ -+ # Stream the iOS 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 "/iOSTestbedTests.xctest/iOSTestbedTests"' -+ ' OR senderImagePath ENDSWITH "/Python.framework/Python"' -+ ), -+ ] -+ -+ async with async_process( -+ *args, -+ stdout=subprocess.PIPE, -+ stderr=subprocess.STDOUT, -+ ) as process: -+ suppress_dupes = False -+ while line := (await process.stdout.readline()).decode(*DECODE_ARGS): -+ # The iOS log streamer can sometimes lag; when it does, it outputs -+ # a warning about messages being dropped... often multiple times. -+ # Only print the first of these duplicated warnings. -+ if line.startswith("=== Messages dropped "): -+ if not suppress_dupes: -+ suppress_dupes = True -+ sys.stdout.write(line) -+ else: -+ suppress_dupes = False -+ sys.stdout.write(line) -+ sys.stdout.flush() -+ -+ -+async def xcode_test(location, simulator, verbose): -+ # Run the test suite on the named simulator -+ print("Starting xcodebuild...") -+ args = [ -+ "xcodebuild", -+ "test", -+ "-project", -+ str(location / "iOSTestbed.xcodeproj"), -+ "-scheme", -+ "iOSTestbed", -+ "-destination", -+ f"platform=iOS Simulator,name={simulator}", -+ "-resultBundlePath", -+ str(location / f"{datetime.now():%Y%m%d-%H%M%S}.xcresult"), -+ "-derivedDataPath", -+ str(location / "DerivedData"), -+ ] -+ if not verbose: -+ args += ["-quiet"] -+ -+ async with async_process( -+ *args, -+ stdout=subprocess.PIPE, -+ stderr=subprocess.STDOUT, -+ ) as process: -+ while line := (await process.stdout.readline()).decode(*DECODE_ARGS): -+ sys.stdout.write(line) -+ sys.stdout.flush() -+ -+ status = await asyncio.wait_for(process.wait(), timeout=1) -+ exit(status) -+ -+ -+def clone_testbed( -+ source: Path, -+ target: Path, -+ framework: Path, -+ apps: list[Path], -+) -> None: -+ if target.exists(): -+ print(f"{target} already exists; aborting without creating project.") -+ sys.exit(10) -+ -+ if framework is None: -+ if not ( -+ source / "Python.xcframework/ios-arm64_x86_64-simulator/bin" -+ ).is_dir(): -+ print( -+ f"The testbed being cloned ({source}) does not contain " -+ f"a simulator framework. Re-run with --framework" -+ ) -+ sys.exit(11) -+ else: -+ if not framework.is_dir(): -+ print(f"{framework} does not exist.") -+ sys.exit(12) -+ elif not ( -+ framework.suffix == ".xcframework" -+ or (framework / "Python.framework").is_dir() -+ ): -+ print( -+ f"{framework} is not an XCframework, " -+ f"or a simulator slice of a framework build." -+ ) -+ sys.exit(13) -+ -+ print("Cloning testbed project:") -+ print(f" Cloning {source}...", end="", flush=True) -+ shutil.copytree(source, target, symlinks=True) -+ print(" done") -+ -+ if framework is not None: -+ if framework.suffix == ".xcframework": -+ print(" Installing XCFramework...", end="", flush=True) -+ xc_framework_path = (target / "Python.xcframework").resolve() -+ if xc_framework_path.is_dir(): -+ shutil.rmtree(xc_framework_path) -+ else: -+ xc_framework_path.unlink() -+ 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) -+ sim_framework_path = ( -+ target / "Python.xcframework" / "ios-arm64_x86_64-simulator" -+ ).resolve() -+ if sim_framework_path.is_dir(): -+ shutil.rmtree(sim_framework_path) -+ else: -+ sim_framework_path.unlink() -+ sim_framework_path.symlink_to( -+ framework.relative_to(sim_framework_path.parent, walk_up=True) -+ ) -+ print(" done") -+ else: -+ print(" Using pre-existing iOS framework.") -+ -+ for app_src in apps: -+ print(f" Installing app {app_src.name!r}...", end="", flush=True) -+ app_target = target / f"iOSTestbed/app/{app_src.name}" -+ if app_target.is_dir(): -+ shutil.rmtree(app_target) -+ shutil.copytree(app_src, app_target) -+ print(" done") -+ -+ print(f"Successfully cloned testbed: {target.resolve()}") -+ -+ -+def update_plist(testbed_path, args): -+ # Add the test runner arguments to the testbed's Info.plist file. -+ info_plist = testbed_path / "iOSTestbed" / "iOSTestbed-Info.plist" -+ with info_plist.open("rb") as f: -+ info = plistlib.load(f) -+ -+ info["TestArgs"] = args -+ -+ with info_plist.open("wb") as f: -+ plistlib.dump(info, f) -+ -+ -+async def run_testbed(simulator: str, args: list[str], verbose: bool=False): -+ location = Path(__file__).parent -+ print("Updating plist...", end="", flush=True) -+ update_plist(location, args) -+ print(" done.") -+ -+ # 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)) -+ 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] -+ -+ -+def main(): -+ parser = argparse.ArgumentParser( -+ description=( -+ "Manages the process of testing a Python project in the iOS simulator." -+ ), -+ ) -+ -+ subcommands = parser.add_subparsers(dest="subcommand") -+ -+ clone = subcommands.add_parser( -+ "clone", -+ description=( -+ "Clone the testbed project, copying in an iOS Python framework and" -+ "any specified application code." -+ ), -+ help="Clone a testbed project to a new location.", -+ ) -+ clone.add_argument( -+ "--framework", -+ help=( -+ "The location of the XCFramework (or simulator-only slice of an " -+ "XCFramework) to use when running the testbed" -+ ), -+ ) -+ clone.add_argument( -+ "--app", -+ dest="apps", -+ action="append", -+ default=[], -+ help="The location of any code to include in the testbed project", -+ ) -+ clone.add_argument( -+ "location", -+ help="The path where the testbed will be cloned.", -+ ) -+ -+ run = subcommands.add_parser( -+ "run", -+ usage="%(prog)s [-h] [--simulator SIMULATOR] -- [ ...]", -+ description=( -+ "Run a testbed project. The arguments provided after `--` will be " -+ "passed to the running iOS process as if they were arguments to " -+ "`python -m`." -+ ), -+ help="Run a testbed project", -+ ) -+ run.add_argument( -+ "--simulator", -+ default="iPhone SE (3rd Generation)", -+ help="The name of the simulator to use (default: 'iPhone SE (3rd Generation)')", -+ ) -+ run.add_argument( -+ "-v", "--verbose", -+ action="store_true", -+ help="Enable verbose output", -+ ) -+ -+ try: -+ pos = sys.argv.index("--") -+ testbed_args = sys.argv[1:pos] -+ test_args = sys.argv[pos + 1 :] -+ except ValueError: -+ testbed_args = sys.argv[1:] -+ test_args = [] -+ -+ context = parser.parse_args(testbed_args) -+ -+ if context.subcommand == "clone": -+ clone_testbed( -+ source=Path(__file__).parent, -+ target=Path(context.location), -+ framework=Path(context.framework).resolve() if context.framework else None, -+ apps=[Path(app) for app in context.apps], -+ ) -+ elif context.subcommand == "run": -+ if test_args: -+ if not ( -+ Path(__file__).parent / "Python.xcframework/ios-arm64_x86_64-simulator/bin" -+ ).is_dir(): -+ print( -+ f"Testbed does not contain a compiled iOS framework. Use " -+ f"`python {sys.argv[0]} clone ...` to create a runnable " -+ f"clone of this testbed." -+ ) -+ sys.exit(20) -+ -+ asyncio.run( -+ run_testbed( -+ simulator=context.simulator, -+ verbose=context.verbose, -+ args=test_args, -+ ) -+ ) -+ else: -+ print(f"Must specify test arguments (e.g., {sys.argv[0]} run -- test)") -+ print() -+ parser.print_help(sys.stderr) -+ sys.exit(21) -+ else: -+ parser.print_help(sys.stderr) -+ sys.exit(1) -+ -+ -+if __name__ == "__main__": -+ main() -diff --git a/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj b/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj -index 6819ac0eeed..c7d63909ee2 100644 ---- a/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj -+++ b/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj -@@ -263,6 +263,7 @@ - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "set -e\n\nmkdir -p \"$CODESIGNING_FOLDER_PATH/python/lib\"\nif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-iphonesimulator\" ]; then\n echo \"Installing Python modules for iOS Simulator\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64_x86_64-simulator/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nelse\n echo \"Installing Python modules for iOS Device\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nfi\n"; -+ showEnvVarsInLog = 0; - }; - 607A66562B0F06200010BFC8 /* Prepare Python Binary Modules */ = { - isa = PBXShellScriptBuildPhase; -@@ -282,6 +283,7 @@ - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "set -e\n\ninstall_dylib () {\n INSTALL_BASE=$1\n FULL_EXT=$2\n\n # The name of the extension file\n EXT=$(basename \"$FULL_EXT\")\n # The location of the extension file, relative to the bundle\n RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/} \n # The path to the extension file, relative to the install base\n PYTHON_EXT=${RELATIVE_EXT/$INSTALL_BASE/}\n # The full dotted name of the extension module, constructed from the file path.\n FULL_MODULE_NAME=$(echo $PYTHON_EXT | cut -d \".\" -f 1 | tr \"/\" \".\"); \n # A bundle identifier; not actually used, but required by Xcode framework packaging\n FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr \"_\" \"-\")\n # The name of the framework folder.\n FRAMEWORK_FOLDER=\"Frameworks/$FULL_MODULE_NAME.framework\"\n\n # If the framework folder doesn't exist, create it.\n if [ ! -d \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\" ]; then\n echo \"Creating framework for $RELATIVE_EXT\" \n mkdir -p \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\"\n cp \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleExecutable -string \"$FULL_MODULE_NAME\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleIdentifier -string \"$FRAMEWORK_BUNDLE_ID\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n fi\n \n echo \"Installing binary for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" \n mv \"$FULL_EXT\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\"\n # Create a placeholder .fwork file where the .so was\n echo \"$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" > ${FULL_EXT%.so}.fwork\n # Create a back reference to the .so file location in the framework\n echo \"${RELATIVE_EXT%.so}.fwork\" > \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin\" \n}\n\nPYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/python/lib\")\necho \"Install Python $PYTHON_VER standard library extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib python/lib/$PYTHON_VER/lib-dynload/ \"$FULL_EXT\"\ndone\necho \"Install app package extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app_packages\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app_packages/ \"$FULL_EXT\"\ndone\necho \"Install app extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app/ \"$FULL_EXT\"\ndone\n\n# Clean up dylib template \nrm -f \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\"\necho \"Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)...\"\nfind \"$CODESIGNING_FOLDER_PATH/Frameworks\" -name \"*.framework\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der \"{}\" \\; \n"; -+ showEnvVarsInLog = 0; - }; - /* End PBXShellScriptBuildPhase section */ - -diff --git a/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m b/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m -index db00d43da85..6db38253396 100644 ---- a/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m -+++ b/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m -@@ -24,8 +24,11 @@ - - NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; - -- // Disable all color, as the Xcode log can't display color -+ // 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("PY_COLORS", "0", true); - - // Arguments to pass into the test suite runner. - // argv[0] must identify the process; any subsequent arg -@@ -50,6 +53,8 @@ - // 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 --- /dev/null +++ b/tvOS/README.rst @@ -0,0 +1,108 @@ From 42a4a135fba53e0a657ef3fcf4cc9c5a4b3cf41a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 22 Dec 2024 20:56:00 +0000 Subject: [PATCH 064/113] Bump actions/upload-artifact from 4.4.3 to 4.5.0 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.4.3 to 4.5.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.4.3...v4.5.0) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2a78d775..e600ed36 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -121,7 +121,7 @@ jobs: make ${{ matrix.target }} BUILD_NUMBER=${{ needs.config.outputs.BUILD_NUMBER }} - name: Upload build artefacts - uses: actions/upload-artifact@v4.4.3 + uses: actions/upload-artifact@v4.5.0 with: name: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz path: dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz From dad62c3fd5945523c4af72ec9a61598c66326f95 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 07:10:22 +0800 Subject: [PATCH 065/113] Bump actions/upload-artifact from 4.5.0 to 4.6.0 (#244) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.5.0 to 4.6.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.5.0...v4.6.0) --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e600ed36..263d5771 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -121,7 +121,7 @@ jobs: make ${{ matrix.target }} BUILD_NUMBER=${{ needs.config.outputs.BUILD_NUMBER }} - name: Upload build artefacts - uses: actions/upload-artifact@v4.5.0 + uses: actions/upload-artifact@v4.6.0 with: name: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz path: dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz From 6d5096d3869a6d61cee965bd2f7b4e725e57784b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 07:26:14 +0800 Subject: [PATCH 066/113] Bump ncipollo/release-action from 1.14.0 to 1.15.0 (#243) Bumps [ncipollo/release-action](https://github.com/ncipollo/release-action) from 1.14.0 to 1.15.0. - [Release notes](https://github.com/ncipollo/release-action/releases) - [Commits](https://github.com/ncipollo/release-action/compare/v1.14.0...v1.15.0) --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 5e5ef751..4c0a16a4 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -47,7 +47,7 @@ jobs: merge-multiple: true - name: Create Release - uses: ncipollo/release-action@v1.14.0 + uses: ncipollo/release-action@v1.15.0 with: name: ${{ needs.ci.outputs.PYTHON_VER }}-${{ needs.config.outputs.BUILD_NUMBER }} tag: ${{ needs.ci.outputs.PYTHON_VER }}-${{ needs.config.outputs.BUILD_NUMBER }} From a3ffcad67280310770611e68c2eee5c269158545 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 3 Feb 2025 09:22:33 +0800 Subject: [PATCH 067/113] Update patch to 3.14.0a4. --- Makefile | 2 +- patch/Python/Python.patch | 192 +++++++++++++++++++------------------- 2 files changed, 97 insertions(+), 97 deletions(-) diff --git a/Makefile b/Makefile index 720461ea..0aa9c0d2 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,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.0a3 +PYTHON_VERSION=3.14.0a4 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+") diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 19768c8e..6137a65d 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -113,10 +113,10 @@ index 1f6baed66d3..235dd98c60a 100644 macos_release = mac_ver()[0] if macos_release: diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py -index ed7b6a335d0..322e6cf1eee 100644 +index 7a4a8f65a5e..04ceb922249 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py -@@ -690,6 +690,14 @@ +@@ -691,6 +691,14 @@ release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0") osname = sys.platform machine = sys.implementation._multiarch @@ -163,10 +163,10 @@ index ec0857a4a99..2350e9dc821 100644 # elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX PLATFORM_TRIPLET=darwin diff --git a/configure b/configure -index 57be576e3ca..6d4ef3d0e01 100755 +index 70581e11b60..249a67228b0 100755 --- a/configure +++ b/configure -@@ -980,6 +980,8 @@ +@@ -974,6 +974,8 @@ CFLAGS CC HAS_XCRUN @@ -175,7 +175,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 IPHONEOS_DEPLOYMENT_TARGET EXPORT_MACOSX_DEPLOYMENT_TARGET CONFIGURE_MACOSX_DEPLOYMENT_TARGET -@@ -4052,6 +4054,12 @@ +@@ -4097,6 +4099,12 @@ *-apple-ios*) ac_sys_system=iOS ;; @@ -185,10 +185,10 @@ index 57be576e3ca..6d4ef3d0e01 100755 + *-apple-watchos*) + ac_sys_system=watchOS + ;; - *-*-vxworks*) - ac_sys_system=VxWorks - ;; -@@ -4129,7 +4137,7 @@ + *-*-darwin*) + ac_sys_system=Darwin + ;; +@@ -4177,7 +4185,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. # @@ -197,7 +197,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 # 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 -@@ -4144,6 +4152,14 @@ +@@ -4192,6 +4200,14 @@ 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 ;; @@ -212,7 +212,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 *) esac fi -@@ -4152,6 +4168,14 @@ +@@ -4200,6 +4216,14 @@ 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 ;; @@ -227,7 +227,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 *) esac fi -@@ -4160,6 +4184,14 @@ +@@ -4208,6 +4232,14 @@ 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 ;; @@ -242,7 +242,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 *) esac fi -@@ -4168,6 +4200,14 @@ +@@ -4216,6 +4248,14 @@ 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++ ;; @@ -257,7 +257,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 *) esac fi -@@ -4288,8 +4328,10 @@ +@@ -4338,8 +4378,10 @@ case $enableval in yes) case $ac_sys_system in @@ -270,7 +270,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 esac esac -@@ -4298,6 +4340,8 @@ +@@ -4348,6 +4390,8 @@ no) case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -279,7 +279,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4404,6 +4448,36 @@ +@@ -4454,6 +4498,36 @@ ac_config_files="$ac_config_files iOS/Resources/Info.plist" @@ -316,8 +316,8 @@ index 57be576e3ca..6d4ef3d0e01 100755 ;; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 -@@ -4415,6 +4489,8 @@ - +@@ -4465,6 +4539,8 @@ + 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 ;; @@ -325,7 +325,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4468,8 +4544,8 @@ +@@ -4519,8 +4595,8 @@ case "$withval" in yes) case $ac_sys_system in @@ -336,9 +336,9 @@ index 57be576e3ca..6d4ef3d0e01 100755 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 ;; -@@ -4487,8 +4563,8 @@ - else $as_nop - +@@ -4538,8 +4614,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 @@ -347,7 +347,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 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; } -@@ -4542,6 +4618,50 @@ +@@ -4594,6 +4670,50 @@ ;; esac ;; @@ -395,10 +395,10 @@ index 57be576e3ca..6d4ef3d0e01 100755 + ;; + esac + ;; - *-*-vxworks*) - _host_ident=$host_cpu - ;; -@@ -4620,9 +4740,13 @@ + *-*-darwin*) + case "$host_cpu" in + arm*) +@@ -4684,9 +4804,13 @@ define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; @@ -413,7 +413,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -4685,7 +4809,10 @@ +@@ -4749,7 +4873,10 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' @@ -425,7 +425,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 # checks for alternative programs -@@ -4726,6 +4853,16 @@ +@@ -4790,6 +4917,16 @@ as_fn_append CFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" as_fn_append LDFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" ;; #( @@ -442,7 +442,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 *) : ;; esac -@@ -7030,6 +7167,10 @@ +@@ -7159,6 +7296,10 @@ MULTIARCH="" ;; #( iOS) : MULTIARCH="" ;; #( @@ -453,7 +453,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -7050,7 +7191,7 @@ +@@ -7179,7 +7320,7 @@ printf "%s\n" "$MULTIARCH" >&6; } case $ac_sys_system in #( @@ -462,7 +462,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2` ;; #( *) : SOABI_PLATFORM=$PLATFORM_TRIPLET -@@ -7101,6 +7242,14 @@ +@@ -7230,6 +7371,14 @@ PY_SUPPORT_TIER=3 ;; #( aarch64-apple-ios*/clang) : PY_SUPPORT_TIER=3 ;; #( @@ -477,7 +477,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 aarch64-*-linux-android/clang) : PY_SUPPORT_TIER=3 ;; #( x86_64-*-linux-android/clang) : -@@ -7530,7 +7679,7 @@ +@@ -7666,7 +7815,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; @@ -486,7 +486,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5;; -@@ -7596,7 +7745,7 @@ +@@ -7732,7 +7881,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -495,7 +495,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -13160,7 +13309,7 @@ +@@ -13540,7 +13689,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -504,7 +504,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -13293,7 +13442,7 @@ +@@ -13673,7 +13822,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -513,7 +513,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -13317,7 +13466,7 @@ +@@ -13697,7 +13846,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -522,7 +522,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -14769,7 +14918,7 @@ +@@ -15282,7 +15431,7 @@ ctypes_malloc_closure=yes ;; #( @@ -531,7 +531,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 ctypes_malloc_closure=yes ;; #( -@@ -18272,12 +18421,6 @@ +@@ -19031,12 +19180,6 @@ then : printf "%s\n" "#define HAVE_DUP3 1" >>confdefs.h @@ -544,7 +544,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 fi ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes -@@ -18338,18 +18481,6 @@ +@@ -19097,18 +19240,6 @@ then : printf "%s\n" "#define HAVE_FEXECVE 1" >>confdefs.h @@ -563,7 +563,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 fi ac_fn_c_check_func "$LINENO" "fpathconf" "ac_cv_func_fpathconf" if test "x$ac_cv_func_fpathconf" = xyes -@@ -18776,24 +18907,6 @@ +@@ -19535,24 +19666,6 @@ then : printf "%s\n" "#define HAVE_POSIX_OPENPT 1" >>confdefs.h @@ -588,7 +588,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 fi ac_fn_c_check_func "$LINENO" "pread" "ac_cv_func_pread" if test "x$ac_cv_func_pread" = xyes -@@ -19094,12 +19207,6 @@ +@@ -19853,12 +19966,6 @@ then : printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h @@ -601,7 +601,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 fi ac_fn_c_check_func "$LINENO" "sigfillset" "ac_cv_func_sigfillset" if test "x$ac_cv_func_sigfillset" = xyes -@@ -19368,11 +19475,11 @@ +@@ -20127,11 +20234,11 @@ fi @@ -615,7 +615,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" if test "x$ac_cv_func_getentropy" = xyes then : -@@ -19394,6 +19501,53 @@ +@@ -20153,6 +20260,53 @@ fi @@ -669,7 +669,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 { 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} -@@ -22269,7 +22423,8 @@ +@@ -23235,7 +23389,8 @@ # check for openpty, login_tty, and forkpty @@ -679,7 +679,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 for ac_func in openpty do : -@@ -22365,7 +22520,7 @@ +@@ -23349,7 +23504,7 @@ fi done @@ -688,7 +688,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 printf %s "checking for library containing login_tty... " >&6; } if test ${ac_cv_search_login_tty+y} then : -@@ -22522,6 +22677,7 @@ +@@ -23532,6 +23687,7 @@ fi done @@ -696,7 +696,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 # check for long file support functions ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" -@@ -22768,10 +22924,10 @@ +@@ -23797,10 +23953,10 @@ done @@ -709,7 +709,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 then for ac_func in clock_settime -@@ -24999,8 +25155,8 @@ +@@ -26139,8 +26295,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -720,7 +720,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -27752,7 +27908,7 @@ +@@ -29010,7 +29166,7 @@ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for device files" >&5 printf "%s\n" "$as_me: checking for device files" >&6;} @@ -729,7 +729,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -28184,7 +28340,7 @@ +@@ -29458,7 +29614,7 @@ with_ensurepip=no ;; #( WASI) : with_ensurepip=no ;; #( @@ -738,7 +738,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 with_ensurepip=no ;; #( *) : with_ensurepip=upgrade -@@ -29130,7 +29286,7 @@ +@@ -30438,7 +30594,7 @@ ;; #( Darwin) : ;; #( @@ -747,7 +747,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 -@@ -33028,6 +33184,8 @@ +@@ -34441,6 +34597,8 @@ "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" ;; @@ -757,7 +757,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 "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 bd0221481c5..6dca265f3cc 100644 +index d7c3920d049..6027da5752a 100644 --- a/configure.ac +++ b/configure.ac @@ -330,6 +330,12 @@ @@ -770,10 +770,10 @@ index bd0221481c5..6dca265f3cc 100644 + *-apple-watchos*) + ac_sys_system=watchOS + ;; - *-*-vxworks*) - ac_sys_system=VxWorks - ;; -@@ -401,7 +407,7 @@ + *-*-darwin*) + ac_sys_system=Darwin + ;; +@@ -404,7 +410,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. # @@ -782,7 +782,7 @@ index bd0221481c5..6dca265f3cc 100644 # 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 -@@ -416,6 +422,14 @@ +@@ -419,6 +425,14 @@ 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 ;; @@ -797,7 +797,7 @@ index bd0221481c5..6dca265f3cc 100644 *) esac fi -@@ -424,6 +438,14 @@ +@@ -427,6 +441,14 @@ 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 ;; @@ -812,7 +812,7 @@ index bd0221481c5..6dca265f3cc 100644 *) esac fi -@@ -432,6 +454,14 @@ +@@ -435,6 +457,14 @@ 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 ;; @@ -827,7 +827,7 @@ index bd0221481c5..6dca265f3cc 100644 *) esac fi -@@ -440,6 +470,14 @@ +@@ -443,6 +473,14 @@ 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++ ;; @@ -842,7 +842,7 @@ index bd0221481c5..6dca265f3cc 100644 *) esac fi -@@ -554,8 +592,10 @@ +@@ -557,8 +595,10 @@ case $enableval in yes) case $ac_sys_system in @@ -855,7 +855,7 @@ index bd0221481c5..6dca265f3cc 100644 *) AC_MSG_ERROR([Unknown platform for framework build]) esac esac -@@ -564,6 +604,8 @@ +@@ -567,6 +607,8 @@ no) case $ac_sys_system in iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;; @@ -864,7 +864,7 @@ index bd0221481c5..6dca265f3cc 100644 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -666,6 +708,34 @@ +@@ -669,6 +711,34 @@ AC_CONFIG_FILES([iOS/Resources/Info.plist]) ;; @@ -899,7 +899,7 @@ index bd0221481c5..6dca265f3cc 100644 *) AC_MSG_ERROR([Unknown platform for framework build]) ;; -@@ -674,6 +744,8 @@ +@@ -677,6 +747,8 @@ ],[ case $ac_sys_system in iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;; @@ -908,7 +908,7 @@ index bd0221481c5..6dca265f3cc 100644 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -726,8 +798,8 @@ +@@ -729,8 +801,8 @@ case "$withval" in yes) case $ac_sys_system in @@ -919,7 +919,7 @@ index bd0221481c5..6dca265f3cc 100644 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" ;; *) AC_MSG_ERROR([no default app store compliance patch available for $ac_sys_system]) ;; -@@ -741,8 +813,8 @@ +@@ -744,8 +816,8 @@ esac ],[ case $ac_sys_system in @@ -930,7 +930,7 @@ index bd0221481c5..6dca265f3cc 100644 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" AC_MSG_RESULT([applying default app store compliance patch]) ;; -@@ -790,6 +862,46 @@ +@@ -793,6 +865,46 @@ ;; esac ;; @@ -974,10 +974,10 @@ index bd0221481c5..6dca265f3cc 100644 + ;; + esac + ;; - *-*-vxworks*) - _host_ident=$host_cpu - ;; -@@ -867,9 +979,13 @@ + *-*-darwin*) + case "$host_cpu" in + arm*) +@@ -882,9 +994,13 @@ define_xopen_source=no;; Darwin/@<:@[12]@:>@@<:@0-9@:>@.*) define_xopen_source=no;; @@ -992,7 +992,7 @@ index bd0221481c5..6dca265f3cc 100644 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -928,8 +1044,11 @@ +@@ -943,8 +1059,11 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' @@ -1005,7 +1005,7 @@ index bd0221481c5..6dca265f3cc 100644 # checks for alternative programs -@@ -963,11 +1082,17 @@ +@@ -978,11 +1097,17 @@ ], ) @@ -1024,7 +1024,7 @@ index bd0221481c5..6dca265f3cc 100644 ], ) -@@ -1156,6 +1281,8 @@ +@@ -1171,6 +1296,8 @@ AS_CASE([$ac_sys_system], [Darwin*], [MULTIARCH=""], [iOS], [MULTIARCH=""], @@ -1033,7 +1033,7 @@ index bd0221481c5..6dca265f3cc 100644 [FreeBSD*], [MULTIARCH=""], [MULTIARCH=$($CC --print-multiarch 2>/dev/null)] ) -@@ -1177,7 +1304,7 @@ +@@ -1192,7 +1319,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], @@ -1042,7 +1042,7 @@ index bd0221481c5..6dca265f3cc 100644 [SOABI_PLATFORM=$PLATFORM_TRIPLET] ) -@@ -1211,6 +1338,10 @@ +@@ -1226,6 +1353,10 @@ [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 @@ -1053,7 +1053,7 @@ index bd0221481c5..6dca265f3cc 100644 [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 -@@ -1520,7 +1651,7 @@ +@@ -1535,7 +1666,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; @@ -1062,7 +1062,7 @@ index bd0221481c5..6dca265f3cc 100644 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) AC_MSG_ERROR([Unknown platform for framework build]);; -@@ -1585,7 +1716,7 @@ +@@ -1600,7 +1731,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -1071,7 +1071,7 @@ index bd0221481c5..6dca265f3cc 100644 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -3412,7 +3543,7 @@ +@@ -3455,7 +3586,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -1080,7 +1080,7 @@ index bd0221481c5..6dca265f3cc 100644 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -3536,7 +3667,7 @@ +@@ -3579,7 +3710,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -1089,7 +1089,7 @@ index bd0221481c5..6dca265f3cc 100644 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -3560,7 +3691,7 @@ +@@ -3603,7 +3734,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -1098,7 +1098,7 @@ index bd0221481c5..6dca265f3cc 100644 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -3980,7 +4111,7 @@ +@@ -4023,7 +4154,7 @@ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], @@ -1107,19 +1107,19 @@ index bd0221481c5..6dca265f3cc 100644 ctypes_malloc_closure=yes ], [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] -@@ -5098,9 +5229,9 @@ +@@ -5140,9 +5271,9 @@ # checks for library functions AC_CHECK_FUNCS([ \ accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ -- copy_file_range ctermid dup dup3 execv explicit_bzero explicit_memset \ -+ copy_file_range ctermid dup dup3 explicit_bzero explicit_memset \ +- copy_file_range ctermid dladdr dup dup3 execv explicit_bzero explicit_memset \ ++ copy_file_range ctermid dladdr dup dup3 explicit_bzero explicit_memset \ faccessat fchmod fchmodat fchown fchownat fdopendir fdwalk fexecve \ - fork fork1 fpathconf fstatat ftime ftruncate futimens futimes futimesat \ + fpathconf fstatat ftime ftruncate futimens futimes futimesat \ gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \ getgrnam_r getgrouplist gethostname getitimer getloadavg getlogin \ getpeername getpgid getpid getppid getpriority _getpty \ -@@ -5108,8 +5239,7 @@ +@@ -5150,8 +5281,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 \ @@ -1129,7 +1129,7 @@ index bd0221481c5..6dca265f3cc 100644 pread preadv preadv2 process_vm_readv \ pthread_cond_timedwait_relative_np pthread_condattr_setclock pthread_init \ pthread_kill pthread_getname_np pthread_setname_np \ -@@ -5118,7 +5248,7 @@ +@@ -5160,7 +5290,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 \ @@ -1138,7 +1138,7 @@ index bd0221481c5..6dca265f3cc 100644 sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ -@@ -5133,12 +5263,20 @@ +@@ -5175,12 +5305,20 @@ AC_CHECK_FUNCS([lchmod]) fi @@ -1162,7 +1162,7 @@ index bd0221481c5..6dca265f3cc 100644 fi AC_CHECK_DECL([dirfd], -@@ -5392,20 +5530,22 @@ +@@ -5434,20 +5572,22 @@ ]) # check for openpty, login_tty, and forkpty @@ -1199,7 +1199,7 @@ index bd0221481c5..6dca265f3cc 100644 # check for long file support functions AC_CHECK_FUNCS([fseek64 fseeko fstatvfs ftell64 ftello statvfs]) -@@ -5444,10 +5584,10 @@ +@@ -5486,10 +5626,10 @@ ]) ]) @@ -1212,7 +1212,7 @@ index bd0221481c5..6dca265f3cc 100644 then AC_CHECK_FUNCS([clock_settime], [], [ AC_CHECK_LIB([rt], [clock_settime], [ -@@ -6198,8 +6338,8 @@ +@@ -6240,8 +6380,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -1223,7 +1223,7 @@ index bd0221481c5..6dca265f3cc 100644 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -6863,7 +7003,7 @@ +@@ -6900,7 +7040,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. @@ -1232,7 +1232,7 @@ index bd0221481c5..6dca265f3cc 100644 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -7119,7 +7259,7 @@ +@@ -7156,7 +7296,7 @@ AS_CASE([$ac_sys_system], [Emscripten], [with_ensurepip=no], [WASI], [with_ensurepip=no], @@ -1241,7 +1241,7 @@ index bd0221481c5..6dca265f3cc 100644 [with_ensurepip=upgrade] ) ]) -@@ -7529,7 +7669,7 @@ +@@ -7567,7 +7707,7 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], From e6682a41b2310b820cf554f3e660eff43d115459 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 10:42:14 +0800 Subject: [PATCH 068/113] Bump actions/setup-python from 5.3.0 to 5.4.0 (#245) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.3.0 to 5.4.0. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5.3.0...v5.4.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yaml | 2 +- .github/workflows/publish.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 263d5771..44609a7a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -109,7 +109,7 @@ jobs: - uses: actions/checkout@v4.1.7 - name: Set up Python - uses: actions/setup-python@v5.3.0 + uses: actions/setup-python@v5.4.0 with: # Appending -dev ensures that we can always build the dev release. # It's a no-op for versions that have been published. diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index a9f69ed4..5d2ce753 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python environment - uses: actions/setup-python@v5.3.0 + uses: actions/setup-python@v5.4.0 with: python-version: "3.X" From a2c04c37393d47947d467e6ad234b17435b64fbc Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Thu, 13 Feb 2025 15:01:46 +0800 Subject: [PATCH 069/113] Bump patch to Python3.14.0a5. (#247) Update Python to 3.14.0a5. Also includes: * python/cpython#130026 testbed symlink handling * LibFFI 3.4.7 * OpenSSL 3.0.16 * XZ 5.6.4 --- Makefile | 8 +- patch/Python/Python.patch | 248 ++++++++++++++++++++++++++------------ 2 files changed, 174 insertions(+), 82 deletions(-) diff --git a/Makefile b/Makefile index 0aa9c0d2..b34d7e05 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,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.0a4 +PYTHON_VERSION=3.14.0a5 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+") @@ -27,10 +27,10 @@ PYTHON_VER=$(basename $(PYTHON_VERSION)) # The binary releases of dependencies, published at: # https://github.com/beeware/cpython-apple-source-deps/releases BZIP2_VERSION=1.0.8-1 -LIBFFI_VERSION=3.4.6-1 +LIBFFI_VERSION=3.4.7-1 MPDECIMAL_VERSION=4.0.0-1 -OPENSSL_VERSION=3.0.15-1 -XZ_VERSION=5.6.2-1 +OPENSSL_VERSION=3.0.16-1 +XZ_VERSION=5.6.4-1 # Supported OS OS_LIST=macOS iOS tvOS watchOS diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 6137a65d..910f33c2 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -113,10 +113,10 @@ index 1f6baed66d3..235dd98c60a 100644 macos_release = mac_ver()[0] if macos_release: diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py -index 7a4a8f65a5e..04ceb922249 100644 +index 69f72452c40..34ce643340b 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py -@@ -691,6 +691,14 @@ +@@ -719,6 +719,14 @@ release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0") osname = sys.platform machine = sys.implementation._multiarch @@ -163,7 +163,7 @@ index ec0857a4a99..2350e9dc821 100644 # elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX PLATFORM_TRIPLET=darwin diff --git a/configure b/configure -index 70581e11b60..249a67228b0 100755 +index d46bc563a67..d5cd81d16a8 100755 --- a/configure +++ b/configure @@ -974,6 +974,8 @@ @@ -175,7 +175,7 @@ index 70581e11b60..249a67228b0 100755 IPHONEOS_DEPLOYMENT_TARGET EXPORT_MACOSX_DEPLOYMENT_TARGET CONFIGURE_MACOSX_DEPLOYMENT_TARGET -@@ -4097,6 +4099,12 @@ +@@ -4100,6 +4102,12 @@ *-apple-ios*) ac_sys_system=iOS ;; @@ -188,7 +188,7 @@ index 70581e11b60..249a67228b0 100755 *-*-darwin*) ac_sys_system=Darwin ;; -@@ -4177,7 +4185,7 @@ +@@ -4181,7 +4189,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. # @@ -197,7 +197,7 @@ index 70581e11b60..249a67228b0 100755 # 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 -@@ -4192,6 +4200,14 @@ +@@ -4196,6 +4204,14 @@ 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 ;; @@ -212,7 +212,7 @@ index 70581e11b60..249a67228b0 100755 *) esac fi -@@ -4200,6 +4216,14 @@ +@@ -4204,6 +4220,14 @@ 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 ;; @@ -227,7 +227,7 @@ index 70581e11b60..249a67228b0 100755 *) esac fi -@@ -4208,6 +4232,14 @@ +@@ -4212,6 +4236,14 @@ 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 ;; @@ -242,7 +242,7 @@ index 70581e11b60..249a67228b0 100755 *) esac fi -@@ -4216,6 +4248,14 @@ +@@ -4220,6 +4252,14 @@ 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++ ;; @@ -257,7 +257,7 @@ index 70581e11b60..249a67228b0 100755 *) esac fi -@@ -4338,8 +4378,10 @@ +@@ -4342,8 +4382,10 @@ case $enableval in yes) case $ac_sys_system in @@ -270,7 +270,7 @@ index 70581e11b60..249a67228b0 100755 *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 esac esac -@@ -4348,6 +4390,8 @@ +@@ -4352,6 +4394,8 @@ no) case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -279,7 +279,7 @@ index 70581e11b60..249a67228b0 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4454,6 +4498,36 @@ +@@ -4458,6 +4502,36 @@ ac_config_files="$ac_config_files iOS/Resources/Info.plist" @@ -316,7 +316,7 @@ index 70581e11b60..249a67228b0 100755 ;; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 -@@ -4465,6 +4539,8 @@ +@@ -4469,6 +4543,8 @@ e) case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -325,7 +325,7 @@ index 70581e11b60..249a67228b0 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4519,8 +4595,8 @@ +@@ -4523,8 +4599,8 @@ case "$withval" in yes) case $ac_sys_system in @@ -336,7 +336,7 @@ index 70581e11b60..249a67228b0 100755 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 ;; -@@ -4538,8 +4614,8 @@ +@@ -4542,8 +4618,8 @@ else case e in #( e) case $ac_sys_system in @@ -347,7 +347,7 @@ index 70581e11b60..249a67228b0 100755 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; } -@@ -4594,6 +4670,50 @@ +@@ -4598,6 +4674,50 @@ ;; esac ;; @@ -398,7 +398,7 @@ index 70581e11b60..249a67228b0 100755 *-*-darwin*) case "$host_cpu" in arm*) -@@ -4684,9 +4804,13 @@ +@@ -4688,9 +4808,13 @@ define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; @@ -413,7 +413,7 @@ index 70581e11b60..249a67228b0 100755 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -4749,7 +4873,10 @@ +@@ -4753,7 +4877,10 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' @@ -425,7 +425,7 @@ index 70581e11b60..249a67228b0 100755 # checks for alternative programs -@@ -4790,6 +4917,16 @@ +@@ -4794,6 +4921,16 @@ as_fn_append CFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" as_fn_append LDFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" ;; #( @@ -442,7 +442,7 @@ index 70581e11b60..249a67228b0 100755 *) : ;; esac -@@ -7159,6 +7296,10 @@ +@@ -7163,6 +7300,10 @@ MULTIARCH="" ;; #( iOS) : MULTIARCH="" ;; #( @@ -453,7 +453,7 @@ index 70581e11b60..249a67228b0 100755 FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -7179,7 +7320,7 @@ +@@ -7183,7 +7324,7 @@ printf "%s\n" "$MULTIARCH" >&6; } case $ac_sys_system in #( @@ -462,7 +462,7 @@ index 70581e11b60..249a67228b0 100755 SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2` ;; #( *) : SOABI_PLATFORM=$PLATFORM_TRIPLET -@@ -7230,6 +7371,14 @@ +@@ -7234,6 +7375,14 @@ PY_SUPPORT_TIER=3 ;; #( aarch64-apple-ios*/clang) : PY_SUPPORT_TIER=3 ;; #( @@ -477,7 +477,7 @@ index 70581e11b60..249a67228b0 100755 aarch64-*-linux-android/clang) : PY_SUPPORT_TIER=3 ;; #( x86_64-*-linux-android/clang) : -@@ -7666,7 +7815,7 @@ +@@ -7670,7 +7819,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; @@ -486,7 +486,7 @@ index 70581e11b60..249a67228b0 100755 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5;; -@@ -7732,7 +7881,7 @@ +@@ -7736,7 +7885,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -495,7 +495,7 @@ index 70581e11b60..249a67228b0 100755 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -13540,7 +13689,7 @@ +@@ -13544,7 +13693,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -504,7 +504,7 @@ index 70581e11b60..249a67228b0 100755 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -13673,7 +13822,7 @@ +@@ -13677,7 +13826,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -513,7 +513,7 @@ index 70581e11b60..249a67228b0 100755 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -13697,7 +13846,7 @@ +@@ -13701,7 +13850,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -522,7 +522,7 @@ index 70581e11b60..249a67228b0 100755 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -15282,7 +15431,7 @@ +@@ -15286,7 +15435,7 @@ ctypes_malloc_closure=yes ;; #( @@ -531,7 +531,7 @@ index 70581e11b60..249a67228b0 100755 ctypes_malloc_closure=yes ;; #( -@@ -19031,12 +19180,6 @@ +@@ -19038,12 +19187,6 @@ then : printf "%s\n" "#define HAVE_DUP3 1" >>confdefs.h @@ -544,7 +544,7 @@ index 70581e11b60..249a67228b0 100755 fi ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes -@@ -19097,18 +19240,6 @@ +@@ -19104,18 +19247,6 @@ then : printf "%s\n" "#define HAVE_FEXECVE 1" >>confdefs.h @@ -563,7 +563,7 @@ index 70581e11b60..249a67228b0 100755 fi ac_fn_c_check_func "$LINENO" "fpathconf" "ac_cv_func_fpathconf" if test "x$ac_cv_func_fpathconf" = xyes -@@ -19535,24 +19666,6 @@ +@@ -19542,24 +19673,6 @@ then : printf "%s\n" "#define HAVE_POSIX_OPENPT 1" >>confdefs.h @@ -588,7 +588,7 @@ index 70581e11b60..249a67228b0 100755 fi ac_fn_c_check_func "$LINENO" "pread" "ac_cv_func_pread" if test "x$ac_cv_func_pread" = xyes -@@ -19853,12 +19966,6 @@ +@@ -19860,12 +19973,6 @@ then : printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h @@ -601,7 +601,7 @@ index 70581e11b60..249a67228b0 100755 fi ac_fn_c_check_func "$LINENO" "sigfillset" "ac_cv_func_sigfillset" if test "x$ac_cv_func_sigfillset" = xyes -@@ -20127,11 +20234,11 @@ +@@ -20134,11 +20241,11 @@ fi @@ -615,7 +615,7 @@ index 70581e11b60..249a67228b0 100755 ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" if test "x$ac_cv_func_getentropy" = xyes then : -@@ -20153,6 +20260,53 @@ +@@ -20160,6 +20267,53 @@ fi @@ -669,7 +669,7 @@ index 70581e11b60..249a67228b0 100755 { 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} -@@ -23235,7 +23389,8 @@ +@@ -23242,7 +23396,8 @@ # check for openpty, login_tty, and forkpty @@ -679,7 +679,7 @@ index 70581e11b60..249a67228b0 100755 for ac_func in openpty do : -@@ -23349,7 +23504,7 @@ +@@ -23356,7 +23511,7 @@ fi done @@ -688,7 +688,7 @@ index 70581e11b60..249a67228b0 100755 printf %s "checking for library containing login_tty... " >&6; } if test ${ac_cv_search_login_tty+y} then : -@@ -23532,6 +23687,7 @@ +@@ -23539,6 +23694,7 @@ fi done @@ -696,7 +696,7 @@ index 70581e11b60..249a67228b0 100755 # check for long file support functions ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" -@@ -23797,10 +23953,10 @@ +@@ -23804,10 +23960,10 @@ done @@ -709,7 +709,7 @@ index 70581e11b60..249a67228b0 100755 then for ac_func in clock_settime -@@ -26139,8 +26295,8 @@ +@@ -26146,8 +26302,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -720,7 +720,7 @@ index 70581e11b60..249a67228b0 100755 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -29010,7 +29166,7 @@ +@@ -29017,7 +29173,7 @@ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for device files" >&5 printf "%s\n" "$as_me: checking for device files" >&6;} @@ -729,7 +729,7 @@ index 70581e11b60..249a67228b0 100755 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -29458,7 +29614,7 @@ +@@ -29510,7 +29666,7 @@ with_ensurepip=no ;; #( WASI) : with_ensurepip=no ;; #( @@ -738,7 +738,7 @@ index 70581e11b60..249a67228b0 100755 with_ensurepip=no ;; #( *) : with_ensurepip=upgrade -@@ -30438,7 +30594,7 @@ +@@ -30490,7 +30646,7 @@ ;; #( Darwin) : ;; #( @@ -747,7 +747,7 @@ index 70581e11b60..249a67228b0 100755 -@@ -34441,6 +34597,8 @@ +@@ -34493,6 +34649,8 @@ "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" ;; @@ -757,7 +757,7 @@ index 70581e11b60..249a67228b0 100755 "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 d7c3920d049..6027da5752a 100644 +index faa89095303..9bd51f7da97 100644 --- a/configure.ac +++ b/configure.ac @@ -330,6 +330,12 @@ @@ -773,7 +773,7 @@ index d7c3920d049..6027da5752a 100644 *-*-darwin*) ac_sys_system=Darwin ;; -@@ -404,7 +410,7 @@ +@@ -405,7 +411,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. # @@ -782,7 +782,7 @@ index d7c3920d049..6027da5752a 100644 # 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 -@@ -419,6 +425,14 @@ +@@ -420,6 +426,14 @@ 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 ;; @@ -797,7 +797,7 @@ index d7c3920d049..6027da5752a 100644 *) esac fi -@@ -427,6 +441,14 @@ +@@ -428,6 +442,14 @@ 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 ;; @@ -812,7 +812,7 @@ index d7c3920d049..6027da5752a 100644 *) esac fi -@@ -435,6 +457,14 @@ +@@ -436,6 +458,14 @@ 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 ;; @@ -827,7 +827,7 @@ index d7c3920d049..6027da5752a 100644 *) esac fi -@@ -443,6 +473,14 @@ +@@ -444,6 +474,14 @@ 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++ ;; @@ -842,7 +842,7 @@ index d7c3920d049..6027da5752a 100644 *) esac fi -@@ -557,8 +595,10 @@ +@@ -558,8 +596,10 @@ case $enableval in yes) case $ac_sys_system in @@ -855,7 +855,7 @@ index d7c3920d049..6027da5752a 100644 *) AC_MSG_ERROR([Unknown platform for framework build]) esac esac -@@ -567,6 +607,8 @@ +@@ -568,6 +608,8 @@ no) case $ac_sys_system in iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;; @@ -864,7 +864,7 @@ index d7c3920d049..6027da5752a 100644 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -669,6 +711,34 @@ +@@ -670,6 +712,34 @@ AC_CONFIG_FILES([iOS/Resources/Info.plist]) ;; @@ -899,7 +899,7 @@ index d7c3920d049..6027da5752a 100644 *) AC_MSG_ERROR([Unknown platform for framework build]) ;; -@@ -677,6 +747,8 @@ +@@ -678,6 +748,8 @@ ],[ case $ac_sys_system in iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;; @@ -908,7 +908,7 @@ index d7c3920d049..6027da5752a 100644 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -729,8 +801,8 @@ +@@ -730,8 +802,8 @@ case "$withval" in yes) case $ac_sys_system in @@ -919,7 +919,7 @@ index d7c3920d049..6027da5752a 100644 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" ;; *) AC_MSG_ERROR([no default app store compliance patch available for $ac_sys_system]) ;; -@@ -744,8 +816,8 @@ +@@ -745,8 +817,8 @@ esac ],[ case $ac_sys_system in @@ -930,7 +930,7 @@ index d7c3920d049..6027da5752a 100644 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" AC_MSG_RESULT([applying default app store compliance patch]) ;; -@@ -793,6 +865,46 @@ +@@ -794,6 +866,46 @@ ;; esac ;; @@ -977,7 +977,7 @@ index d7c3920d049..6027da5752a 100644 *-*-darwin*) case "$host_cpu" in arm*) -@@ -882,9 +994,13 @@ +@@ -883,9 +995,13 @@ define_xopen_source=no;; Darwin/@<:@[12]@:>@@<:@0-9@:>@.*) define_xopen_source=no;; @@ -992,7 +992,7 @@ index d7c3920d049..6027da5752a 100644 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -943,8 +1059,11 @@ +@@ -944,8 +1060,11 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' @@ -1005,7 +1005,7 @@ index d7c3920d049..6027da5752a 100644 # checks for alternative programs -@@ -978,11 +1097,17 @@ +@@ -979,11 +1098,17 @@ ], ) @@ -1024,7 +1024,7 @@ index d7c3920d049..6027da5752a 100644 ], ) -@@ -1171,6 +1296,8 @@ +@@ -1172,6 +1297,8 @@ AS_CASE([$ac_sys_system], [Darwin*], [MULTIARCH=""], [iOS], [MULTIARCH=""], @@ -1033,7 +1033,7 @@ index d7c3920d049..6027da5752a 100644 [FreeBSD*], [MULTIARCH=""], [MULTIARCH=$($CC --print-multiarch 2>/dev/null)] ) -@@ -1192,7 +1319,7 @@ +@@ -1193,7 +1320,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], @@ -1042,7 +1042,7 @@ index d7c3920d049..6027da5752a 100644 [SOABI_PLATFORM=$PLATFORM_TRIPLET] ) -@@ -1226,6 +1353,10 @@ +@@ -1227,6 +1354,10 @@ [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 @@ -1053,7 +1053,7 @@ index d7c3920d049..6027da5752a 100644 [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 -@@ -1535,7 +1666,7 @@ +@@ -1536,7 +1667,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; @@ -1062,7 +1062,7 @@ index d7c3920d049..6027da5752a 100644 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) AC_MSG_ERROR([Unknown platform for framework build]);; -@@ -1600,7 +1731,7 @@ +@@ -1601,7 +1732,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -1071,7 +1071,7 @@ index d7c3920d049..6027da5752a 100644 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -3455,7 +3586,7 @@ +@@ -3456,7 +3587,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -1080,7 +1080,7 @@ index d7c3920d049..6027da5752a 100644 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -3579,7 +3710,7 @@ +@@ -3580,7 +3711,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -1089,7 +1089,7 @@ index d7c3920d049..6027da5752a 100644 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -3603,7 +3734,7 @@ +@@ -3604,7 +3735,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -1098,7 +1098,7 @@ index d7c3920d049..6027da5752a 100644 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -4023,7 +4154,7 @@ +@@ -4024,7 +4155,7 @@ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], @@ -1107,7 +1107,7 @@ index d7c3920d049..6027da5752a 100644 ctypes_malloc_closure=yes ], [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] -@@ -5140,9 +5271,9 @@ +@@ -5133,9 +5264,9 @@ # checks for library functions AC_CHECK_FUNCS([ \ accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ @@ -1119,7 +1119,7 @@ index d7c3920d049..6027da5752a 100644 gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \ getgrnam_r getgrouplist gethostname getitimer getloadavg getlogin \ getpeername getpgid getpid getppid getpriority _getpty \ -@@ -5150,8 +5281,7 @@ +@@ -5143,8 +5274,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 \ @@ -1129,7 +1129,7 @@ index d7c3920d049..6027da5752a 100644 pread preadv preadv2 process_vm_readv \ pthread_cond_timedwait_relative_np pthread_condattr_setclock pthread_init \ pthread_kill pthread_getname_np pthread_setname_np \ -@@ -5160,7 +5290,7 @@ +@@ -5153,7 +5283,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 \ @@ -1138,7 +1138,7 @@ index d7c3920d049..6027da5752a 100644 sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ -@@ -5175,12 +5305,20 @@ +@@ -5168,12 +5298,20 @@ AC_CHECK_FUNCS([lchmod]) fi @@ -1162,7 +1162,7 @@ index d7c3920d049..6027da5752a 100644 fi AC_CHECK_DECL([dirfd], -@@ -5434,20 +5572,22 @@ +@@ -5427,20 +5565,22 @@ ]) # check for openpty, login_tty, and forkpty @@ -1199,7 +1199,7 @@ index d7c3920d049..6027da5752a 100644 # check for long file support functions AC_CHECK_FUNCS([fseek64 fseeko fstatvfs ftell64 ftello statvfs]) -@@ -5486,10 +5626,10 @@ +@@ -5479,10 +5619,10 @@ ]) ]) @@ -1212,7 +1212,7 @@ index d7c3920d049..6027da5752a 100644 then AC_CHECK_FUNCS([clock_settime], [], [ AC_CHECK_LIB([rt], [clock_settime], [ -@@ -6240,8 +6380,8 @@ +@@ -6233,8 +6373,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -1223,7 +1223,7 @@ index d7c3920d049..6027da5752a 100644 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -6900,7 +7040,7 @@ +@@ -6893,7 +7033,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. @@ -1232,7 +1232,7 @@ index d7c3920d049..6027da5752a 100644 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -7156,7 +7296,7 @@ +@@ -7187,7 +7327,7 @@ AS_CASE([$ac_sys_system], [Emscripten], [with_ensurepip=no], [WASI], [with_ensurepip=no], @@ -1241,7 +1241,7 @@ index d7c3920d049..6027da5752a 100644 [with_ensurepip=upgrade] ) ]) -@@ -7567,7 +7707,7 @@ +@@ -7598,7 +7738,7 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], @@ -1270,6 +1270,98 @@ index c3e261ecd9e..26ef7a95de4 100644 CFBundleSupportedPlatforms iPhoneOS +diff --git a/iOS/testbed/__main__.py b/iOS/testbed/__main__.py +index b4499f5ac17..08fbe90a1c6 100644 +--- a/iOS/testbed/__main__.py ++++ b/iOS/testbed/__main__.py +@@ -230,33 +230,69 @@ + shutil.copytree(source, target, symlinks=True) + print(" done") + ++ xc_framework_path = target / "Python.xcframework" ++ sim_framework_path = xc_framework_path / "ios-arm64_x86_64-simulator" + if framework is not None: + if framework.suffix == ".xcframework": + print(" Installing XCFramework...", end="", flush=True) +- xc_framework_path = (target / "Python.xcframework").resolve() + if xc_framework_path.is_dir(): + shutil.rmtree(xc_framework_path) + else: +- xc_framework_path.unlink() ++ xc_framework_path.unlink(missing_ok=True) + xc_framework_path.symlink_to( + framework.relative_to(xc_framework_path.parent, walk_up=True) + ) + print(" done") + else: + print(" Installing simulator framework...", end="", flush=True) +- sim_framework_path = ( +- target / "Python.xcframework" / "ios-arm64_x86_64-simulator" +- ).resolve() + if sim_framework_path.is_dir(): + shutil.rmtree(sim_framework_path) + else: +- sim_framework_path.unlink() ++ sim_framework_path.unlink(missing_ok=True) + sim_framework_path.symlink_to( + framework.relative_to(sim_framework_path.parent, walk_up=True) + ) + print(" done") + else: +- print(" Using pre-existing iOS framework.") ++ if ( ++ xc_framework_path.is_symlink() ++ and not xc_framework_path.readlink().is_absolute() ++ ): ++ # XCFramework is a relative symlink. Rewrite the symlink relative ++ # to the new location. ++ print(" Rewriting symlink to XCframework...", end="", flush=True) ++ orig_xc_framework_path = ( ++ source ++ / xc_framework_path.readlink() ++ ).resolve() ++ xc_framework_path.unlink() ++ xc_framework_path.symlink_to( ++ orig_xc_framework_path.relative_to( ++ xc_framework_path.parent, walk_up=True ++ ) ++ ) ++ print(" done") ++ elif ( ++ sim_framework_path.is_symlink() ++ and not sim_framework_path.readlink().is_absolute() ++ ): ++ print(" Rewriting symlink to simulator framework...", end="", flush=True) ++ # Simulator framework is a relative symlink. Rewrite the symlink ++ # relative to the new location. ++ orig_sim_framework_path = ( ++ source ++ / "Python.XCframework" ++ / sim_framework_path.readlink() ++ ).resolve() ++ sim_framework_path.unlink() ++ sim_framework_path.symlink_to( ++ orig_sim_framework_path.relative_to( ++ sim_framework_path.parent, walk_up=True ++ ) ++ ) ++ print(" done") ++ else: ++ print(" Using pre-existing iOS framework.") + + for app_src in apps: + print(f" Installing app {app_src.name!r}...", end="", flush=True) +@@ -372,8 +408,8 @@ + + if context.subcommand == "clone": + clone_testbed( +- source=Path(__file__).parent, +- target=Path(context.location), ++ source=Path(__file__).parent.resolve(), ++ target=Path(context.location).resolve(), + framework=Path(context.framework).resolve() if context.framework else None, + apps=[Path(app) for app in context.apps], + ) --- /dev/null +++ b/tvOS/README.rst @@ -0,0 +1,108 @@ From c82aa70a26a9b6180089e13f9010b5127d83fc44 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 14 Feb 2025 10:19:08 +0800 Subject: [PATCH 070/113] Modify handling of platform site to allow for venv usage. (#246) Moves the platform site handling into the framework, and adds a mechanism to convert a macOS virtual environment into a cross-platform iOS build environment. --- Makefile | 60 +++++++---- patch/Python/_cross_target.py.tmpl | 71 +++++++++++++ patch/Python/_cross_venv.py | 105 +++++++++++++++++++ patch/Python/make_cross_venv.py | 144 ++++++++++++++++++++++++++ patch/Python/sitecustomize.iOS.py | 114 -------------------- patch/Python/sitecustomize.macOS.py | 14 --- patch/Python/sitecustomize.py.tmpl | 22 ++++ patch/Python/sitecustomize.tvOS.py | 99 ------------------ patch/Python/sitecustomize.watchOS.py | 99 ------------------ 9 files changed, 383 insertions(+), 345 deletions(-) create mode 100644 patch/Python/_cross_target.py.tmpl create mode 100644 patch/Python/_cross_venv.py create mode 100644 patch/Python/make_cross_venv.py delete mode 100644 patch/Python/sitecustomize.iOS.py delete mode 100644 patch/Python/sitecustomize.macOS.py create mode 100644 patch/Python/sitecustomize.py.tmpl delete mode 100644 patch/Python/sitecustomize.tvOS.py delete mode 100644 patch/Python/sitecustomize.watchOS.py diff --git a/Makefile b/Makefile index b34d7e05..765bf39f 100644 --- a/Makefile +++ b/Makefile @@ -129,10 +129,10 @@ ARCH-$(target)=$$(subst .,,$$(suffix $(target))) ifneq ($(os),macOS) ifeq ($$(findstring simulator,$$(SDK-$(target))),) TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(OS_LOWER-$(target))$$(VERSION_MIN-$(os)) -IS_SIMULATOR-$(target)="False" +IS_SIMULATOR-$(target)=False else TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(OS_LOWER-$(target))$$(VERSION_MIN-$(os))-simulator -IS_SIMULATOR-$(target)="True" +IS_SIMULATOR-$(target)=True endif endif @@ -261,6 +261,9 @@ PYTHON_LIB-$(target)=$$(PYTHON_FRAMEWORK-$(target))/Python PYTHON_BIN-$(target)=$$(PYTHON_INSTALL-$(target))/bin PYTHON_INCLUDE-$(target)=$$(PYTHON_FRAMEWORK-$(target))/Headers PYTHON_STDLIB-$(target)=$$(PYTHON_INSTALL-$(target))/lib/python$(PYTHON_VER) +PYTHON_PLATFORM_CONFIG-$(target)=$$(PYTHON_INSTALL-$(target))/platform-config/$$(ARCH-$(target))-$$(SDK-$(target)) +PYTHON_PLATFORM_SITECUSTOMIZE-$(target)=$$(PYTHON_PLATFORM_CONFIG-$(target))/sitecustomize.py + $$(PYTHON_SRCDIR-$(target))/configure: \ downloads/Python-$(PYTHON_VERSION).tar.gz \ @@ -319,23 +322,35 @@ $$(PYTHON_LIB-$(target)): $$(PYTHON_SRCDIR-$(target))/python.exe # Remove any .orig files produced by the compliance patching process find $$(PYTHON_INSTALL-$(target)) -name "*.orig" -exec rm {} \; -endif - -PYTHON_SITECUSTOMIZE-$(target)=$(PROJECT_DIR)/support/$(PYTHON_VER)/$(os)/platform-site/$(target)/sitecustomize.py -$$(PYTHON_SITECUSTOMIZE-$(target)): - @echo ">>> Create cross-platform sitecustomize.py for $(target)" - mkdir -p $$(dir $$(PYTHON_SITECUSTOMIZE-$(target))) - cat $(PROJECT_DIR)/patch/Python/sitecustomize.$(os).py \ +$$(PYTHON_PLATFORM_SITECUSTOMIZE-$(target)): + @echo ">>> Create cross-plaform config for $(target)" + mkdir -p $$(PYTHON_PLATFORM_CONFIG-$(target)) + # Create the cross-platform site definition + echo "import _cross_$$(ARCH-$(target))_$$(SDK-$(target)); import _cross_venv;" \ + > $$(PYTHON_PLATFORM_CONFIG-$(target))/_cross_venv.pth + cp $(PROJECT_DIR)/patch/Python/make_cross_venv.py \ + $$(PYTHON_PLATFORM_CONFIG-$(target))/make_cross_venv.py + cp $(PROJECT_DIR)/patch/Python/_cross_venv.py \ + $$(PYTHON_PLATFORM_CONFIG-$(target))/_cross_venv.py + cp $$(PYTHON_STDLIB-$(target))/_sysconfig* \ + $$(PYTHON_PLATFORM_CONFIG-$(target)) + cat $(PROJECT_DIR)/patch/Python/_cross_target.py.tmpl \ | sed -e "s/{{os}}/$(os)/g" \ + | sed -e "s/{{platform}}/$$(OS_LOWER-$(target))/g" \ | sed -e "s/{{arch}}/$$(ARCH-$(target))/g" \ + | sed -e "s/{{sdk}}/$$(SDK-$(target))/g" \ | sed -e "s/{{version_min}}/$$(VERSION_MIN-$(os))/g" \ | sed -e "s/{{is_simulator}}/$$(IS_SIMULATOR-$(target))/g" \ - | sed -e "s/{{multiarch}}/$$(ARCH-$(target))-$$(SDK-$(target))/g" \ - | sed -e "s/{{tag}}/$$(OS_LOWER-$(target))-$$(VERSION_MIN-$(os))-$$(ARCH-$(target))-$$(SDK-$(target))/g" \ - > $$(PYTHON_SITECUSTOMIZE-$(target)) + > $$(PYTHON_PLATFORM_CONFIG-$(target))/_cross_$$(ARCH-$(target))_$$(SDK-$(target)).py + cat $(PROJECT_DIR)/patch/Python/sitecustomize.py.tmpl \ + | sed -e "s/{{arch}}/$$(ARCH-$(target))/g" \ + | sed -e "s/{{sdk}}/$$(SDK-$(target))/g" \ + > $$(PYTHON_PLATFORM_SITECUSTOMIZE-$(target)) -$(target): $$(PYTHON_SITECUSTOMIZE-$(target)) $$(PYTHON_LIB-$(target)) +endif + +$(target): $$(PYTHON_PLATFORM_SITECUSTOMIZE-$(target)) $$(PYTHON_LIB-$(target)) ########################################################################### # Target: Debug @@ -364,6 +379,8 @@ vars-$(target): @echo "PYTHON_BIN-$(target): $$(PYTHON_BIN-$(target))" @echo "PYTHON_INCLUDE-$(target): $$(PYTHON_INCLUDE-$(target))" @echo "PYTHON_STDLIB-$(target): $$(PYTHON_STDLIB-$(target))" + @echo "PYTHON_PLATFORM_CONFIG-$(target): $$(PYTHON_PLATFORM_CONFIG-$(target))" + @echo "PYTHON_PLATFORM_SITECUSTOMIZE-$(target): $$(PYTHON_PLATFORM_SITECUSTOMIZE-$(target))" @echo endef # build-target @@ -424,6 +441,7 @@ PYTHON_LIB-$(sdk)=$$(PYTHON_FRAMEWORK-$(sdk))/Python PYTHON_BIN-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/bin PYTHON_INCLUDE-$(sdk)=$$(PYTHON_FRAMEWORK-$(sdk))/Headers PYTHON_STDLIB-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/lib/python$(PYTHON_VER) +PYTHON_PLATFORM_CONFIG-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/platform-config $$(PYTHON_LIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_LIB-$$(target))) @echo ">>> Build Python fat library for the $(sdk) SDK" @@ -459,7 +477,7 @@ $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h: $$(PYTHON_LIB-$(sdk)) cp $$(PYTHON_SRCDIR-$$(firstword $$(SDK_TARGETS-$(sdk))))/$(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 +$$(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))) @echo ">>> Build Python stdlib for the $(sdk) SDK" mkdir -p $$(PYTHON_STDLIB-$(sdk))/lib-dynload # Copy stdlib from the first target associated with the $(sdk) SDK @@ -468,11 +486,17 @@ $$(PYTHON_STDLIB-$(sdk))/LICENSE.TXT: $$(PYTHON_LIB-$(sdk)) $$(PYTHON_FRAMEWORK- # Delete the single-SDK parts of the standard library rm -rf \ $$(PYTHON_STDLIB-$(sdk))/_sysconfigdata__*.py \ + $$(PYTHON_STDLIB-$(sdk))/_sysconfig_vars__*.json \ $$(PYTHON_STDLIB-$(sdk))/config-* \ $$(PYTHON_STDLIB-$(sdk))/lib-dynload/* # Copy the individual _sysconfigdata modules into names that include the architecture $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_STDLIB-$$(target))/_sysconfigdata_* $$(PYTHON_STDLIB-$(sdk))/; ) + $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_STDLIB-$$(target))/_sysconfig_vars_* $$(PYTHON_STDLIB-$(sdk))/; ) + + # Copy the platform site folders for each architecture + mkdir -p $$(PYTHON_PLATFORM_CONFIG-$(sdk)) + $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp -r $$(PYTHON_PLATFORM_CONFIG-$$(target)) $$(PYTHON_PLATFORM_CONFIG-$(sdk)); ) # Merge the binary modules from each target in the $(sdk) SDK into a single binary $$(foreach module,$$(wildcard $$(PYTHON_STDLIB-$$(firstword $$(SDK_TARGETS-$(sdk))))/lib-dynload/*),lipo -create -output $$(PYTHON_STDLIB-$(sdk))/lib-dynload/$$(notdir $$(module)) $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_STDLIB-$$(target))/lib-dynload/$$(notdir $$(module))); ) @@ -581,7 +605,7 @@ support/$(PYTHON_VER)/macOS/VERSIONS: dist/Python-$(PYTHON_VER)-macOS-support.$(BUILD_NUMBER).tar.gz: \ $$(PYTHON_XCFRAMEWORK-macOS)/Info.plist \ support/$(PYTHON_VER)/macOS/VERSIONS \ - $$(foreach target,$$(TARGETS-macOS), $$(PYTHON_SITECUSTOMIZE-$$(target))) + $$(foreach target,$$(TARGETS-macOS), $$(PYTHON_PLATFORM_SITECUSTOMIZE-$$(target))) @echo ">>> Create final distribution artefact for macOS" mkdir -p dist @@ -604,9 +628,7 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ $$(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)); ) $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/lib $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) - - @echo ">>> Create helper links in XCframework for $(os)" - $$(foreach sdk,$$(SDKS-$(os)),ln -si $$(SDK_SLICE-$$(sdk)) $$(PYTHON_XCFRAMEWORK-$(os))/$$(sdk); ) + $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/platform-config $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) ifeq ($(os),iOS) @echo ">>> Clone testbed project for $(os)" @@ -626,7 +648,7 @@ endif dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz: \ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist \ - $$(foreach target,$$(TARGETS-$(os)), $$(PYTHON_SITECUSTOMIZE-$$(target))) + $$(foreach target,$$(TARGETS-$(os)), $$(PYTHON_PLATFORM_SITECUSTOMIZE-$$(target))) @echo ">>> Create final distribution artefact for $(os)" mkdir -p dist diff --git a/patch/Python/_cross_target.py.tmpl b/patch/Python/_cross_target.py.tmpl new file mode 100644 index 00000000..9f75af53 --- /dev/null +++ b/patch/Python/_cross_target.py.tmpl @@ -0,0 +1,71 @@ +# A site package that turns a macOS virtual environment +# into an {{arch}} {{sdk}} cross-platform virtual environment +import platform +import subprocess +import sys +import sysconfig + +########################################################################### +# sys module patches +########################################################################### +sys.cross_compiling = True +sys.platform = "{{platform}}" +sys.implementation._multiarch = "{{arch}}-{{sdk}}" + +########################################################################### +# subprocess module patches +########################################################################### +subprocess._can_fork_exec = True + + +########################################################################### +# platform module patches +########################################################################### + +def cross_system(): + return "{{os}}" + + +def cross_uname(): + return platform.uname_result( + system="{{os}}", + node="build", + release="{{version_min}}", + version="", + machine="{{arch}}", + ) + + +def cross_ios_ver(system="", release="", model="", is_simulator=False): + if system == "": + system = "{{os}}" + if release == "": + release = "{{version_min}}" + if model == "": + model = "{{sdk}}" + + return platform.IOSVersionInfo(system, release, model, {{is_simulator}}) + + +platform.system = cross_system +platform.uname = cross_uname +platform.ios_ver = cross_ios_ver + + +########################################################################### +# sysconfig module patches +########################################################################### + +def cross_get_platform(): + return "{{platform}}-{{version_min}}-{{arch}}-{{sdk}}" + + +def cross_get_sysconfigdata_name(): + return "_sysconfigdata__{{platform}}_{{arch}}-{{sdk}}" + + +sysconfig.get_platform = cross_get_platform +sysconfig._get_sysconfigdata_name = cross_get_sysconfigdata_name + +# Force sysconfig data to be loaded (and cached). +sysconfig._init_config_vars() diff --git a/patch/Python/_cross_venv.py b/patch/Python/_cross_venv.py new file mode 100644 index 00000000..9caddf60 --- /dev/null +++ b/patch/Python/_cross_venv.py @@ -0,0 +1,105 @@ +import shutil +import sys +import sysconfig +from pathlib import Path + +SITE_PACKAGE_PATH = Path(__file__).parent + +########################################################################### +# importlib module patches +########################################################################### + + +def patch_env_create(env): + """ + Patch the process of creating virtual environments to ensure that the cross + environment modification files are also copied as part of environment + creation. + """ + old_pip_env_create = env._PipBackend.create + + def pip_env_create(self, path, *args, **kwargs): + result = old_pip_env_create(self, path, *args, **kwargs) + # Copy any _cross_*.pth or _cross_*.py file, plus the cross-platform + # sysconfigdata module and sysconfig_vars JSON to the new environment. + data_name = sysconfig._get_sysconfigdata_name() + json_name = data_name.replace("_sysconfigdata", "_sysconfig_vars") + for filename in [ + "_cross_venv.pth", + "_cross_venv.py", + f"_cross_{sys.implementation._multiarch.replace('-', '_')}.py", + f"{data_name}.py", + f"{json_name}.json", + ]: + src = SITE_PACKAGE_PATH / filename + target = Path(path) / src.relative_to( + SITE_PACKAGE_PATH.parent.parent.parent + ) + if not target.exists(): + shutil.copy(src, target) + return result + + env._PipBackend.create = pip_env_create + + +# Import hook that patches the creation of virtual environments by `build` +# +# The approach used here is the same as the one used by virtualenv to patch +# distutils (but without support for the older load_module API). +# https://docs.python.org/3/library/importlib.html#setting-up-an-importer +_BUILD_PATCH = ("build.env",) + + +class _Finder: + """A meta path finder that allows patching the imported build modules.""" + + fullname = None + + # lock[0] is threading.Lock(), but initialized lazily to avoid importing + # threading very early at startup, because there are gevent-based + # applications that need to be first to import threading by themselves. + # See https://github.com/pypa/virtualenv/issues/1895 for details. + lock = [] # noqa: RUF012 + + def find_spec(self, fullname, path, target=None): + if fullname in _BUILD_PATCH and self.fullname is None: + # initialize lock[0] lazily + if len(self.lock) == 0: + import threading + + lock = threading.Lock() + # there is possibility that two threads T1 and T2 are + # simultaneously running into find_spec, observing .lock as + # empty, and further going into hereby initialization. However + # due to the GIL, list.append() operation is atomic and this + # way only one of the threads will "win" to put the lock + # - that every thread will use - into .lock[0]. + # https://docs.python.org/3/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe + self.lock.append(lock) + + from functools import partial + from importlib.util import find_spec + + with self.lock[0]: + self.fullname = fullname + try: + spec = find_spec(fullname, path) + if spec is not None: + # https://www.python.org/dev/peps/pep-0451/#how-loading-will-work + old = spec.loader.exec_module + func = self.exec_module + if old is not func: + spec.loader.exec_module = partial(func, old) + return spec + finally: + self.fullname = None + return None + + @staticmethod + def exec_module(old, module): + old(module) + if module.__name__ in _BUILD_PATCH: + patch_env_create(module) + + +sys.meta_path.insert(0, _Finder()) diff --git a/patch/Python/make_cross_venv.py b/patch/Python/make_cross_venv.py new file mode 100644 index 00000000..f8dad270 --- /dev/null +++ b/patch/Python/make_cross_venv.py @@ -0,0 +1,144 @@ +import json +import pprint +import shutil +import sys +from pathlib import Path +from importlib import util as importlib_util + + +def localized_vars(orig_vars, slice_path): + """Update (where possible) any references to build-time variables with the + best guess of the installed location. + """ + # The host's sysconfigdata will include references to build-time variables. + # Update these to refer to the current known install location. + orig_prefix = orig_vars["prefix"] + localized_vars = {} + for key, value in orig_vars.items(): + final = value + if isinstance(value, str): + # Replace any reference to the build installation prefix + final = final.replace(orig_prefix, str(slice_path)) + # Replace any reference to the build-time Framework location + final = final.replace("-F .", f"-F {slice_path}") + localized_vars[key] = final + + return localized_vars + + +def localize_sysconfigdata(platform_config_path, venv_site_packages): + """Localize a sysconfigdata python module. + + :param platform_config_path: The platform config that contains the + sysconfigdata module to localize. + :param venv_site_packages: The site packages folder where the localized + sysconfigdata module should be output. + """ + # Find the "_sysconfigdata_*.py" file in the platform config + sysconfigdata_path = next(platform_config_path.glob("_sysconfigdata_*.py")) + + # Import the sysconfigdata module + spec = importlib_util.spec_from_file_location( + sysconfigdata_path.stem, + sysconfigdata_path + ) + if spec is None: + msg = f"Unable to load spec for {sysconfigdata_path}" + raise ValueError(msg) + if spec.loader is None: + msg = f"Spec for {sysconfigdata_path} does not define a loader" + raise ValueError(msg) + sysconfigdata = importlib_util.module_from_spec(spec) + spec.loader.exec_module(sysconfigdata) + + # Write the updated sysconfigdata module into the cross-platform site. + slice_path = sysconfigdata_path.parent.parent.parent + with (venv_site_packages / sysconfigdata_path.name).open("w") as f: + f.write(f"# Generated from {sysconfigdata_path}\n") + f.write("build_time_vars = ") + pprint.pprint( + localized_vars(sysconfigdata.build_time_vars, slice_path), + stream=f, + compact=True + ) + + +def localize_sysconfig_vars(platform_config_path, venv_site_packages): + """Localize a sysconfig_vars.json file. + + :param platform_config_path: The platform config that contains the + sysconfigdata module to localize. + :param venv_site_packages: The site-packages folder where the localized + sysconfig_vars.json file should be output. + """ + # Find the "_sysconfig_vars_*.json" file in the platform config + sysconfig_vars_path = next(platform_config_path.glob("_sysconfig_vars_*.json")) + + with sysconfig_vars_path.open("rb") as f: + build_time_vars = json.load(f) + + slice_path = sysconfig_vars_path.parent.parent.parent + with (venv_site_packages / sysconfig_vars_path.name).open("w") as f: + json.dump(localized_vars(build_time_vars, slice_path), f, indent=2) + + +def make_cross_venv(venv_path: Path, platform_config_path: Path): + """Convert a virtual environment into a cross-platform environment. + + :param venv_path: The path to the root of the venv. + :param platform_config_path: The path containing the platform config. + """ + if not venv_path.exists(): + raise ValueError(f"Virtual environment {venv_path} does not exist.") + if not (venv_path / "bin/python3").exists(): + raise ValueError(f"{venv_path} does not appear to be a virtual environment.") + + print( + f"Converting {venv_path} into a {platform_config_path.name} environment... ", + end="", + ) + + LIB_PATH = f"lib/python{sys.version_info[0]}.{sys.version_info[1]}" + + # Update path references in the sysconfigdata to reflect local conditions. + venv_site_packages = venv_path / LIB_PATH / "site-packages" + localize_sysconfigdata(platform_config_path, venv_site_packages) + localize_sysconfig_vars(platform_config_path, venv_site_packages) + + # Copy in the site-package environment modifications. + cross_multiarch = f"_cross_{platform_config_path.name.replace('-', '_')}" + shutil.copy( + platform_config_path / f"{cross_multiarch}.py", + venv_site_packages / f"{cross_multiarch}.py", + ) + shutil.copy( + platform_config_path / "_cross_venv.py", + venv_site_packages / "_cross_venv.py", + ) + # Write the .pth file that will enable the cross-env modifications + (venv_site_packages / "_cross_venv.pth").write_text( + f"import {cross_multiarch}; import _cross_venv\n" + ) + + print("done.") + + +if __name__ == "__main__": + try: + platform_config_path = Path(sys.argv[2]).resolve() + except IndexError: + platform_config_path = Path(__file__).parent + + try: + venv_path = Path(sys.argv[1]).resolve() + make_cross_venv(venv_path, platform_config_path) + except IndexError: + print(""" +Convert a virtual environment in to a cross-platform environment. + +Usage: + make_cross_venv () + +If an explicit platform config isn't provided, it is assumed the directory +containing the make_cross_venv script *is* a platform config. +""") diff --git a/patch/Python/sitecustomize.iOS.py b/patch/Python/sitecustomize.iOS.py deleted file mode 100644 index ccc291f3..00000000 --- a/patch/Python/sitecustomize.iOS.py +++ /dev/null @@ -1,114 +0,0 @@ -# A site customization that can be used to trick pip into installing -# packages cross-platform. If the folder containing this file is on -# your PYTHONPATH when you invoke pip, pip will behave as if it were -# running on {{os}}. -import collections -import distutils.ccompiler -import distutils.unixccompiler -import os -import platform -import sys -import sysconfig -import types - -# Make platform.system() return "{{os}}" -def custom_system(): - return "{{os}}" - -platform.system = custom_system - -# Make platform.ios_ver() return an appropriate namedtuple -IOSVersionInfo = collections.namedtuple( - "IOSVersionInfo", - ["system", "release", "model", "is_simulator"] -) - -def custom_ios_ver(system="", release="", model="", is_simulator=False): - return IOSVersionInfo("{{os}}", "{{version_min}}", "iPhone", {{is_simulator}}) - -platform.ios_ver = custom_ios_ver - -# Make sys.implementation._multiarch return the multiarch description -sys.implementation._multiarch = "{{multiarch}}" - -# Make sysconfig.get_platform() return the platform tag -def custom_get_platform(): - return "{{tag}}" - -sysconfig.get_platform = custom_get_platform - -# Make distutils raise errors if you try to use it to build modules. -DISABLED_COMPILER_ERROR = "Cannot compile native modules" - -distutils.ccompiler.get_default_compiler = lambda *args, **kwargs: "disabled" -distutils.ccompiler.compiler_class["disabled"] = ( - "disabledcompiler", - "DisabledCompiler", - "Compiler disabled ({})".format(DISABLED_COMPILER_ERROR), -) - - -def disabled_compiler(prefix): - # No need to give any more advice here: that will come from the higher-level code in pip. - from distutils.errors import DistutilsPlatformError - - raise DistutilsPlatformError("{}: {}".format(prefix, DISABLED_COMPILER_ERROR)) - - -class DisabledCompiler(distutils.ccompiler.CCompiler): - compiler_type = "disabled" - - def preprocess(*args, **kwargs): - disabled_compiler("CCompiler.preprocess") - - def compile(*args, **kwargs): - disabled_compiler("CCompiler.compile") - - def create_static_lib(*args, **kwargs): - disabled_compiler("CCompiler.create_static_lib") - - def link(*args, **kwargs): - disabled_compiler("CCompiler.link") - - -# To maximize the chance of the build getting as far as actually calling compile(), make -# sure the class has all of the expected attributes. -for name in [ - "src_extensions", - "obj_extension", - "static_lib_extension", - "shared_lib_extension", - "static_lib_format", - "shared_lib_format", - "exe_extension", -]: - setattr( - DisabledCompiler, name, getattr(distutils.unixccompiler.UnixCCompiler, name) - ) - -DisabledCompiler.executables = { - name: [DISABLED_COMPILER_ERROR.replace(" ", "_")] - for name in distutils.unixccompiler.UnixCCompiler.executables -} - -disabled_mod = types.ModuleType("distutils.disabledcompiler") -disabled_mod.DisabledCompiler = DisabledCompiler -sys.modules["distutils.disabledcompiler"] = disabled_mod - - -# Try to disable native builds for packages which don't use the distutils native build -# system at all (e.g. uwsgi), or only use it to wrap an external build script (e.g. pynacl). -for tool in ["ar", "as", "cc", "cxx", "ld"]: - os.environ[tool.upper()] = DISABLED_COMPILER_ERROR.replace(" ", "_") - - -# Call the next sitecustomize script if there is one -# (https://nedbatchelder.com/blog/201001/running_code_at_python_startup.html). -del sys.modules["sitecustomize"] -this_dir = os.path.dirname(__file__) -path_index = sys.path.index(this_dir) -del sys.path[path_index] -try: - import sitecustomize # noqa: F401 -finally: - sys.path.insert(path_index, this_dir) diff --git a/patch/Python/sitecustomize.macOS.py b/patch/Python/sitecustomize.macOS.py deleted file mode 100644 index 500714da..00000000 --- a/patch/Python/sitecustomize.macOS.py +++ /dev/null @@ -1,14 +0,0 @@ -# A site customization that can be used to trick pip into installing -# packages cross-platform. If the folder containing this file is on -# your PYTHONPATH when you invoke pip, pip will behave as if it were -# running on {{arch}}. -import platform - -# Make platform.mac_ver() return {{arch}} -orig_mac_ver = platform.mac_ver - -def custom_mac_ver(): - orig = orig_mac_ver() - return orig[0], orig[1], "{{arch}}" - -platform.mac_ver = custom_mac_ver diff --git a/patch/Python/sitecustomize.py.tmpl b/patch/Python/sitecustomize.py.tmpl new file mode 100644 index 00000000..0330575a --- /dev/null +++ b/patch/Python/sitecustomize.py.tmpl @@ -0,0 +1,22 @@ +# A site customization that can be used to trick pip into installing packages +# cross-platform. If the folder containing this file is on your PYTHONPATH when +# you invoke python, the interpreter will behave as if it were running on +# {{arch}} {{sdk}}. +import sys +import os + +# Apply the cross-platform patch +import _cross_{{arch}}_{{sdk}} +import _cross_venv + + +# Call the next sitecustomize script if there is one +# (https://nedbatchelder.com/blog/201001/running_code_at_python_startup.html). +del sys.modules["sitecustomize"] +this_dir = os.path.dirname(__file__) +path_index = sys.path.index(this_dir) +del sys.path[path_index] +try: + import sitecustomize # noqa: F401 +finally: + sys.path.insert(path_index, this_dir) diff --git a/patch/Python/sitecustomize.tvOS.py b/patch/Python/sitecustomize.tvOS.py deleted file mode 100644 index d7d86e36..00000000 --- a/patch/Python/sitecustomize.tvOS.py +++ /dev/null @@ -1,99 +0,0 @@ -# A site customization that can be used to trick pip into installing -# packages cross-platform. If the folder containing this file is on -# your PYTHONPATH when you invoke pip, pip will behave as if it were -# running on {{os}}. -import distutils.ccompiler -import distutils.unixccompiler -import os -import platform -import sys -import sysconfig -import types - -# Make platform.system() return "{{os}}" -def custom_system(): - return "{{os}}" - -platform.system = custom_system - -# Make sysconfig.get_platform() return "{{tag}}" -def custom_get_platform(): - return "{{tag}}" - -sysconfig.get_platform = custom_get_platform - -# Make distutils raise errors if you try to use it to build modules. -DISABLED_COMPILER_ERROR = "Cannot compile native modules" - -distutils.ccompiler.get_default_compiler = lambda *args, **kwargs: "disabled" -distutils.ccompiler.compiler_class["disabled"] = ( - "disabledcompiler", - "DisabledCompiler", - "Compiler disabled ({})".format(DISABLED_COMPILER_ERROR), -) - - -def disabled_compiler(prefix): - # No need to give any more advice here: that will come from the higher-level code in pip. - from distutils.errors import DistutilsPlatformError - - raise DistutilsPlatformError("{}: {}".format(prefix, DISABLED_COMPILER_ERROR)) - - -class DisabledCompiler(distutils.ccompiler.CCompiler): - compiler_type = "disabled" - - def preprocess(*args, **kwargs): - disabled_compiler("CCompiler.preprocess") - - def compile(*args, **kwargs): - disabled_compiler("CCompiler.compile") - - def create_static_lib(*args, **kwargs): - disabled_compiler("CCompiler.create_static_lib") - - def link(*args, **kwargs): - disabled_compiler("CCompiler.link") - - -# To maximize the chance of the build getting as far as actually calling compile(), make -# sure the class has all of the expected attributes. -for name in [ - "src_extensions", - "obj_extension", - "static_lib_extension", - "shared_lib_extension", - "static_lib_format", - "shared_lib_format", - "exe_extension", -]: - setattr( - DisabledCompiler, name, getattr(distutils.unixccompiler.UnixCCompiler, name) - ) - -DisabledCompiler.executables = { - name: [DISABLED_COMPILER_ERROR.replace(" ", "_")] - for name in distutils.unixccompiler.UnixCCompiler.executables -} - -disabled_mod = types.ModuleType("distutils.disabledcompiler") -disabled_mod.DisabledCompiler = DisabledCompiler -sys.modules["distutils.disabledcompiler"] = disabled_mod - - -# Try to disable native builds for packages which don't use the distutils native build -# system at all (e.g. uwsgi), or only use it to wrap an external build script (e.g. pynacl). -for tool in ["ar", "as", "cc", "cxx", "ld"]: - os.environ[tool.upper()] = DISABLED_COMPILER_ERROR.replace(" ", "_") - - -# Call the next sitecustomize script if there is one -# (https://nedbatchelder.com/blog/201001/running_code_at_python_startup.html). -del sys.modules["sitecustomize"] -this_dir = os.path.dirname(__file__) -path_index = sys.path.index(this_dir) -del sys.path[path_index] -try: - import sitecustomize # noqa: F401 -finally: - sys.path.insert(path_index, this_dir) diff --git a/patch/Python/sitecustomize.watchOS.py b/patch/Python/sitecustomize.watchOS.py deleted file mode 100644 index d7d86e36..00000000 --- a/patch/Python/sitecustomize.watchOS.py +++ /dev/null @@ -1,99 +0,0 @@ -# A site customization that can be used to trick pip into installing -# packages cross-platform. If the folder containing this file is on -# your PYTHONPATH when you invoke pip, pip will behave as if it were -# running on {{os}}. -import distutils.ccompiler -import distutils.unixccompiler -import os -import platform -import sys -import sysconfig -import types - -# Make platform.system() return "{{os}}" -def custom_system(): - return "{{os}}" - -platform.system = custom_system - -# Make sysconfig.get_platform() return "{{tag}}" -def custom_get_platform(): - return "{{tag}}" - -sysconfig.get_platform = custom_get_platform - -# Make distutils raise errors if you try to use it to build modules. -DISABLED_COMPILER_ERROR = "Cannot compile native modules" - -distutils.ccompiler.get_default_compiler = lambda *args, **kwargs: "disabled" -distutils.ccompiler.compiler_class["disabled"] = ( - "disabledcompiler", - "DisabledCompiler", - "Compiler disabled ({})".format(DISABLED_COMPILER_ERROR), -) - - -def disabled_compiler(prefix): - # No need to give any more advice here: that will come from the higher-level code in pip. - from distutils.errors import DistutilsPlatformError - - raise DistutilsPlatformError("{}: {}".format(prefix, DISABLED_COMPILER_ERROR)) - - -class DisabledCompiler(distutils.ccompiler.CCompiler): - compiler_type = "disabled" - - def preprocess(*args, **kwargs): - disabled_compiler("CCompiler.preprocess") - - def compile(*args, **kwargs): - disabled_compiler("CCompiler.compile") - - def create_static_lib(*args, **kwargs): - disabled_compiler("CCompiler.create_static_lib") - - def link(*args, **kwargs): - disabled_compiler("CCompiler.link") - - -# To maximize the chance of the build getting as far as actually calling compile(), make -# sure the class has all of the expected attributes. -for name in [ - "src_extensions", - "obj_extension", - "static_lib_extension", - "shared_lib_extension", - "static_lib_format", - "shared_lib_format", - "exe_extension", -]: - setattr( - DisabledCompiler, name, getattr(distutils.unixccompiler.UnixCCompiler, name) - ) - -DisabledCompiler.executables = { - name: [DISABLED_COMPILER_ERROR.replace(" ", "_")] - for name in distutils.unixccompiler.UnixCCompiler.executables -} - -disabled_mod = types.ModuleType("distutils.disabledcompiler") -disabled_mod.DisabledCompiler = DisabledCompiler -sys.modules["distutils.disabledcompiler"] = disabled_mod - - -# Try to disable native builds for packages which don't use the distutils native build -# system at all (e.g. uwsgi), or only use it to wrap an external build script (e.g. pynacl). -for tool in ["ar", "as", "cc", "cxx", "ld"]: - os.environ[tool.upper()] = DISABLED_COMPILER_ERROR.replace(" ", "_") - - -# Call the next sitecustomize script if there is one -# (https://nedbatchelder.com/blog/201001/running_code_at_python_startup.html). -del sys.modules["sitecustomize"] -this_dir = os.path.dirname(__file__) -path_index = sys.path.index(this_dir) -del sys.path[path_index] -try: - import sitecustomize # noqa: F401 -finally: - sys.path.insert(path_index, this_dir) From 5cd3512edbcf817ea9adbcfb89473e83981cd2d8 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 14 Feb 2025 16:36:48 +0800 Subject: [PATCH 071/113] Improve support for PythonKit, and update usage guide (#248) * Add a Swift modulemap to the generated frameworks. * Updates usage information to reflect current package structure. --- Makefile | 6 ++++++ USAGE.md | 36 +++++++++++++++-------------------- patch/Python/module.modulemap | 5 +++++ 3 files changed, 26 insertions(+), 21 deletions(-) create mode 100644 patch/Python/module.modulemap diff --git a/Makefile b/Makefile index 765bf39f..b0ba7299 100644 --- a/Makefile +++ b/Makefile @@ -466,6 +466,9 @@ $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h: $$(PYTHON_LIB-$(sdk)) # Copy headers as-is from the first target in the $(sdk) SDK cp -r $$(PYTHON_INCLUDE-$$(firstword $$(SDK_TARGETS-$(sdk)))) $$(PYTHON_INCLUDE-$(sdk)) + # Copy in the modulemap file + cp -r patch/Python/module.modulemap $$(PYTHON_INCLUDE-$(sdk)) + # Link the PYTHONHOME version of the headers mkdir -p $$(PYTHON_INSTALL-$(sdk))/include ln -si ../Python.framework/Headers $$(PYTHON_INSTALL-$(sdk))/include/python$(PYTHON_VER) @@ -582,6 +585,9 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ # Rewrite the framework to make it standalone patch/make-relocatable.sh $$(PYTHON_INSTALL_VERSION-macosx) 2>&1 > /dev/null + # Copy in the modulemap file + cp -r patch/Python/module.modulemap $$(PYTHON_FRAMEWORK-macosx)/Headers + # Re-apply the signature on the binaries. codesign -s - --preserve-metadata=identifier,entitlements,flags,runtime -f $$(PYTHON_LIB-macosx) \ 2>&1 | tee $$(PYTHON_INSTALL-macosx)/python-$(os).codesign.log diff --git a/USAGE.md b/USAGE.md index 12bc1dc7..40bbcf80 100644 --- a/USAGE.md +++ b/USAGE.md @@ -20,7 +20,7 @@ what Briefcase is doing). The steps required are documented in the CPython usage guides: * [macOS](https://docs.python.org/3/using/mac.html) -* [iOS](https://docs.python.org/3.14/using/ios.html) +* [iOS](https://docs.python.org/3/using/ios.html#adding-python-to-an-ios-project) For tvOS and watchOS, you should be able to broadly follow the instructions in the iOS guide. @@ -29,7 +29,7 @@ the iOS guide. There are 2 ways to access the Python runtime in your project code. -### Embedded C API. +### Embedded C API You can use the [Python Embedded C API](https://docs.python.org/3/extending/embedding.html) to instantiate a Python @@ -43,37 +43,31 @@ An alternate approach is to use [PythonKit](https://github.com/pvieito/PythonKit). PythonKit is a package that provides a Swift API to running Python code. -To use PythonKit in your project: +To use PythonKit in your project, add the Python Apple Support package to your +project as described above; then: 1. Add PythonKit to your project using the Swift Package manager. See the PythonKit documentation for details. -2. Create a file called `module.modulemap` inside - `Python.xcframework/macos-arm64_x86_64/Headers/`, containing the following - code: -``` -module Python { - umbrella header "Python.h" - export * - link "Python" -} -``` - -3. In your Swift code, initialize the Python runtime. This should generally be +2. In your Swift code, initialize the Python runtime. This should generally be done as early as possible in the application's lifecycle, but definitely - needs to be done before you invoke Python code: + needs to be done before you invoke Python code. References to a specific + Python version should reflect the version of Python you are using: ```swift import Python -guard let stdLibPath = Bundle.main.path(forResource: "python-stdlib", ofType: nil) else { return } -guard let libDynloadPath = Bundle.main.path(forResource: "python-stdlib/lib-dynload", ofType: nil) else { return } -setenv("PYTHONHOME", stdLibPath, 1) -setenv("PYTHONPATH", "\(stdLibPath):\(libDynloadPath)", 1) +guard let pythonHome = Bundle.main.path(forResource: "python", ofType: nil) else { return } +guard let pythonPath = Bundle.main.path(forResource: "python/lib/python3.13", ofType: nil) else { return } +guard let libDynloadPath = Bundle.main.path(forResource: "python/lib/python3.13/lib-dynload", ofType: nil) else { return } +let appPath = Bundle.main.path(forResource: "app", ofType: nil) + +setenv("PYTHONHOME", pythonHome, 1) +setenv("PYTHONPATH", [pythonPath, libDynloadPath, appPath].compactMap { $0 }.joined(separator: ":"), 1) Py_Initialize() // we now have a Python interpreter ready to be used ``` -5. Invoke Python code in your app. For example: +3. Invoke Python code in your app. For example: ```swift import PythonKit diff --git a/patch/Python/module.modulemap b/patch/Python/module.modulemap new file mode 100644 index 00000000..9a4dcbb8 --- /dev/null +++ b/patch/Python/module.modulemap @@ -0,0 +1,5 @@ +module Python { + umbrella header "Python.h" + export * + link "Python" +} From 3a7cd861f523cd5a80b9ba1d452278cdc138cfd8 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 17 Feb 2025 14:38:25 +0800 Subject: [PATCH 072/113] More updates to USAGE and README (#250) * Adds details on `python-config` and `testbed` * Adds a bare bones interpreter instantiation example * Clarifies the difference between instantiating the interpreter, and accessing the Python API with PythonKit * Adds header exclusions to the modulemap to silence build warnings. --- README.rst | 35 ++++++-- USAGE.md | 113 ++++++++++++++++-------- patch/Python/module.modulemap | 160 ++++++++++++++++++++++++++++++++++ 3 files changed, 265 insertions(+), 43 deletions(-) diff --git a/README.rst b/README.rst index 94c0ae94..2317b749 100644 --- a/README.rst +++ b/README.rst @@ -83,15 +83,6 @@ Each support package contains: * ``VERSIONS``, a text file describing the specific versions of code used to build the support package; -* ``platform-site``, a folder that contains site customization scripts that can be used - to make your local Python install look like it is an on-device install for each of the - underlying target architectures supported by the platform. This is needed because when - you run ``pip`` you'll be on a macOS machine with a specific architecture; if ``pip`` - tries to install a binary package, it will install a macOS binary wheel (which won't - work on iOS/tvOS/watchOS). However, if you add the ``platform-site`` folder to your - ``PYTHONPATH`` when invoking pip, the site customization will make your Python install - return ``platform`` and ``sysconfig`` responses consistent with on-device behavior, - which will cause ``pip`` to install platform-appropriate packages. * ``Python.xcframework``, a multi-architecture build of the Python runtime library On iOS/tvOS/watchOS, the ``Python.xcframework`` contains a @@ -105,6 +96,32 @@ needed to build packages. This is required because Xcode uses the ``xcrun`` alias to dynamically generate the name of binaries, but a lot of C tooling expects that ``CC`` will not contain spaces. +Each slice of an iOS/tvOS/watchOS XCframework also contains a +``platform-config`` folder with a subfolder for each supported architecture in +that slice. These subfolders can be used to make a macOS Python environment +behave as if it were on an iOS/tvOS/watchOS device. This works in one of two +ways: + +1. **A sitecustomize.py script**. If the ``platform-config`` subfolder is on + your ``PYTHONPATH`` when a Python interpreter is started, a site + customization will be applied that patches methods in ``sys``, ``sysconfig`` + and ``platform`` that are used to identify the system. + +2. **A make_cross_venv.py script**. If you call ``make_cross_venv.py``, + providing the location of a virtual environment, the script will add some + files to the ``site-packages`` folder of that environment that will + automatically apply the same set of patches as the ``sitecustomize.py`` + script whenever the environment is activated, without any need to modify + ``PYTHONPATH``. If you use ``build`` to create an isolated PEP 517 + environment to build a wheel, these patches will also be applied to the + isolated build environment that is created. + +iOS distributions also contain a copy of the iOS ``testbed`` project - an Xcode +project that can be used to run test suites of Python code. See the `CPython +documentation on testing packages +`__ for +details on how to use this testbed. + For a detailed instructions on using the support package in your own project, see the `usage guide <./USAGE.md>`__ diff --git a/USAGE.md b/USAGE.md index 40bbcf80..096f71b0 100644 --- a/USAGE.md +++ b/USAGE.md @@ -25,6 +25,75 @@ guides: For tvOS and watchOS, you should be able to broadly follow the instructions in the iOS guide. +### Using Objective C + +Once you've added the Python XCframework to your project, you'll need to +initialize the Python runtime in your Objective C code (This is step 10 of the +iOS guide linked above). This initialization should generally be done as early +as possible in the application's lifecycle, but definitely needs to be done +before you invoke Python code. + +As a *bare minimum*, you can do the following: + +1. Import the Python C API headers: + ```objc + #include + ``` + +2. Initialize the Python interpreter: + ```objc + NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; + NSString *pythonHome = [NSString stringWithFormat:@"%@/python", resourcePath, nil]; + NSString *pythonPath = [NSString stringWithFormat:@"%@/lib/python3.13", python_home, nil]; + NSString *libDynloadPath = [NSString stringWithFormat:@"%@/lib/python3.13/lib-dynload", python_home, nil]; + NSString *appPath = [NSString stringWithFormat:@"%@/app", resourcePath, nil]; + + setenv("PYTHONHOME", pythonHome, 1); + setenv("PYTHONPATH", [NSString stringWithFormat:@"%@:%@:%@", pythonpath, libDynloadPath, appPath, nil]); + + Py_Initialize(); + + // we now have a Python interpreter ready to be used + ``` + References to a specific Python version should reflect the version of + Python you are using. + +Again - this is the *bare minimum* initialization. In practice, you will likely +need to configure other aspects of the Python interpreter using the +`PyPreConfig` and `PyConfig` mechanisms. Consult the [Python documentation on +interpreter configuration](https://docs.python.org/3/c-api/init_config.html) for +more details on the configuration options that are available. You may find the +[bootstrap mainline code used by +Briefcase](https://github.com/beeware/briefcase-iOS-Xcode-template/blob/main/%7B%7B%20cookiecutter.format%20%7D%7D/%7B%7B%20cookiecutter.class_name%20%7D%7D/main.m) +a helpful point of comparison. + +### Using Swift + +If you want to use Swift instead of Objective C, the bare minimum initialization +code will look something like this: + +1. Import the Python framework: + ```swift + import Python + ``` + +2. Initialize the Python interpreter: + ```swift + guard let pythonHome = Bundle.main.path(forResource: "python", ofType: nil) else { return } + guard let pythonPath = Bundle.main.path(forResource: "python/lib/python3.13", ofType: nil) else { return } + guard let libDynloadPath = Bundle.main.path(forResource: "python/lib/python3.13/lib-dynload", ofType: nil) else { return } + let appPath = Bundle.main.path(forResource: "app", ofType: nil) + + setenv("PYTHONHOME", pythonHome, 1) + setenv("PYTHONPATH", [pythonPath, libDynloadPath, appPath].compactMap { $0 }.joined(separator: ":"), 1) + Py_Initialize() + // we now have a Python interpreter ready to be used + ``` + + Again, references to a specific Python version should reflect the version of + Python you are using; and you will likely need to use `PyPreConfig` and + `PreConfig` APIs. + ## Accessing the Python runtime There are 2 ways to access the Python runtime in your project code. @@ -32,53 +101,29 @@ There are 2 ways to access the Python runtime in your project code. ### Embedded C API You can use the [Python Embedded C -API](https://docs.python.org/3/extending/embedding.html) to instantiate a Python -interpreter. This is the approach taken by Briefcase; you may find the bootstrap -mainline code generated by Briefcase a helpful guide to what is needed to start -an interpreter and run Python code. +API](https://docs.python.org/3/extending/embedding.html) to invoke Python code +and interact with Python objects. This is a raw C API that is accesible to both +Objective C and Swift. ### PythonKit -An alternate approach is to use +If you're using Swift, an alternate approach is to use [PythonKit](https://github.com/pvieito/PythonKit). PythonKit is a package that provides a Swift API to running Python code. To use PythonKit in your project, add the Python Apple Support package to your -project as described above; then: - -1. Add PythonKit to your project using the Swift Package manager. See the - PythonKit documentation for details. +project and instantiate a Python interpreter as described above; then add +PythonKit to your project using the Swift Package manager (see the [PythonKit +documentation](https://github.com/pvieito/PythonKit) for details). -2. In your Swift code, initialize the Python runtime. This should generally be - done as early as possible in the application's lifecycle, but definitely - needs to be done before you invoke Python code. References to a specific - Python version should reflect the version of Python you are using: +Once you've done this, you can import PythonKit: ```swift -import Python - -guard let pythonHome = Bundle.main.path(forResource: "python", ofType: nil) else { return } -guard let pythonPath = Bundle.main.path(forResource: "python/lib/python3.13", ofType: nil) else { return } -guard let libDynloadPath = Bundle.main.path(forResource: "python/lib/python3.13/lib-dynload", ofType: nil) else { return } -let appPath = Bundle.main.path(forResource: "app", ofType: nil) - -setenv("PYTHONHOME", pythonHome, 1) -setenv("PYTHONPATH", [pythonPath, libDynloadPath, appPath].compactMap { $0 }.joined(separator: ":"), 1) -Py_Initialize() -// we now have a Python interpreter ready to be used +import PythonKit ``` - -3. Invoke Python code in your app. For example: +and use the PythonKit Swift API to interact with Python code: ```swift -import PythonKit - let sys = Python.import("sys") print("Python Version: \(sys.version_info.major).\(sys.version_info.minor)") print("Python Encoding: \(sys.getdefaultencoding().upper())") print("Python Path: \(sys.path)") - -_ = Python.import("math") // verifies `lib-dynload` is found and signed successfully ``` - -To integrate 3rd party python code and dependencies, you will need to make sure -`PYTHONPATH` contains their paths; once this has been done, you can run -`Python.import("")`. to import that module from inside swift. diff --git a/patch/Python/module.modulemap b/patch/Python/module.modulemap index 9a4dcbb8..96b05fc6 100644 --- a/patch/Python/module.modulemap +++ b/patch/Python/module.modulemap @@ -2,4 +2,164 @@ module Python { umbrella header "Python.h" export * link "Python" + + exclude header "datetime.h" + exclude header "dynamic_annotations.h" + exclude header "errcode.h" + exclude header "frameobject.h" + exclude header "marshal.h" + exclude header "opcode_ids.h" + exclude header "opcode.h" + exclude header "osdefs.h" + exclude header "py_curses.h" + exclude header "pyconfig-arm32_64.h" + exclude header "pyconfig-arm64.h" + exclude header "pyconfig-x86_64.h" + exclude header "pydtrace.h" + exclude header "pyexpat.h" + exclude header "structmember.h" + + exclude header "cpython/frameobject.h" + exclude header "cpython/pthread_stubs.h" + exclude header "cpython/pyatomic_msc.h" + exclude header "cpython/pyatomic_std.h" + exclude header "cpython/pystats.h" + + exclude header "internal/mimalloc/mimalloc.h" + exclude header "internal/mimalloc/mimalloc/atomic.h" + exclude header "internal/mimalloc/mimalloc/internal.h" + exclude header "internal/mimalloc/mimalloc/prim.h" + exclude header "internal/mimalloc/mimalloc/track.h" + exclude header "internal/mimalloc/mimalloc/types.h" + + exclude header "internal/pycore_abstract.h" + exclude header "internal/pycore_asdl.h" + exclude header "internal/pycore_ast_state.h" + exclude header "internal/pycore_ast.h" + exclude header "internal/pycore_atexit.h" + exclude header "internal/pycore_audit.h" + exclude header "internal/pycore_backoff.h" + exclude header "internal/pycore_bitutils.h" + exclude header "internal/pycore_blocks_output_buffer.h" + exclude header "internal/pycore_brc.h" + exclude header "internal/pycore_bytes_methods.h" + exclude header "internal/pycore_bytesobject.h" + exclude header "internal/pycore_call.h" + exclude header "internal/pycore_capsule.h" + exclude header "internal/pycore_cell.h" + exclude header "internal/pycore_ceval_state.h" + exclude header "internal/pycore_ceval.h" + exclude header "internal/pycore_code.h" + exclude header "internal/pycore_codecs.h" + exclude header "internal/pycore_compile.h" + exclude header "internal/pycore_complexobject.h" + exclude header "internal/pycore_condvar.h" + exclude header "internal/pycore_context.h" + exclude header "internal/pycore_critical_section.h" + exclude header "internal/pycore_crossinterp_data_registry.h" + exclude header "internal/pycore_crossinterp.h" + exclude header "internal/pycore_debug_offsets.h" + exclude header "internal/pycore_descrobject.h" + exclude header "internal/pycore_dict_state.h" + exclude header "internal/pycore_dict.h" + exclude header "internal/pycore_dtoa.h" + exclude header "internal/pycore_emscripten_signal.h" + exclude header "internal/pycore_emscripten_trampoline.h" + exclude header "internal/pycore_exceptions.h" + exclude header "internal/pycore_faulthandler.h" + exclude header "internal/pycore_fileutils_windows.h" + exclude header "internal/pycore_fileutils.h" + exclude header "internal/pycore_floatobject.h" + exclude header "internal/pycore_flowgraph.h" + exclude header "internal/pycore_format.h" + exclude header "internal/pycore_frame.h" + exclude header "internal/pycore_freelist_state.h" + exclude header "internal/pycore_freelist.h" + exclude header "internal/pycore_function.h" + exclude header "internal/pycore_gc.h" + exclude header "internal/pycore_genobject.h" + exclude header "internal/pycore_getopt.h" + exclude header "internal/pycore_gil.h" + exclude header "internal/pycore_global_objects_fini_generated.h" + exclude header "internal/pycore_global_objects.h" + exclude header "internal/pycore_global_strings.h" + exclude header "internal/pycore_hamt.h" + exclude header "internal/pycore_hashtable.h" + exclude header "internal/pycore_identifier.h" + exclude header "internal/pycore_import.h" + exclude header "internal/pycore_importdl.h" + exclude header "internal/pycore_index_pool.h" + exclude header "internal/pycore_initconfig.h" + exclude header "internal/pycore_instruction_sequence.h" + exclude header "internal/pycore_instruments.h" + exclude header "internal/pycore_interp.h" + exclude header "internal/pycore_intrinsics.h" + exclude header "internal/pycore_jit.h" + exclude header "internal/pycore_list.h" + exclude header "internal/pycore_llist.h" + exclude header "internal/pycore_lock.h" + exclude header "internal/pycore_long.h" + exclude header "internal/pycore_magic_number.h" + exclude header "internal/pycore_memoryobject.h" + exclude header "internal/pycore_mimalloc.h" + exclude header "internal/pycore_modsupport.h" + exclude header "internal/pycore_moduleobject.h" + exclude header "internal/pycore_namespace.h" + exclude header "internal/pycore_object_alloc.h" + exclude header "internal/pycore_object_deferred.h" + exclude header "internal/pycore_object_stack.h" + exclude header "internal/pycore_object_state.h" + exclude header "internal/pycore_object.h" + exclude header "internal/pycore_obmalloc_init.h" + exclude header "internal/pycore_obmalloc.h" + exclude header "internal/pycore_opcode_metadata.h" + exclude header "internal/pycore_opcode_utils.h" + exclude header "internal/pycore_optimizer.h" + exclude header "internal/pycore_parking_lot.h" + exclude header "internal/pycore_parser.h" + exclude header "internal/pycore_pathconfig.h" + exclude header "internal/pycore_pyarena.h" + exclude header "internal/pycore_pyatomic_ft_wrappers.h" + exclude header "internal/pycore_pybuffer.h" + exclude header "internal/pycore_pyerrors.h" + exclude header "internal/pycore_pyhash.h" + exclude header "internal/pycore_pylifecycle.h" + exclude header "internal/pycore_pymath.h" + exclude header "internal/pycore_pymem_init.h" + exclude header "internal/pycore_pymem.h" + exclude header "internal/pycore_pystate.h" + exclude header "internal/pycore_pystats.h" + exclude header "internal/pycore_pythonrun.h" + exclude header "internal/pycore_pythread.h" + exclude header "internal/pycore_qsbr.h" + exclude header "internal/pycore_range.h" + exclude header "internal/pycore_runtime_init_generated.h" + exclude header "internal/pycore_runtime_init.h" + exclude header "internal/pycore_runtime.h" + exclude header "internal/pycore_semaphore.h" + exclude header "internal/pycore_setobject.h" + exclude header "internal/pycore_signal.h" + exclude header "internal/pycore_sliceobject.h" + exclude header "internal/pycore_stackref.h" + exclude header "internal/pycore_strhex.h" + exclude header "internal/pycore_structseq.h" + exclude header "internal/pycore_symtable.h" + exclude header "internal/pycore_sysmodule.h" + exclude header "internal/pycore_time.h" + exclude header "internal/pycore_token.h" + exclude header "internal/pycore_traceback.h" + exclude header "internal/pycore_tracemalloc.h" + exclude header "internal/pycore_tstate.h" + exclude header "internal/pycore_tuple.h" + exclude header "internal/pycore_typeobject.h" + exclude header "internal/pycore_typevarobject.h" + exclude header "internal/pycore_ucnhash.h" + exclude header "internal/pycore_unicodeobject_generated.h" + exclude header "internal/pycore_unicodeobject.h" + exclude header "internal/pycore_unionobject.h" + exclude header "internal/pycore_uniqueid.h" + exclude header "internal/pycore_uop_ids.h" + exclude header "internal/pycore_uop_metadata.h" + exclude header "internal/pycore_warnings.h" + exclude header "internal/pycore_weakref.h" } From 1158013c280b625e64db4470c528f03f61b48546 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 06:08:50 +0800 Subject: [PATCH 073/113] Bump ncipollo/release-action from 1.15.0 to 1.16.0 (#253) Bumps [ncipollo/release-action](https://github.com/ncipollo/release-action) from 1.15.0 to 1.16.0. - [Release notes](https://github.com/ncipollo/release-action/releases) - [Commits](https://github.com/ncipollo/release-action/compare/v1.15.0...v1.16.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 4c0a16a4..0980fb50 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -47,7 +47,7 @@ jobs: merge-multiple: true - name: Create Release - uses: ncipollo/release-action@v1.15.0 + uses: ncipollo/release-action@v1.16.0 with: name: ${{ needs.ci.outputs.PYTHON_VER }}-${{ needs.config.outputs.BUILD_NUMBER }} tag: ${{ needs.ci.outputs.PYTHON_VER }}-${{ needs.config.outputs.BUILD_NUMBER }} From 832d39318cb752f0a0165ee2eeee263610fb12ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 06:09:14 +0800 Subject: [PATCH 074/113] Bump actions/upload-artifact from 4.6.0 to 4.6.1 (#254) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.0 to 4.6.1. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.6.0...v4.6.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 44609a7a..ef7ad512 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -121,7 +121,7 @@ jobs: make ${{ matrix.target }} BUILD_NUMBER=${{ needs.config.outputs.BUILD_NUMBER }} - name: Upload build artefacts - uses: actions/upload-artifact@v4.6.0 + uses: actions/upload-artifact@v4.6.1 with: name: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz path: dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz From aa545e919535a3334cf17a79dc4acfdd512d9a41 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 26 Feb 2025 07:05:14 +0800 Subject: [PATCH 075/113] Add patch for handling empty simulator lists. --- patch/Python/Python.patch | 49 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 910f33c2..139da995 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1271,10 +1271,53 @@ index c3e261ecd9e..26ef7a95de4 100644 iPhoneOS diff --git a/iOS/testbed/__main__.py b/iOS/testbed/__main__.py -index b4499f5ac17..08fbe90a1c6 100644 +index b4499f5ac17..d12a5ab065b 100644 --- a/iOS/testbed/__main__.py +++ b/iOS/testbed/__main__.py -@@ -230,33 +230,69 @@ +@@ -82,19 +82,29 @@ + + # Return a list of UDIDs associated with booted simulators + async def list_devices(): +- # 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 iOS simulators +- return [ +- simulator["udid"] +- for runtime, simulators in json_data["devices"].items() +- for simulator in simulators +- if runtime.split(".")[-1].startswith("iOS") and simulator["state"] == "Booted" +- ] ++ 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 iOS simulators ++ return [ ++ simulator["udid"] ++ for runtime, simulators in json_data["devices"].items() ++ for simulator in simulators ++ if runtime.split(".")[-1].startswith("iOS") 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): +@@ -230,33 +240,69 @@ shutil.copytree(source, target, symlinks=True) print(" done") @@ -1351,7 +1394,7 @@ index b4499f5ac17..08fbe90a1c6 100644 for app_src in apps: print(f" Installing app {app_src.name!r}...", end="", flush=True) -@@ -372,8 +408,8 @@ +@@ -372,8 +418,8 @@ if context.subcommand == "clone": clone_testbed( From c794b061d3092ea6716039b22118de85fd98aa27 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 08:56:06 +0800 Subject: [PATCH 076/113] Bump actions/download-artifact from 4.1.8 to 4.1.9 (#255) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.1.8 to 4.1.9. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4.1.8...v4.1.9) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 0980fb50..61da403c 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -40,7 +40,7 @@ jobs: needs: [ config, ci ] steps: - name: Get build artifacts - uses: actions/download-artifact@v4.1.8 + uses: actions/download-artifact@v4.1.9 with: pattern: Python-* path: dist From 0013ca0bc85a38f21ca1c4c9f6c8b1e44ae6c400 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 18 Mar 2025 15:39:26 +0800 Subject: [PATCH 077/113] Update patch for Python 3.14.0a6 (#258) * Update patch for v3.14.0a6, * Automate the contents of the modulemap file. --- .github/workflows/ci.yaml | 7 +- Makefile | 24 +++- patch/Python/Python.patch | 171 +++------------------------ patch/Python/module.modulemap | 165 -------------------------- patch/Python/module.modulemap.prefix | 20 ++++ 5 files changed, 60 insertions(+), 327 deletions(-) delete mode 100644 patch/Python/module.modulemap create mode 100644 patch/Python/module.modulemap.prefix diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ef7ad512..6befbc46 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,10 +1,6 @@ name: CI on: pull_request: - push: - branches: - - main - - 3.* workflow_call: inputs: build-number: @@ -114,6 +110,9 @@ jobs: # Appending -dev ensures that we can always build the dev release. # It's a no-op for versions that have been published. python-version: ${{ needs.config.outputs.PYTHON_VER }}-dev + # Ensure that we *always* use the latest build, not a cached version. + # It's an edge case, but when a new alpha is released, we need to use it ASAP. + check-latest: true - name: Build ${{ matrix.target }} run: | diff --git a/Makefile b/Makefile index b0ba7299..7d3f6bfa 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,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.0a5 +PYTHON_VERSION=3.14.0a6 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+") @@ -426,6 +426,7 @@ PYTHON_FRAMEWORK-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/Python.framework PYTHON_INSTALL_VERSION-$(sdk)=$$(PYTHON_FRAMEWORK-$(sdk))/Versions/$(PYTHON_VER) PYTHON_LIB-$(sdk)=$$(PYTHON_INSTALL_VERSION-$(sdk))/Python PYTHON_INCLUDE-$(sdk)=$$(PYTHON_INSTALL_VERSION-$(sdk))/include/python$(PYTHON_VER) +PYTHON_MODULEMAP-$(sdk)=$$(PYTHON_INCLUDE-$(sdk))/module.modulemap PYTHON_STDLIB-$(sdk)=$$(PYTHON_INSTALL_VERSION-$(sdk))/lib/python$(PYTHON_VER) else @@ -436,6 +437,7 @@ else # The non-macOS frameworks don't use the versioning structure. PYTHON_INSTALL-$(sdk)=$(PROJECT_DIR)/install/$(os)/$(sdk)/python-$(PYTHON_VERSION) +PYTHON_MODULEMAP-$(sdk)=$$(PYTHON_INCLUDE-$(sdk))/module.modulemap PYTHON_FRAMEWORK-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/Python.framework PYTHON_LIB-$(sdk)=$$(PYTHON_FRAMEWORK-$(sdk))/Python PYTHON_BIN-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/bin @@ -466,8 +468,14 @@ $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h: $$(PYTHON_LIB-$(sdk)) # Copy headers as-is from the first target in the $(sdk) SDK cp -r $$(PYTHON_INCLUDE-$$(firstword $$(SDK_TARGETS-$(sdk)))) $$(PYTHON_INCLUDE-$(sdk)) - # Copy in the modulemap file - cp -r patch/Python/module.modulemap $$(PYTHON_INCLUDE-$(sdk)) + # Create the modulemap file + cp -r patch/Python/module.modulemap.prefix $$(PYTHON_MODULEMAP-$(sdk)) + echo "" >> $$(PYTHON_MODULEMAP-$(sdk)) + cd $$(PYTHON_SRCDIR-$$(firstword $$(SDK_TARGETS-$(sdk))))/Include && \ + find cpython -name "*.h" | sort | sed -e 's/^/ exclude header "/' | sed 's/$$$$/"/' >> $$(PYTHON_MODULEMAP-$(sdk)) && \ + echo "" >> $$(PYTHON_MODULEMAP-$(sdk)) && \ + find internal -name "*.h" | sort | sed -e 's/^/ exclude header "/' | sed 's/$$$$/"/' >> $$(PYTHON_MODULEMAP-$(sdk)) + echo "\n}" >> $$(PYTHON_MODULEMAP-$(sdk)) # Link the PYTHONHOME version of the headers mkdir -p $$(PYTHON_INSTALL-$(sdk))/include @@ -585,8 +593,14 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ # Rewrite the framework to make it standalone patch/make-relocatable.sh $$(PYTHON_INSTALL_VERSION-macosx) 2>&1 > /dev/null - # Copy in the modulemap file - cp -r patch/Python/module.modulemap $$(PYTHON_FRAMEWORK-macosx)/Headers + # Create the modulemap file + cp -r patch/Python/module.modulemap.prefix $$(PYTHON_MODULEMAP-macosx) + echo "" >> $$(PYTHON_MODULEMAP-macosx) + cd $$(PYTHON_INCLUDE-macosx) && \ + find cpython -name "*.h" | sort | sed -e 's/^/ exclude header "/' | sed 's/$$$$/"/' >> $$(PYTHON_MODULEMAP-macosx) && \ + echo "" >> $$(PYTHON_MODULEMAP-macosx) && \ + find internal -name "*.h" | sort | sed -e 's/^/ exclude header "/' | sed 's/$$$$/"/' >> $$(PYTHON_MODULEMAP-macosx) + echo "\n}" >> $$(PYTHON_MODULEMAP-macosx) # Re-apply the signature on the binaries. codesign -s - --preserve-metadata=identifier,entitlements,flags,runtime -f $$(PYTHON_LIB-macosx) \ diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 139da995..c9c0ef6f 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -113,7 +113,7 @@ index 1f6baed66d3..235dd98c60a 100644 macos_release = mac_ver()[0] if macos_release: diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py -index 69f72452c40..34ce643340b 100644 +index 18e6b8d25e5..4994c56778c 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -719,6 +719,14 @@ @@ -163,7 +163,7 @@ index ec0857a4a99..2350e9dc821 100644 # elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX PLATFORM_TRIPLET=darwin diff --git a/configure b/configure -index d46bc563a67..d5cd81d16a8 100755 +index d0ae103014a..308124ef06d 100755 --- a/configure +++ b/configure @@ -974,6 +974,8 @@ @@ -588,7 +588,7 @@ index d46bc563a67..d5cd81d16a8 100755 fi ac_fn_c_check_func "$LINENO" "pread" "ac_cv_func_pread" if test "x$ac_cv_func_pread" = xyes -@@ -19860,12 +19973,6 @@ +@@ -19866,12 +19979,6 @@ then : printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h @@ -601,7 +601,7 @@ index d46bc563a67..d5cd81d16a8 100755 fi ac_fn_c_check_func "$LINENO" "sigfillset" "ac_cv_func_sigfillset" if test "x$ac_cv_func_sigfillset" = xyes -@@ -20134,11 +20241,11 @@ +@@ -20140,11 +20247,11 @@ fi @@ -615,7 +615,7 @@ index d46bc563a67..d5cd81d16a8 100755 ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" if test "x$ac_cv_func_getentropy" = xyes then : -@@ -20160,6 +20267,53 @@ +@@ -20166,6 +20273,53 @@ fi @@ -669,7 +669,7 @@ index d46bc563a67..d5cd81d16a8 100755 { 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} -@@ -23242,7 +23396,8 @@ +@@ -23248,7 +23402,8 @@ # check for openpty, login_tty, and forkpty @@ -679,7 +679,7 @@ index d46bc563a67..d5cd81d16a8 100755 for ac_func in openpty do : -@@ -23356,7 +23511,7 @@ +@@ -23362,7 +23517,7 @@ fi done @@ -688,7 +688,7 @@ index d46bc563a67..d5cd81d16a8 100755 printf %s "checking for library containing login_tty... " >&6; } if test ${ac_cv_search_login_tty+y} then : -@@ -23539,6 +23694,7 @@ +@@ -23545,6 +23700,7 @@ fi done @@ -696,7 +696,7 @@ index d46bc563a67..d5cd81d16a8 100755 # check for long file support functions ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" -@@ -23804,10 +23960,10 @@ +@@ -23810,10 +23966,10 @@ done @@ -709,7 +709,7 @@ index d46bc563a67..d5cd81d16a8 100755 then for ac_func in clock_settime -@@ -26146,8 +26302,8 @@ +@@ -26152,8 +26308,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -720,7 +720,7 @@ index d46bc563a67..d5cd81d16a8 100755 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -29017,7 +29173,7 @@ +@@ -29023,7 +29179,7 @@ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for device files" >&5 printf "%s\n" "$as_me: checking for device files" >&6;} @@ -729,7 +729,7 @@ index d46bc563a67..d5cd81d16a8 100755 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -29510,7 +29666,7 @@ +@@ -29504,7 +29660,7 @@ with_ensurepip=no ;; #( WASI) : with_ensurepip=no ;; #( @@ -738,7 +738,7 @@ index d46bc563a67..d5cd81d16a8 100755 with_ensurepip=no ;; #( *) : with_ensurepip=upgrade -@@ -30490,7 +30646,7 @@ +@@ -30484,7 +30640,7 @@ ;; #( Darwin) : ;; #( @@ -747,7 +747,7 @@ index d46bc563a67..d5cd81d16a8 100755 -@@ -34493,6 +34649,8 @@ +@@ -34487,6 +34643,8 @@ "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" ;; @@ -757,7 +757,7 @@ index d46bc563a67..d5cd81d16a8 100755 "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 faa89095303..9bd51f7da97 100644 +index 8bb0f1c6ef4..bfd67de48bb 100644 --- a/configure.ac +++ b/configure.ac @@ -330,6 +330,12 @@ @@ -1128,7 +1128,7 @@ index faa89095303..9bd51f7da97 100644 + 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_getname_np pthread_setname_np \ + pthread_kill pthread_getname_np pthread_setname_np pthread_getattr_np \ @@ -5153,7 +5283,7 @@ sched_setparam sched_setscheduler sem_clockwait sem_getvalue sem_open \ sem_timedwait sem_unlink sendfile setegid seteuid setgid sethostname \ @@ -1232,7 +1232,7 @@ index faa89095303..9bd51f7da97 100644 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -7187,7 +7327,7 @@ +@@ -7174,7 +7314,7 @@ AS_CASE([$ac_sys_system], [Emscripten], [with_ensurepip=no], [WASI], [with_ensurepip=no], @@ -1241,7 +1241,7 @@ index faa89095303..9bd51f7da97 100644 [with_ensurepip=upgrade] ) ]) -@@ -7598,7 +7738,7 @@ +@@ -7585,7 +7725,7 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], @@ -1270,141 +1270,6 @@ index c3e261ecd9e..26ef7a95de4 100644 CFBundleSupportedPlatforms iPhoneOS -diff --git a/iOS/testbed/__main__.py b/iOS/testbed/__main__.py -index b4499f5ac17..d12a5ab065b 100644 ---- a/iOS/testbed/__main__.py -+++ b/iOS/testbed/__main__.py -@@ -82,19 +82,29 @@ - - # Return a list of UDIDs associated with booted simulators - async def list_devices(): -- # 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 iOS simulators -- return [ -- simulator["udid"] -- for runtime, simulators in json_data["devices"].items() -- for simulator in simulators -- if runtime.split(".")[-1].startswith("iOS") and simulator["state"] == "Booted" -- ] -+ 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 iOS simulators -+ return [ -+ simulator["udid"] -+ for runtime, simulators in json_data["devices"].items() -+ for simulator in simulators -+ if runtime.split(".")[-1].startswith("iOS") 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): -@@ -230,33 +240,69 @@ - shutil.copytree(source, target, symlinks=True) - print(" done") - -+ xc_framework_path = target / "Python.xcframework" -+ sim_framework_path = xc_framework_path / "ios-arm64_x86_64-simulator" - if framework is not None: - if framework.suffix == ".xcframework": - print(" Installing XCFramework...", end="", flush=True) -- xc_framework_path = (target / "Python.xcframework").resolve() - if xc_framework_path.is_dir(): - shutil.rmtree(xc_framework_path) - else: -- xc_framework_path.unlink() -+ xc_framework_path.unlink(missing_ok=True) - xc_framework_path.symlink_to( - framework.relative_to(xc_framework_path.parent, walk_up=True) - ) - print(" done") - else: - print(" Installing simulator framework...", end="", flush=True) -- sim_framework_path = ( -- target / "Python.xcframework" / "ios-arm64_x86_64-simulator" -- ).resolve() - if sim_framework_path.is_dir(): - shutil.rmtree(sim_framework_path) - else: -- sim_framework_path.unlink() -+ sim_framework_path.unlink(missing_ok=True) - sim_framework_path.symlink_to( - framework.relative_to(sim_framework_path.parent, walk_up=True) - ) - print(" done") - else: -- print(" Using pre-existing iOS framework.") -+ if ( -+ xc_framework_path.is_symlink() -+ and not xc_framework_path.readlink().is_absolute() -+ ): -+ # XCFramework is a relative symlink. Rewrite the symlink relative -+ # to the new location. -+ print(" Rewriting symlink to XCframework...", end="", flush=True) -+ orig_xc_framework_path = ( -+ source -+ / xc_framework_path.readlink() -+ ).resolve() -+ xc_framework_path.unlink() -+ xc_framework_path.symlink_to( -+ orig_xc_framework_path.relative_to( -+ xc_framework_path.parent, walk_up=True -+ ) -+ ) -+ print(" done") -+ elif ( -+ sim_framework_path.is_symlink() -+ and not sim_framework_path.readlink().is_absolute() -+ ): -+ print(" Rewriting symlink to simulator framework...", end="", flush=True) -+ # Simulator framework is a relative symlink. Rewrite the symlink -+ # relative to the new location. -+ orig_sim_framework_path = ( -+ source -+ / "Python.XCframework" -+ / sim_framework_path.readlink() -+ ).resolve() -+ sim_framework_path.unlink() -+ sim_framework_path.symlink_to( -+ orig_sim_framework_path.relative_to( -+ sim_framework_path.parent, walk_up=True -+ ) -+ ) -+ print(" done") -+ else: -+ print(" Using pre-existing iOS framework.") - - for app_src in apps: - print(f" Installing app {app_src.name!r}...", end="", flush=True) -@@ -372,8 +418,8 @@ - - if context.subcommand == "clone": - clone_testbed( -- source=Path(__file__).parent, -- target=Path(context.location), -+ source=Path(__file__).parent.resolve(), -+ target=Path(context.location).resolve(), - framework=Path(context.framework).resolve() if context.framework else None, - apps=[Path(app) for app in context.apps], - ) --- /dev/null +++ b/tvOS/README.rst @@ -0,0 +1,108 @@ diff --git a/patch/Python/module.modulemap b/patch/Python/module.modulemap deleted file mode 100644 index 96b05fc6..00000000 --- a/patch/Python/module.modulemap +++ /dev/null @@ -1,165 +0,0 @@ -module Python { - umbrella header "Python.h" - export * - link "Python" - - exclude header "datetime.h" - exclude header "dynamic_annotations.h" - exclude header "errcode.h" - exclude header "frameobject.h" - exclude header "marshal.h" - exclude header "opcode_ids.h" - exclude header "opcode.h" - exclude header "osdefs.h" - exclude header "py_curses.h" - exclude header "pyconfig-arm32_64.h" - exclude header "pyconfig-arm64.h" - exclude header "pyconfig-x86_64.h" - exclude header "pydtrace.h" - exclude header "pyexpat.h" - exclude header "structmember.h" - - exclude header "cpython/frameobject.h" - exclude header "cpython/pthread_stubs.h" - exclude header "cpython/pyatomic_msc.h" - exclude header "cpython/pyatomic_std.h" - exclude header "cpython/pystats.h" - - exclude header "internal/mimalloc/mimalloc.h" - exclude header "internal/mimalloc/mimalloc/atomic.h" - exclude header "internal/mimalloc/mimalloc/internal.h" - exclude header "internal/mimalloc/mimalloc/prim.h" - exclude header "internal/mimalloc/mimalloc/track.h" - exclude header "internal/mimalloc/mimalloc/types.h" - - exclude header "internal/pycore_abstract.h" - exclude header "internal/pycore_asdl.h" - exclude header "internal/pycore_ast_state.h" - exclude header "internal/pycore_ast.h" - exclude header "internal/pycore_atexit.h" - exclude header "internal/pycore_audit.h" - exclude header "internal/pycore_backoff.h" - exclude header "internal/pycore_bitutils.h" - exclude header "internal/pycore_blocks_output_buffer.h" - exclude header "internal/pycore_brc.h" - exclude header "internal/pycore_bytes_methods.h" - exclude header "internal/pycore_bytesobject.h" - exclude header "internal/pycore_call.h" - exclude header "internal/pycore_capsule.h" - exclude header "internal/pycore_cell.h" - exclude header "internal/pycore_ceval_state.h" - exclude header "internal/pycore_ceval.h" - exclude header "internal/pycore_code.h" - exclude header "internal/pycore_codecs.h" - exclude header "internal/pycore_compile.h" - exclude header "internal/pycore_complexobject.h" - exclude header "internal/pycore_condvar.h" - exclude header "internal/pycore_context.h" - exclude header "internal/pycore_critical_section.h" - exclude header "internal/pycore_crossinterp_data_registry.h" - exclude header "internal/pycore_crossinterp.h" - exclude header "internal/pycore_debug_offsets.h" - exclude header "internal/pycore_descrobject.h" - exclude header "internal/pycore_dict_state.h" - exclude header "internal/pycore_dict.h" - exclude header "internal/pycore_dtoa.h" - exclude header "internal/pycore_emscripten_signal.h" - exclude header "internal/pycore_emscripten_trampoline.h" - exclude header "internal/pycore_exceptions.h" - exclude header "internal/pycore_faulthandler.h" - exclude header "internal/pycore_fileutils_windows.h" - exclude header "internal/pycore_fileutils.h" - exclude header "internal/pycore_floatobject.h" - exclude header "internal/pycore_flowgraph.h" - exclude header "internal/pycore_format.h" - exclude header "internal/pycore_frame.h" - exclude header "internal/pycore_freelist_state.h" - exclude header "internal/pycore_freelist.h" - exclude header "internal/pycore_function.h" - exclude header "internal/pycore_gc.h" - exclude header "internal/pycore_genobject.h" - exclude header "internal/pycore_getopt.h" - exclude header "internal/pycore_gil.h" - exclude header "internal/pycore_global_objects_fini_generated.h" - exclude header "internal/pycore_global_objects.h" - exclude header "internal/pycore_global_strings.h" - exclude header "internal/pycore_hamt.h" - exclude header "internal/pycore_hashtable.h" - exclude header "internal/pycore_identifier.h" - exclude header "internal/pycore_import.h" - exclude header "internal/pycore_importdl.h" - exclude header "internal/pycore_index_pool.h" - exclude header "internal/pycore_initconfig.h" - exclude header "internal/pycore_instruction_sequence.h" - exclude header "internal/pycore_instruments.h" - exclude header "internal/pycore_interp.h" - exclude header "internal/pycore_intrinsics.h" - exclude header "internal/pycore_jit.h" - exclude header "internal/pycore_list.h" - exclude header "internal/pycore_llist.h" - exclude header "internal/pycore_lock.h" - exclude header "internal/pycore_long.h" - exclude header "internal/pycore_magic_number.h" - exclude header "internal/pycore_memoryobject.h" - exclude header "internal/pycore_mimalloc.h" - exclude header "internal/pycore_modsupport.h" - exclude header "internal/pycore_moduleobject.h" - exclude header "internal/pycore_namespace.h" - exclude header "internal/pycore_object_alloc.h" - exclude header "internal/pycore_object_deferred.h" - exclude header "internal/pycore_object_stack.h" - exclude header "internal/pycore_object_state.h" - exclude header "internal/pycore_object.h" - exclude header "internal/pycore_obmalloc_init.h" - exclude header "internal/pycore_obmalloc.h" - exclude header "internal/pycore_opcode_metadata.h" - exclude header "internal/pycore_opcode_utils.h" - exclude header "internal/pycore_optimizer.h" - exclude header "internal/pycore_parking_lot.h" - exclude header "internal/pycore_parser.h" - exclude header "internal/pycore_pathconfig.h" - exclude header "internal/pycore_pyarena.h" - exclude header "internal/pycore_pyatomic_ft_wrappers.h" - exclude header "internal/pycore_pybuffer.h" - exclude header "internal/pycore_pyerrors.h" - exclude header "internal/pycore_pyhash.h" - exclude header "internal/pycore_pylifecycle.h" - exclude header "internal/pycore_pymath.h" - exclude header "internal/pycore_pymem_init.h" - exclude header "internal/pycore_pymem.h" - exclude header "internal/pycore_pystate.h" - exclude header "internal/pycore_pystats.h" - exclude header "internal/pycore_pythonrun.h" - exclude header "internal/pycore_pythread.h" - exclude header "internal/pycore_qsbr.h" - exclude header "internal/pycore_range.h" - exclude header "internal/pycore_runtime_init_generated.h" - exclude header "internal/pycore_runtime_init.h" - exclude header "internal/pycore_runtime.h" - exclude header "internal/pycore_semaphore.h" - exclude header "internal/pycore_setobject.h" - exclude header "internal/pycore_signal.h" - exclude header "internal/pycore_sliceobject.h" - exclude header "internal/pycore_stackref.h" - exclude header "internal/pycore_strhex.h" - exclude header "internal/pycore_structseq.h" - exclude header "internal/pycore_symtable.h" - exclude header "internal/pycore_sysmodule.h" - exclude header "internal/pycore_time.h" - exclude header "internal/pycore_token.h" - exclude header "internal/pycore_traceback.h" - exclude header "internal/pycore_tracemalloc.h" - exclude header "internal/pycore_tstate.h" - exclude header "internal/pycore_tuple.h" - exclude header "internal/pycore_typeobject.h" - exclude header "internal/pycore_typevarobject.h" - exclude header "internal/pycore_ucnhash.h" - exclude header "internal/pycore_unicodeobject_generated.h" - exclude header "internal/pycore_unicodeobject.h" - exclude header "internal/pycore_unionobject.h" - exclude header "internal/pycore_uniqueid.h" - exclude header "internal/pycore_uop_ids.h" - exclude header "internal/pycore_uop_metadata.h" - exclude header "internal/pycore_warnings.h" - exclude header "internal/pycore_weakref.h" -} diff --git a/patch/Python/module.modulemap.prefix b/patch/Python/module.modulemap.prefix new file mode 100644 index 00000000..e3b3aafb --- /dev/null +++ b/patch/Python/module.modulemap.prefix @@ -0,0 +1,20 @@ +module Python { + umbrella header "Python.h" + export * + link "Python" + + exclude header "datetime.h" + exclude header "dynamic_annotations.h" + exclude header "errcode.h" + exclude header "frameobject.h" + exclude header "marshal.h" + exclude header "opcode_ids.h" + exclude header "opcode.h" + exclude header "osdefs.h" + exclude header "py_curses.h" + exclude header "pyconfig-arm32_64.h" + exclude header "pyconfig-arm64.h" + exclude header "pyconfig-x86_64.h" + exclude header "pydtrace.h" + exclude header "pyexpat.h" + exclude header "structmember.h" From ca43143a7cc279b687291928ecf28d2c599b1e71 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 07:25:35 +0800 Subject: [PATCH 078/113] Bump actions/download-artifact from 4.1.9 to 4.2.1 (#265) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.1.9 to 4.2.1. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4.1.9...v4.2.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 61da403c..e4da86bc 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -40,7 +40,7 @@ jobs: needs: [ config, ci ] steps: - name: Get build artifacts - uses: actions/download-artifact@v4.1.9 + uses: actions/download-artifact@v4.2.1 with: pattern: Python-* path: dist From cfd548d6df644ffd922491e47087d4f0c547d6fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 07:26:17 +0800 Subject: [PATCH 079/113] Bump actions/upload-artifact from 4.6.1 to 4.6.2 (#266) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.1 to 4.6.2. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.6.1...v4.6.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6befbc46..8569ccfa 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -120,7 +120,7 @@ jobs: make ${{ matrix.target }} BUILD_NUMBER=${{ needs.config.outputs.BUILD_NUMBER }} - name: Upload build artefacts - uses: actions/upload-artifact@v4.6.1 + uses: actions/upload-artifact@v4.6.2 with: name: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz path: dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz From db04fbd9b2da6a34e3837f552ee33733e7062be5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 06:55:55 +0800 Subject: [PATCH 080/113] Bump actions/setup-python from 5.4.0 to 5.5.0 (#267) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.4.0 to 5.5.0. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5.4.0...v5.5.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yaml | 2 +- .github/workflows/publish.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8569ccfa..22859602 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -105,7 +105,7 @@ jobs: - uses: actions/checkout@v4.1.7 - name: Set up Python - uses: actions/setup-python@v5.4.0 + uses: actions/setup-python@v5.5.0 with: # Appending -dev ensures that we can always build the dev release. # It's a no-op for versions that have been published. diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 5d2ce753..3d6922ba 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python environment - uses: actions/setup-python@v5.4.0 + uses: actions/setup-python@v5.5.0 with: python-version: "3.X" From 81428dbb5dcc3b95260afe06c0bcf275e53f41a5 Mon Sep 17 00:00:00 2001 From: John Date: Wed, 16 Apr 2025 19:01:51 -0500 Subject: [PATCH 081/113] Add Python.patch Note to Contributing.md (#271) Add notes about contributing changes to Python.patch. Co-authored-by: Russell Keith-Magee --- CONTRIBUTING.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d6aad904..10402ae6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,30 @@ BeeWare <3's contributions! -Please be aware, BeeWare operates under a Code of Conduct. +Please be aware that BeeWare operates under a [Code of +Conduct](https://beeware.org/community/behavior/code-of-conduct/). -See [CONTRIBUTING to BeeWare](https://beeware.org/contributing) for details. +See [CONTRIBUTING to BeeWare](https://beeware.org/contributing) for general +project contribution guidelines. +Unless a fix is version specific, PRs should genereally be made against the +`main` branch of this repo, targeting the current development version of Python. +Project maintainers will manage the process of backporting changes to older +Python versions. + +## Changes to `Python.patch` + +Additional handling is required if you need to make modifications to the patch +applied to Python sources (`patch/Python/Python.patch`). + +Any iOS or macOS-specific changes should be submitted to the [upstream CPython +repository](https://github.com/python/cpython). macOS and iOS are both +officially supported Python platforms, and the code distributed by this project +for those platforms is unmodified from the official repository. + +Changes to to support other platforms can be included in this PR, but they must +also be submitted as a pull request against the `MAJOR.MINOR-patched` branch on +[the `freakboy3742` fork of the CPython +repo](https://github.com/freakboy3742/cpython). This is required to ensure that +any contributed changes can be easily reproduced in future patches as more +changes are made. From 92d8d958bd451e6683e255a357fa9f1378d4cffa Mon Sep 17 00:00:00 2001 From: John Date: Wed, 23 Apr 2025 02:18:48 -0500 Subject: [PATCH 082/113] Add support for visionOS (#270) Adds targets and patches to support building a visionOS support package. Co-authored-by: Russell Keith-Magee --- .github/workflows/ci.yaml | 2 +- .github/workflows/publish.yaml | 3 + Makefile | 39 +- patch/Python/Python.patch | 2464 +++++++++++++++++++++++-- patch/Python/release.visionOS.exclude | 6 + 5 files changed, 2365 insertions(+), 149 deletions(-) create mode 100644 patch/Python/release.visionOS.exclude diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 22859602..1f53f4cc 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -89,7 +89,7 @@ jobs: strategy: fail-fast: false matrix: - target: ['macOS', 'iOS', 'tvOS', 'watchOS'] + target: ['macOS', 'iOS', 'tvOS', 'watchOS', 'visionOS'] include: - briefcase-run-args: - run-tests: false diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 3d6922ba..c06a6e06 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -48,3 +48,6 @@ jobs: # watchOS build curl -o watchOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.outputs.TAG }}/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-watchOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz aws s3 cp watchOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.outputs.PYTHON_VER }}/watchOS/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-watchOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + # visionOS build + curl -o visionOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.outputs.TAG }}/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-visionOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + aws s3 cp visionOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.outputs.PYTHON_VER }}/visionOS/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-visionOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz diff --git a/Makefile b/Makefile index 7d3f6bfa..72994c0b 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ # - iOS - build everything for iOS # - tvOS - build everything for tvOS # - watchOS - build everything for watchOS +# - visionOS - build everything for visionOS # Current directory PROJECT_DIR=$(shell pwd) @@ -18,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.0a6 +PYTHON_VERSION=3.14.0a7 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+") @@ -26,33 +27,41 @@ PYTHON_VER=$(basename $(PYTHON_VERSION)) # The binary releases of dependencies, published at: # https://github.com/beeware/cpython-apple-source-deps/releases -BZIP2_VERSION=1.0.8-1 -LIBFFI_VERSION=3.4.7-1 -MPDECIMAL_VERSION=4.0.0-1 -OPENSSL_VERSION=3.0.16-1 -XZ_VERSION=5.6.4-1 +BZIP2_VERSION=1.0.8-2 +LIBFFI_VERSION=3.4.7-2 +MPDECIMAL_VERSION=4.0.0-2 +OPENSSL_VERSION=3.0.16-2 +XZ_VERSION=5.6.4-2 # Supported OS -OS_LIST=macOS iOS tvOS watchOS +OS_LIST=macOS iOS tvOS watchOS visionOS CURL_FLAGS=--disable --fail --location --create-dirs --progress-bar # macOS targets TARGETS-macOS=macosx.x86_64 macosx.arm64 +TRIPLE_OS-macOS=macos VERSION_MIN-macOS=11.0 # iOS targets TARGETS-iOS=iphonesimulator.x86_64 iphonesimulator.arm64 iphoneos.arm64 +TRIPLE_OS-iOS=ios VERSION_MIN-iOS=13.0 # tvOS targets TARGETS-tvOS=appletvsimulator.x86_64 appletvsimulator.arm64 appletvos.arm64 +TRIPLE_OS-tvOS=tvos VERSION_MIN-tvOS=12.0 # watchOS targets TARGETS-watchOS=watchsimulator.x86_64 watchsimulator.arm64 watchos.arm64_32 +TRIPLE_OS-watchOS=watchos VERSION_MIN-watchOS=4.0 +TARGETS-visionOS=xrsimulator.arm64 xros.arm64 +TRIPLE_OS-visionOS=xros +VERSION_MIN-visionOS=2.0 + # The architecture of the machine doing the build HOST_ARCH=$(shell uname -m) HOST_PYTHON=$(shell which python$(PYTHON_VER)) @@ -128,10 +137,10 @@ ARCH-$(target)=$$(subst .,,$$(suffix $(target))) ifneq ($(os),macOS) ifeq ($$(findstring simulator,$$(SDK-$(target))),) -TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(OS_LOWER-$(target))$$(VERSION_MIN-$(os)) +TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(TRIPLE_OS-$(os))$$(VERSION_MIN-$(os)) IS_SIMULATOR-$(target)=False else -TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(OS_LOWER-$(target))$$(VERSION_MIN-$(os))-simulator +TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(TRIPLE_OS-$(os))$$(VERSION_MIN-$(os))-simulator IS_SIMULATOR-$(target)=True endif endif @@ -398,15 +407,13 @@ define build-sdk sdk=$1 os=$2 -OS_LOWER-$(sdk)=$(shell echo $(os) | tr '[:upper:]' '[:lower:]') - SDK_TARGETS-$(sdk)=$$(filter $(sdk).%,$$(TARGETS-$(os))) SDK_ARCHES-$(sdk)=$$(sort $$(subst .,,$$(suffix $$(SDK_TARGETS-$(sdk))))) ifeq ($$(findstring simulator,$(sdk)),) -SDK_SLICE-$(sdk)=$$(OS_LOWER-$(sdk))-$$(shell echo $$(SDK_ARCHES-$(sdk)) | sed "s/ /_/g") +SDK_SLICE-$(sdk)=$$(TRIPLE_OS-$(os))-$$(shell echo $$(SDK_ARCHES-$(sdk)) | sed "s/ /_/g") else -SDK_SLICE-$(sdk)=$$(OS_LOWER-$(sdk))-$$(shell echo $$(SDK_ARCHES-$(sdk)) | sed "s/ /_/g")-simulator +SDK_SLICE-$(sdk)=$$(TRIPLE_OS-$(os))-$$(shell echo $$(SDK_ARCHES-$(sdk)) | sed "s/ /_/g")-simulator endif # Expand the build-target macro for target on this OS @@ -481,11 +488,15 @@ $$(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 $$(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))) @@ -650,7 +661,7 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/lib $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/platform-config $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) -ifeq ($(os),iOS) +ifeq ($(filter $(os),iOS visionOS),$(os)) @echo ">>> Clone testbed project for $(os)" $(HOST_PYTHON) $$(PYTHON_SRCDIR-$$(firstword $$(SDK_TARGETS-$$(firstword $$(SDKS-$(os))))))/iOS/testbed clone --framework $$(PYTHON_XCFRAMEWORK-$(os)) support/$(PYTHON_VER)/$(os)/testbed endif diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index c9c0ef6f..4c59f844 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1,8 +1,65 @@ +diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py +index bba08b99b95..8d03017c223 100644 +--- a/Lib/ctypes/__init__.py ++++ b/Lib/ctypes/__init__.py +@@ -361,7 +361,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 1f6baed66d3..235dd98c60a 100644 +index a62192589af..31f18b026b7 100644 --- a/Lib/platform.py +++ b/Lib/platform.py -@@ -521,6 +521,54 @@ +@@ -528,6 +528,78 @@ return IOSVersionInfo(system, release, model, is_simulator) @@ -53,18 +110,51 @@ index 1f6baed66d3..235dd98c60a 100644 + + 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 -@@ -884,14 +932,25 @@ +@@ -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 simulator, os.uname returns the architecture as ++ # On the iOS/tvOS/watchOS/visionOS simulator, os.uname returns the architecture as + # uname.machine. On device it returns the model name for some reason; but + # there's only one CPU architecture for devices, so we know the right + # answer. @@ -82,11 +172,16 @@ index 1f6baed66d3..235dd98c60a 100644 + if sys.implementation._multiarch.endswith("simulator"): + return os.uname().machine + return 'arm64_32' ++ ++ def get_visionos(): ++ if sys.implementation._multiarch.endswith("simulator"): ++ return os.uname().machine ++ return 'arm64' + def from_subprocess(): """ Fall back to `uname -p` -@@ -1051,9 +1110,13 @@ +@@ -1058,9 +1146,15 @@ system = 'Android' release = android_ver().release @@ -98,10 +193,12 @@ index 1f6baed66d3..235dd98c60a 100644 + 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 '' -@@ -1343,6 +1406,10 @@ +@@ -1350,6 +1444,12 @@ # macOS and iOS both report as a "Darwin" kernel if sys.platform == "ios": system, release, _, _ = ios_ver() @@ -109,14 +206,63 @@ index 1f6baed66d3..235dd98c60a 100644 + 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 9da8b6724e1..345f55a5bde 100644 +--- a/Lib/site.py ++++ b/Lib/site.py +@@ -297,8 +297,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 da5f5729e09..7401ffbc987 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 18e6b8d25e5..4994c56778c 100644 +index 18e6b8d25e5..64603fb1bb1 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py -@@ -719,6 +719,14 @@ +@@ -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): +@@ -719,6 +722,18 @@ release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0") osname = sys.platform machine = sys.implementation._multiarch @@ -127,15 +273,224 @@ index 18e6b8d25e5..4994c56778c 100644 + 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 ecb37250ceb..67d04491072 100644 +--- a/Lib/test/datetimetester.py ++++ b/Lib/test/datetimetester.py +@@ -7155,9 +7155,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 6d670a575b0..8d7d68d1ee1 100644 +--- a/Lib/test/support/__init__.py ++++ b/Lib/test/support/__init__.py +@@ -551,7 +551,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 +@@ -567,7 +567,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/support/os_helper.py b/Lib/test/support/os_helper.py +index d82093e375c..2c45fe2369e 100644 +--- a/Lib/test/support/os_helper.py ++++ b/Lib/test/support/os_helper.py +@@ -657,7 +657,7 @@ + """ + if sys.platform.startswith(('linux', 'android', 'freebsd', 'emscripten')): + fd_path = "/proc/self/fd" +- elif sys.platform == "darwin": ++ elif support.is_apple: + fd_path = "/dev/fd" + else: + fd_path = None +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 6ba630ad527..7b447744d12 100644 +--- a/Lib/test/test_platform.py ++++ b/Lib/test/test_platform.py +@@ -268,13 +268,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 232d3c3a9c5..e042c20ea54 100644 +--- a/Lib/webbrowser.py ++++ b/Lib/webbrowser.py +@@ -488,7 +488,8 @@ + # OS X can use below Unix support (but we prefer using the OS X + # 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": +@@ -640,9 +641,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 e10c78d6403..920e707ab26 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 + +@@ -2243,7 +2249,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 \ +@@ -2263,11 +2269,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 ec0857a4a99..2350e9dc821 100644 +index ec0857a4a99..e52f486cdb3 100644 --- a/Misc/platform_triplet.c +++ b/Misc/platform_triplet.c -@@ -257,6 +257,26 @@ +@@ -257,6 +257,32 @@ # else PLATFORM_TRIPLET=arm64-iphoneos # endif @@ -158,24 +513,54 @@ index ec0857a4a99..2350e9dc821 100644 +# 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 d0ae103014a..308124ef06d 100755 +index 1b75ddfa26d..7b79dfc424c 100755 --- a/configure +++ b/configure -@@ -974,6 +974,8 @@ +@@ -978,6 +978,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 -@@ -4100,6 +4102,12 @@ +@@ -4106,6 +4110,15 @@ *-apple-ios*) ac_sys_system=iOS ;; @@ -184,20 +569,23 @@ index d0ae103014a..308124ef06d 100755 + ;; + *-apple-watchos*) + ac_sys_system=watchOS ++ ;; ++ *-apple-xros*) ++ ac_sys_system=visionOS + ;; *-*-darwin*) ac_sys_system=Darwin ;; -@@ -4181,7 +4189,7 @@ +@@ -4187,7 +4200,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, 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 -@@ -4196,6 +4204,14 @@ +@@ -4202,6 +4215,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 ;; @@ -209,10 +597,13 @@ index d0ae103014a..308124ef06d 100755 + 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 -@@ -4204,6 +4220,14 @@ +@@ -4210,6 +4234,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 ;; @@ -224,10 +615,13 @@ index d0ae103014a..308124ef06d 100755 + 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 -@@ -4212,6 +4236,14 @@ +@@ -4218,6 +4253,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 ;; @@ -239,10 +633,13 @@ index d0ae103014a..308124ef06d 100755 + 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 -@@ -4220,6 +4252,14 @@ +@@ -4226,6 +4272,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++ ;; @@ -254,10 +651,13 @@ index d0ae103014a..308124ef06d 100755 + 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 -@@ -4342,8 +4382,10 @@ +@@ -4348,8 +4405,11 @@ case $enableval in yes) case $ac_sys_system in @@ -267,19 +667,21 @@ index d0ae103014a..308124ef06d 100755 + 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 -@@ -4352,6 +4394,8 @@ +@@ -4358,6 +4418,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 -@@ -4458,6 +4502,36 @@ +@@ -4464,6 +4527,51 @@ ac_config_files="$ac_config_files iOS/Resources/Info.plist" @@ -312,42 +714,67 @@ index d0ae103014a..308124ef06d 100755 + RESSRCDIR=watchOS/Resources + + ac_config_files="$ac_config_files watchOS/Resources/Info.plist" ++ ++ ;; ++ visionOS) : ++ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=visionOS/Resources ++ ++ ac_config_files="$ac_config_files visionOS/Resources/Info.plist" + ;; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 -@@ -4469,6 +4543,8 @@ +@@ -4475,6 +4583,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 -@@ -4523,8 +4599,8 @@ +@@ -4529,8 +4640,8 @@ case "$withval" in yes) case $ac_sys_system in - Darwin|iOS) - # iOS is able to share the macOS patch -+ Darwin|iOS|tvOS|watchOS) -+ # iOS/tvOS/watchOS is able to share the macOS patch ++ 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 ;; -@@ -4542,8 +4618,8 @@ +@@ -4548,8 +4659,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) -+ # Always apply the compliance patch on iOS/tvOS/watchOS; we can use the macOS patch ++ iOS|tvOS|watchOS|visionOS) ++ # Always apply the compliance patch on iOS/tvOS/watchOS/visionOS; we can use the macOS patch APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: applying default app store compliance patch" >&5 printf "%s\n" "applying default app store compliance patch" >&6; } -@@ -4598,6 +4674,50 @@ +@@ -4567,6 +4678,8 @@ + + + ++EXPORT_XROS_DEPLOYMENT_TARGET='#' ++ + + if test "$cross_compiling" = yes; then + case "$host" in +@@ -4604,6 +4717,78 @@ ;; esac ;; @@ -394,38 +821,71 @@ index d0ae103014a..308124ef06d 100755 + _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-$host_cpu-watch${_host_device} + ;; + esac ++ ;; ++ *-apple-xros*) ++ _host_os=`echo $host | cut -d '-' -f3` ++ _host_device=`echo $host | cut -d '-' -f4` ++ _host_device=${_host_device:=os} ++ ++ # XROS_DEPLOYMENT_TARGET is the minimum supported visionOS version ++ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking visionOS deployment target" >&5 ++printf %s "checking visionOS deployment target... " >&6; } ++ XROS_DEPLOYMENT_TARGET=${_host_os:8} ++ XROS_DEPLOYMENT_TARGET=${XROS_DEPLOYMENT_TARGET:=2.0} ++ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $XROS_DEPLOYMENT_TARGET" >&5 ++printf "%s\n" "$XROS_DEPLOYMENT_TARGET" >&6; } ++ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking exporting flag of visionOS deployment target" >&5 ++printf %s "checking exporting flag of visionOS deployment target... " >&6; } ++ export XROS_DEPLOYMENT_TARGET ++ EXPORT_XROS_DEPLOYMENT_TARGET='' ++ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $EXPORT_XROS_DEPLOYMENT_TARGET" >&5 ++printf "%s\n" "$EXPORT_XROS_DEPLOYMENT_TARGET" >&6; } ++ ++ case "$host_cpu" in ++ aarch64) ++ _host_ident=${XROS_DEPLOYMENT_TARGET}-arm64-xr${_host_device} ++ ;; ++ *) ++ _host_ident=${XROS_DEPLOYMENT_TARGET}-$host_cpu-xr${_host_device} ++ ;; ++ esac + ;; *-*-darwin*) case "$host_cpu" in arm*) -@@ -4688,9 +4808,13 @@ +@@ -4694,9 +4879,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, 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) -@@ -4753,7 +4877,10 @@ +@@ -4759,7 +4950,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 enforced by the selected host triple. ++# WATCHOS_DEPLOYMENT_TARGET / XROS_DEPLOYMENT_TARGET enforced by the selected host triple. ++ + + ++ ++# XROS_DEPLOYMENT_TARGET should get exported # checks for alternative programs -@@ -4794,6 +4921,16 @@ +@@ -4800,6 +4997,16 @@ as_fn_append CFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" as_fn_append LDFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" ;; #( @@ -442,27 +902,29 @@ index d0ae103014a..308124ef06d 100755 *) : ;; esac -@@ -7163,6 +7300,10 @@ +@@ -7169,6 +7376,12 @@ MULTIARCH="" ;; #( iOS) : MULTIARCH="" ;; #( + tvOS) : + MULTIARCH="" ;; #( + watchOS) : ++ MULTIARCH="" ;; #( ++ visionOS) : + MULTIARCH="" ;; #( FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -7183,7 +7324,7 @@ +@@ -7189,7 +7402,7 @@ printf "%s\n" "$MULTIARCH" >&6; } case $ac_sys_system in #( - iOS) : -+ iOS|tvOS|watchOS) : ++ iOS|tvOS|watchOS|visionOS) : SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2` ;; #( *) : SOABI_PLATFORM=$PLATFORM_TRIPLET -@@ -7234,6 +7375,14 @@ +@@ -7240,6 +7453,18 @@ PY_SUPPORT_TIER=3 ;; #( aarch64-apple-ios*/clang) : PY_SUPPORT_TIER=3 ;; #( @@ -473,65 +935,69 @@ index d0ae103014a..308124ef06d 100755 + 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) : -@@ -7670,7 +7819,7 @@ +@@ -7676,7 +7901,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; - iOS) -+ iOS|tvOS|watchOS) ++ iOS|tvOS|watchOS|visionOS) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5;; -@@ -7736,7 +7885,7 @@ +@@ -7742,7 +7967,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; - iOS) -+ iOS|tvOS|watchOS) ++ iOS|tvOS|watchOS|visionOS) LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -13544,7 +13693,7 @@ +@@ -13550,7 +13775,7 @@ BLDSHARED="$LDSHARED" fi ;; - iOS/*) -+ iOS/*|tvOS/*|watchOS/*) ++ iOS/*|tvOS/*|watchOS/*|visionOS/*) LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -13677,7 +13826,7 @@ +@@ -13683,7 +13908,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/*) ++ Darwin/*|iOS/*|tvOS/*|watchOS/*|visionOS/*) LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -13701,7 +13850,7 @@ +@@ -13707,7 +13932,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"; 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 ;; -@@ -15286,7 +15435,7 @@ +@@ -15292,7 +15517,7 @@ ctypes_malloc_closure=yes ;; #( - iOS) : -+ iOS|tvOS|watchOS) : ++ iOS|tvOS|watchOS|visionOS) : ctypes_malloc_closure=yes ;; #( -@@ -19038,12 +19187,6 @@ +@@ -19044,12 +19269,6 @@ then : printf "%s\n" "#define HAVE_DUP3 1" >>confdefs.h @@ -544,7 +1010,7 @@ index d0ae103014a..308124ef06d 100755 fi ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes -@@ -19104,18 +19247,6 @@ +@@ -19110,18 +19329,6 @@ then : printf "%s\n" "#define HAVE_FEXECVE 1" >>confdefs.h @@ -563,7 +1029,7 @@ index d0ae103014a..308124ef06d 100755 fi ac_fn_c_check_func "$LINENO" "fpathconf" "ac_cv_func_fpathconf" if test "x$ac_cv_func_fpathconf" = xyes -@@ -19542,24 +19673,6 @@ +@@ -19548,24 +19755,6 @@ then : printf "%s\n" "#define HAVE_POSIX_OPENPT 1" >>confdefs.h @@ -588,7 +1054,7 @@ index d0ae103014a..308124ef06d 100755 fi ac_fn_c_check_func "$LINENO" "pread" "ac_cv_func_pread" if test "x$ac_cv_func_pread" = xyes -@@ -19866,12 +19979,6 @@ +@@ -19884,12 +20073,6 @@ then : printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h @@ -601,21 +1067,21 @@ index d0ae103014a..308124ef06d 100755 fi ac_fn_c_check_func "$LINENO" "sigfillset" "ac_cv_func_sigfillset" if test "x$ac_cv_func_sigfillset" = xyes -@@ -20140,11 +20247,11 @@ +@@ -20158,11 +20341,11 @@ fi -# iOS defines some system methods that can be linked (so they are -+# iOS/tvOS/watchOS define some system methods that can be linked (so they are ++# 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" ; 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 : -@@ -20166,6 +20273,53 @@ +@@ -20184,6 +20367,53 @@ fi @@ -669,7 +1135,7 @@ index d0ae103014a..308124ef06d 100755 { 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} -@@ -23248,7 +23402,8 @@ +@@ -23266,7 +23496,8 @@ # check for openpty, login_tty, and forkpty @@ -679,7 +1145,7 @@ index d0ae103014a..308124ef06d 100755 for ac_func in openpty do : -@@ -23362,7 +23517,7 @@ +@@ -23380,7 +23611,7 @@ fi done @@ -688,7 +1154,7 @@ index d0ae103014a..308124ef06d 100755 printf %s "checking for library containing login_tty... " >&6; } if test ${ac_cv_search_login_tty+y} then : -@@ -23545,6 +23700,7 @@ +@@ -23563,6 +23794,7 @@ fi done @@ -696,71 +1162,90 @@ index d0ae103014a..308124ef06d 100755 # check for long file support functions ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" -@@ -23810,10 +23966,10 @@ +@@ -23828,10 +24060,10 @@ done -# On Android and iOS, clock_settime can be linked (so it is found by -+# On Android, iOS, tvOS and watchOS, clock_settime can be linked (so it is found by ++# 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" ++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 -@@ -26152,8 +26308,8 @@ +@@ -24148,7 +24380,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" +@@ -26170,8 +26402,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"; then ++if test "$ac_sys_system" = "iOS" -o $ac_sys_system = "tvOS" -o $ac_sys_system = "watchOS" -o $ac_sys_system = "visionOS"; then MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -29023,7 +29179,7 @@ +@@ -29041,7 +29273,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" ; 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 -@@ -29504,7 +29660,7 @@ +@@ -29550,7 +29782,7 @@ with_ensurepip=no ;; #( WASI) : with_ensurepip=no ;; #( - iOS) : -+ iOS|tvOS|watchOS) : ++ iOS|tvOS|watchOS|visionOS) : with_ensurepip=no ;; #( *) : with_ensurepip=upgrade -@@ -30484,7 +30640,7 @@ +@@ -30499,7 +30731,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=;; +@@ -30531,7 +30763,7 @@ ;; #( Darwin) : ;; #( - iOS) : -+ iOS|tvOS|watchOS) : ++ iOS|tvOS|watchOS|visionOS) : -@@ -34487,6 +34643,8 @@ +@@ -34605,6 +34837,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 8bb0f1c6ef4..bfd67de48bb 100644 +index c449bb5ebb3..29b9a82374b 100644 --- a/configure.ac +++ b/configure.ac -@@ -330,6 +330,12 @@ +@@ -330,6 +330,15 @@ *-apple-ios*) ac_sys_system=iOS ;; @@ -769,20 +1254,23 @@ index 8bb0f1c6ef4..bfd67de48bb 100644 + ;; + *-apple-watchos*) + ac_sys_system=watchOS ++ ;; ++ *-apple-xros*) ++ ac_sys_system=visionOS + ;; *-*-darwin*) ac_sys_system=Darwin ;; -@@ -405,7 +411,7 @@ +@@ -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, 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 +426,14 @@ +@@ -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 ;; @@ -794,10 +1282,13 @@ index 8bb0f1c6ef4..bfd67de48bb 100644 + aarch64-apple-watchos*-simulator) AR=arm64-apple-watchos-simulator-ar ;; + aarch64-apple-watchos*) AR=arm64_32-apple-watchos-ar ;; + x86_64-apple-watchos*-simulator) AR=x86_64-apple-watchos-simulator-ar ;; ++ ++ aarch64-apple-xros*-simulator) AR=arm64-apple-xros-simulator-ar ;; ++ aarch64-apple-xros*) AR=arm64-apple-xros-ar ;; *) esac fi -@@ -428,6 +442,14 @@ +@@ -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 ;; @@ -809,10 +1300,13 @@ index 8bb0f1c6ef4..bfd67de48bb 100644 + 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 +458,14 @@ +@@ -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 ;; @@ -824,10 +1318,13 @@ index 8bb0f1c6ef4..bfd67de48bb 100644 + aarch64-apple-watchos*-simulator) CPP=arm64-apple-watchos-simulator-cpp ;; + aarch64-apple-watchos*) CPP=arm64_32-apple-watchos-cpp ;; + x86_64-apple-watchos*-simulator) CPP=x86_64-apple-watchos-simulator-cpp ;; ++ ++ aarch64-apple-xros*-simulator) CPP=arm64-apple-xros-simulator-cpp ;; ++ aarch64-apple-xros*) CPP=arm64-apple-xros-cpp ;; *) esac fi -@@ -444,6 +474,14 @@ +@@ -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++ ;; @@ -839,10 +1336,13 @@ index 8bb0f1c6ef4..bfd67de48bb 100644 + 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 +596,10 @@ +@@ -558,8 +611,11 @@ case $enableval in yes) case $ac_sys_system in @@ -852,19 +1352,21 @@ index 8bb0f1c6ef4..bfd67de48bb 100644 + 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 +608,8 @@ +@@ -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 +712,34 @@ +@@ -670,6 +729,48 @@ AC_CONFIG_FILES([iOS/Resources/Info.plist]) ;; @@ -895,42 +1397,66 @@ index 8bb0f1c6ef4..bfd67de48bb 100644 + RESSRCDIR=watchOS/Resources + + AC_CONFIG_FILES([watchOS/Resources/Info.plist]) ++ ;; ++ visionOS) : ++ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=visionOS/Resources ++ ++ AC_CONFIG_FILES([visionOS/Resources/Info.plist]) + ;; *) AC_MSG_ERROR([Unknown platform for framework build]) ;; -@@ -678,6 +748,8 @@ +@@ -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 +802,8 @@ +@@ -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) -+ # iOS/tvOS/watchOS is able to share the macOS patch ++ Darwin|iOS|tvOS|watchOS|visionOS) ++ # iOS/tvOS/watchOS/visionOS is able to share the macOS patch APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" ;; *) AC_MSG_ERROR([no default app store compliance patch available for $ac_sys_system]) ;; -@@ -745,8 +817,8 @@ +@@ -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) -+ # Always apply the compliance patch on iOS/tvOS/watchOS; we can use the macOS patch ++ iOS|tvOS|watchOS|visionOS) ++ # Always apply the compliance patch on iOS/tvOS/watchOS/visionOS; we can use the macOS patch APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" AC_MSG_RESULT([applying default app store compliance patch]) ;; -@@ -794,6 +866,46 @@ +@@ -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 ;; @@ -973,44 +1499,75 @@ index 8bb0f1c6ef4..bfd67de48bb 100644 + _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-$host_cpu-watch${_host_device} + ;; + esac ++ ;; ++ *-apple-xros*) ++ _host_os=`echo $host | cut -d '-' -f3` ++ _host_device=`echo $host | cut -d '-' -f4` ++ _host_device=${_host_device:=os} ++ ++ # XROS_DEPLOYMENT_TARGET is the minimum supported visionOS version ++ AC_MSG_CHECKING([visionOS deployment target]) ++ XROS_DEPLOYMENT_TARGET=${_host_os:8} ++ XROS_DEPLOYMENT_TARGET=${XROS_DEPLOYMENT_TARGET:=2.0} ++ AC_MSG_RESULT([$XROS_DEPLOYMENT_TARGET]) ++ AC_MSG_CHECKING([exporting flag of visionOS deployment target]) ++ export XROS_DEPLOYMENT_TARGET ++ EXPORT_XROS_DEPLOYMENT_TARGET='' ++ AC_MSG_RESULT([$EXPORT_XROS_DEPLOYMENT_TARGET]) ++ ++ case "$host_cpu" in ++ aarch64) ++ _host_ident=${XROS_DEPLOYMENT_TARGET}-arm64-xr${_host_device} ++ ;; ++ *) ++ _host_ident=${XROS_DEPLOYMENT_TARGET}-$host_cpu-xr${_host_device} ++ ;; ++ esac + ;; *-*-darwin*) case "$host_cpu" in arm*) -@@ -883,9 +995,13 @@ +@@ -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, 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 +1060,11 @@ +@@ -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 enforced by the selected host triple. ++# WATCHOS_DEPLOYMENT_TARGET / XROS_DEPLOYMENT_TARGET enforced by the selected host triple. AC_SUBST([IPHONEOS_DEPLOYMENT_TARGET]) +AC_SUBST([TVOS_DEPLOYMENT_TARGET]) +AC_SUBST([WATCHOS_DEPLOYMENT_TARGET]) ++AC_SUBST([XROS_DEPLOYMENT_TARGET]) ++# XROS_DEPLOYMENT_TARGET should get exported ++AC_SUBST([EXPORT_XROS_DEPLOYMENT_TARGET]) # checks for alternative programs -@@ -979,11 +1098,17 @@ +@@ -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 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}"]) @@ -1024,25 +1581,26 @@ index 8bb0f1c6ef4..bfd67de48bb 100644 ], ) -@@ -1172,6 +1297,8 @@ +@@ -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 +1320,7 @@ +@@ -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], [SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2`], ++ [iOS|tvOS|watchOS|visionOS], [SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2`], [SOABI_PLATFORM=$PLATFORM_TRIPLET] ) -@@ -1227,6 +1354,10 @@ +@@ -1227,6 +1420,12 @@ [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 @@ -1050,64 +1608,66 @@ index 8bb0f1c6ef4..bfd67de48bb 100644 + [aarch64-apple-tvos*/clang], [PY_SUPPORT_TIER=3], dnl tvOS on ARM64 + [aarch64-apple-watchos*-simulator/clang], [PY_SUPPORT_TIER=3], dnl watchOS Simulator on arm64 + [arm64_32-apple-watchos*/clang], [PY_SUPPORT_TIER=3], dnl watchOS on ARM64 ++ [aarch64-apple-xros*-simulator/clang], [PY_SUPPORT_TIER=3], dnl visionOS Simulator on arm64 ++ [aarch64-apple-xros*/clang], [PY_SUPPORT_TIER=3], dnl visionOS on ARM64 [aarch64-*-linux-android/clang], [PY_SUPPORT_TIER=3], dnl Android on ARM64 [x86_64-*-linux-android/clang], [PY_SUPPORT_TIER=3], dnl Android on AMD64 -@@ -1536,7 +1667,7 @@ +@@ -1536,7 +1735,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; - iOS) -+ iOS|tvOS|watchOS) ++ iOS|tvOS|watchOS|visionOS) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) AC_MSG_ERROR([Unknown platform for framework build]);; -@@ -1601,7 +1732,7 @@ +@@ -1601,7 +1800,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; - iOS) -+ iOS|tvOS|watchOS) ++ iOS|tvOS|watchOS|visionOS) LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -3456,7 +3587,7 @@ +@@ -3456,7 +3655,7 @@ BLDSHARED="$LDSHARED" fi ;; - iOS/*) -+ iOS/*|tvOS/*|watchOS/*) ++ iOS/*|tvOS/*|watchOS/*|visionOS/*) LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -3580,7 +3711,7 @@ +@@ -3580,7 +3779,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/*) ++ Darwin/*|iOS/*|tvOS/*|watchOS/*|visionOS/*) LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -3604,7 +3735,7 @@ +@@ -3604,7 +3803,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"; 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 ;; -@@ -4024,7 +4155,7 @@ +@@ -4024,7 +4223,7 @@ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], - [iOS], [ -+ [iOS|tvOS|watchOS], [ ++ [iOS|tvOS|watchOS|visionOS], [ ctypes_malloc_closure=yes ], [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] -@@ -5133,9 +5264,9 @@ +@@ -5133,9 +5332,9 @@ # checks for library functions AC_CHECK_FUNCS([ \ accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ @@ -1119,7 +1679,7 @@ index 8bb0f1c6ef4..bfd67de48bb 100644 gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \ getgrnam_r getgrouplist gethostname getitimer getloadavg getlogin \ getpeername getpgid getpid getppid getpriority _getpty \ -@@ -5143,8 +5274,7 @@ +@@ -5143,8 +5342,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 \ @@ -1128,8 +1688,8 @@ index 8bb0f1c6ef4..bfd67de48bb 100644 + 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_getname_np pthread_setname_np pthread_getattr_np \ -@@ -5153,7 +5283,7 @@ + pthread_kill pthread_get_name_np pthread_getname_np pthread_set_name_np +@@ -5154,7 +5352,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 \ @@ -1138,18 +1698,18 @@ index 8bb0f1c6ef4..bfd67de48bb 100644 sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ -@@ -5168,12 +5298,20 @@ +@@ -5169,12 +5367,20 @@ AC_CHECK_FUNCS([lchmod]) fi -# iOS defines some system methods that can be linked (so they are -+# iOS/tvOS/watchOS define some system methods that can be linked (so they are ++# 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" ; then ++if test "$ac_sys_system" != "iOS" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" -a "$ac_sys_system" != "visionOS" ; then + AC_CHECK_FUNCS([ getentropy getgroups system ]) +fi + @@ -1162,7 +1722,7 @@ index 8bb0f1c6ef4..bfd67de48bb 100644 fi AC_CHECK_DECL([dirfd], -@@ -5427,20 +5565,22 @@ +@@ -5428,20 +5634,22 @@ ]) # check for openpty, login_tty, and forkpty @@ -1199,54 +1759,72 @@ index 8bb0f1c6ef4..bfd67de48bb 100644 # check for long file support functions AC_CHECK_FUNCS([fseek64 fseeko fstatvfs ftell64 ftello statvfs]) -@@ -5479,10 +5619,10 @@ +@@ -5480,10 +5688,10 @@ ]) ]) -# On Android and iOS, clock_settime can be linked (so it is found by -+# On Android, iOS, tvOS and watchOS, clock_settime can be linked (so it is found by ++# 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" ++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], [ -@@ -6233,8 +6373,8 @@ +@@ -5641,7 +5849,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" +@@ -6234,8 +6442,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"; then ++if test "$ac_sys_system" = "iOS" -o $ac_sys_system = "tvOS" -o $ac_sys_system = "watchOS" -o $ac_sys_system = "visionOS"; then MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -6893,7 +7033,7 @@ +@@ -6894,7 +7102,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" ; 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 -@@ -7174,7 +7314,7 @@ +@@ -7195,7 +7403,7 @@ AS_CASE([$ac_sys_system], [Emscripten], [with_ensurepip=no], [WASI], [with_ensurepip=no], - [iOS], [with_ensurepip=no], -+ [iOS|tvOS|watchOS], [with_ensurepip=no], ++ [iOS|tvOS|watchOS|visionOS], [with_ensurepip=no], [with_ensurepip=upgrade] ) ]) -@@ -7585,7 +7725,7 @@ +@@ -7582,7 +7790,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=;; +@@ -7607,7 +7815,7 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], - [iOS], [ -+ [iOS|tvOS|watchOS], [ ++ [iOS|tvOS|watchOS|visionOS], [ dnl subprocess and multiprocessing are not supported (no fork syscall). dnl curses and tkinter user interface are not available. dnl gdbm and nis aren't available @@ -1518,6 +2096,1624 @@ index c3e261ecd9e..26ef7a95de4 100644 +#include "pyconfig-x86_64.h" +#endif --- /dev/null ++++ b/visionOS/Resources/Info.plist.in +@@ -0,0 +1,34 @@ ++ ++ ++ ++ ++ CFBundleDevelopmentRegion ++ en ++ CFBundleExecutable ++ Python ++ CFBundleGetInfoString ++ Python Runtime and Library ++ CFBundleIdentifier ++ @PYTHONFRAMEWORKIDENTIFIER@ ++ CFBundleInfoDictionaryVersion ++ 6.0 ++ CFBundleName ++ Python ++ CFBundlePackageType ++ FMWK ++ CFBundleShortVersionString ++ %VERSION% ++ CFBundleLongVersionString ++ %VERSION%, (c) 2001-2023 Python Software Foundation. ++ CFBundleSignature ++ ???? ++ CFBundleVersion ++ %VERSION% ++ CFBundleSupportedPlatforms ++ ++ xrOS ++ ++ MinimumOSVersion ++ @XROS_DEPLOYMENT_TARGET@ ++ ++ +--- /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 "$@" +--- /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 "$@" +--- /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 "$@" +--- /dev/null ++++ b/visionOS/Resources/dylib-Info-template.plist +@@ -0,0 +1,26 @@ ++ ++ ++ ++ ++ CFBundleDevelopmentRegion ++ en ++ CFBundleExecutable ++ ++ CFBundleIdentifier ++ ++ CFBundleInfoDictionaryVersion ++ 6.0 ++ CFBundlePackageType ++ APPL ++ CFBundleShortVersionString ++ 1.0 ++ CFBundleSupportedPlatforms ++ ++ xrOS ++ ++ MinimumOSVersion ++ 2.0 ++ CFBundleVersion ++ 1 ++ ++ +--- /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 ++ ++ +--- /dev/null ++++ b/visionOS/testbed/__main__.py +@@ -0,0 +1,512 @@ ++import argparse ++import asyncio ++import fcntl ++import json ++import os ++import plistlib ++import re ++import shutil ++import subprocess ++import sys ++import tempfile ++from contextlib import asynccontextmanager ++from datetime import datetime ++from pathlib import Path ++ ++ ++DECODE_ARGS = ("UTF-8", "backslashreplace") ++ ++# The system log prefixes each line: ++# 2025-01-17 16:14:29.090 Df visionOSTestbed[23987:1fd393b4] (Python) ... ++# 2025-01-17 16:14:29.090 E visionOSTestbed[23987:1fd393b4] (Python) ... ++ ++LOG_PREFIX_REGEX = re.compile( ++ r"^\d{4}-\d{2}-\d{2}" # YYYY-MM-DD ++ r"\s+\d+:\d{2}:\d{2}\.\d+" # HH:MM:SS.sss ++ r"\s+\w+" # Df/E ++ r"\s+visionOSTestbed\[\d+:\w+\]" # Process/thread ID ++ r"\s+\(Python\)\s" # Logger name ++) ++ ++ ++# Work around a bug involving sys.exit and TaskGroups ++# (https://github.com/python/cpython/issues/101515). ++def exit(*args): ++ raise MySystemExit(*args) ++ ++ ++class MySystemExit(Exception): ++ pass ++ ++ ++class SimulatorLock: ++ # An fcntl-based filesystem lock that can be used to ensure that ++ def __init__(self, timeout): ++ self.filename = Path(tempfile.gettempdir()) / "python-visionos-testbed" ++ self.timeout = timeout ++ ++ self.fd = None ++ ++ async def acquire(self): ++ # Ensure the lockfile exists ++ self.filename.touch(exist_ok=True) ++ ++ # Try `timeout` times to acquire the lock file, with a 1 second pause ++ # between each attempt. Report status every 10 seconds. ++ for i in range(0, self.timeout): ++ try: ++ fd = os.open(self.filename, os.O_RDWR | os.O_TRUNC, 0o644) ++ fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) ++ except OSError: ++ os.close(fd) ++ if i % 10 == 0: ++ print("... waiting", flush=True) ++ await asyncio.sleep(1) ++ else: ++ self.fd = fd ++ return ++ ++ # If we reach the end of the loop, we've exceeded the allowed number of ++ # attempts. ++ raise ValueError("Unable to obtain lock on visionOS simulator creation") ++ ++ def release(self): ++ # If a lock is held, release it. ++ if self.fd is not None: ++ # Release the lock. ++ fcntl.flock(self.fd, fcntl.LOCK_UN) ++ os.close(self.fd) ++ self.fd = None ++ ++ ++# All subprocesses are executed through this context manager so that no matter ++# what happens, they can always be cancelled from another task, and they will ++# always be cleaned up on exit. ++@asynccontextmanager ++async def async_process(*args, **kwargs): ++ process = await asyncio.create_subprocess_exec(*args, **kwargs) ++ try: ++ yield process ++ finally: ++ if process.returncode is None: ++ # Allow a reasonably long time for Xcode to clean itself up, ++ # because we don't want stale emulators left behind. ++ timeout = 10 ++ process.terminate() ++ try: ++ await asyncio.wait_for(process.wait(), timeout) ++ except TimeoutError: ++ print( ++ f"Command {args} did not terminate after {timeout} seconds " ++ f" - sending SIGKILL" ++ ) ++ process.kill() ++ ++ # Even after killing the process we must still wait for it, ++ # otherwise we'll get the warning "Exception ignored in __del__". ++ await asyncio.wait_for(process.wait(), timeout=1) ++ ++ ++async def async_check_output(*args, **kwargs): ++ async with async_process( ++ *args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs ++ ) as process: ++ stdout, stderr = await process.communicate() ++ if process.returncode == 0: ++ return stdout.decode(*DECODE_ARGS) ++ else: ++ raise subprocess.CalledProcessError( ++ process.returncode, ++ args, ++ stdout.decode(*DECODE_ARGS), ++ stderr.decode(*DECODE_ARGS), ++ ) ++ ++ ++# Return a list of UDIDs associated with booted simulators ++async def list_devices(): ++ try: ++ # List the testing simulators, in JSON format ++ raw_json = await async_check_output( ++ "xcrun", "simctl", "--set", "testing", "list", "-j" ++ ) ++ json_data = json.loads(raw_json) ++ ++ # Filter out the booted visionOS simulators ++ return [ ++ simulator["udid"] ++ for runtime, simulators in json_data["devices"].items() ++ for simulator in simulators ++ if runtime.split(".")[-1].startswith("xrOS") and simulator["state"] == "Booted" ++ ] ++ except subprocess.CalledProcessError as e: ++ # If there's no ~/Library/Developer/XCTestDevices folder (which is the ++ # case on fresh installs, and in some CI environments), `simctl list` ++ # returns error code 1, rather than an empty list. Handle that case, ++ # but raise all other errors. ++ if e.returncode == 1: ++ return [] ++ else: ++ raise ++ ++ ++async def find_device(initial_devices, lock): ++ while True: ++ new_devices = set(await list_devices()).difference(initial_devices) ++ if len(new_devices) == 0: ++ await asyncio.sleep(1) ++ elif len(new_devices) == 1: ++ udid = new_devices.pop() ++ print(f"{datetime.now():%Y-%m-%d %H:%M:%S}: New test simulator detected") ++ print(f"UDID: {udid}", flush=True) ++ lock.release() ++ return udid ++ else: ++ exit(f"Found more than one new device: {new_devices}") ++ ++ ++async def log_stream_task(initial_devices, lock): ++ # Wait up to 5 minutes for the build to complete and the simulator to boot. ++ udid = await asyncio.wait_for(find_device(initial_devices, lock), 5 * 60) ++ ++ # Stream the visionOS device's logs, filtering out messages that come from the ++ # XCTest test suite (catching NSLog messages from the test method), or ++ # Python itself (catching stdout/stderr content routed to the system log ++ # with config->use_system_logger). ++ args = [ ++ "xcrun", ++ "simctl", ++ "--set", ++ "testing", ++ "spawn", ++ udid, ++ "log", ++ "stream", ++ "--style", ++ "compact", ++ "--predicate", ++ ( ++ 'senderImagePath ENDSWITH "/visionOSTestbedTests.xctest/visionOSTestbedTests"' ++ ' OR senderImagePath ENDSWITH "/Python.framework/Python"' ++ ), ++ ] ++ ++ async with async_process( ++ *args, ++ stdout=subprocess.PIPE, ++ stderr=subprocess.STDOUT, ++ ) as process: ++ suppress_dupes = False ++ while line := (await process.stdout.readline()).decode(*DECODE_ARGS): ++ # Strip the prefix from each log line ++ line = LOG_PREFIX_REGEX.sub("", line) ++ # The visionOS log streamer can sometimes lag; when it does, it outputs ++ # a warning about messages being dropped... often multiple times. ++ # Only print the first of these duplicated warnings. ++ if line.startswith("=== Messages dropped "): ++ if not suppress_dupes: ++ suppress_dupes = True ++ sys.stdout.write(line) ++ else: ++ suppress_dupes = False ++ sys.stdout.write(line) ++ sys.stdout.flush() ++ ++ ++async def xcode_test(location, simulator, verbose): ++ # Run the test suite on the named simulator ++ print("Starting xcodebuild...", flush=True) ++ args = [ ++ "xcodebuild", ++ "test", ++ "-project", ++ str(location / "visionOSTestbed.xcodeproj"), ++ "-scheme", ++ "visionOSTestbed", ++ "-destination", ++ f"platform=visionOS Simulator,name={simulator}", ++ "-resultBundlePath", ++ str(location / f"{datetime.now():%Y%m%d-%H%M%S}.xcresult"), ++ "-derivedDataPath", ++ str(location / "DerivedData"), ++ ] ++ if not verbose: ++ args += ["-quiet"] ++ ++ async with async_process( ++ *args, ++ stdout=subprocess.PIPE, ++ stderr=subprocess.STDOUT, ++ ) as process: ++ while line := (await process.stdout.readline()).decode(*DECODE_ARGS): ++ sys.stdout.write(line) ++ sys.stdout.flush() ++ ++ status = await asyncio.wait_for(process.wait(), timeout=1) ++ exit(status) ++ ++ ++def clone_testbed( ++ source: Path, ++ target: Path, ++ framework: Path, ++ apps: list[Path], ++) -> None: ++ if target.exists(): ++ print(f"{target} already exists; aborting without creating project.") ++ sys.exit(10) ++ ++ if framework is None: ++ if not ( ++ source / "Python.xcframework/xros-arm64-simulator/bin" ++ ).is_dir(): ++ print( ++ f"The testbed being cloned ({source}) does not contain " ++ f"a simulator framework. Re-run with --framework" ++ ) ++ sys.exit(11) ++ else: ++ if not framework.is_dir(): ++ print(f"{framework} does not exist.") ++ sys.exit(12) ++ elif not ( ++ framework.suffix == ".xcframework" ++ or (framework / "Python.framework").is_dir() ++ ): ++ print( ++ f"{framework} is not an XCframework, " ++ f"or a simulator slice of a framework build." ++ ) ++ sys.exit(13) ++ ++ print("Cloning testbed project:") ++ print(f" Cloning {source}...", end="", flush=True) ++ shutil.copytree(source, target, symlinks=True) ++ print(" done") ++ ++ xc_framework_path = target / "Python.xcframework" ++ sim_framework_path = xc_framework_path / "xros-arm64-simulator" ++ if framework is not None: ++ if framework.suffix == ".xcframework": ++ print(" Installing XCFramework...", end="", flush=True) ++ if xc_framework_path.is_dir(): ++ shutil.rmtree(xc_framework_path) ++ else: ++ xc_framework_path.unlink(missing_ok=True) ++ xc_framework_path.symlink_to( ++ framework.relative_to(xc_framework_path.parent, walk_up=True) ++ ) ++ print(" done") ++ else: ++ print(" Installing simulator framework...", end="", flush=True) ++ if sim_framework_path.is_dir(): ++ shutil.rmtree(sim_framework_path) ++ else: ++ sim_framework_path.unlink(missing_ok=True) ++ sim_framework_path.symlink_to( ++ framework.relative_to(sim_framework_path.parent, walk_up=True) ++ ) ++ print(" done") ++ else: ++ if ( ++ xc_framework_path.is_symlink() ++ and not xc_framework_path.readlink().is_absolute() ++ ): ++ # XCFramework is a relative symlink. Rewrite the symlink relative ++ # to the new location. ++ print(" Rewriting symlink to XCframework...", end="", flush=True) ++ orig_xc_framework_path = ( ++ source ++ / xc_framework_path.readlink() ++ ).resolve() ++ xc_framework_path.unlink() ++ xc_framework_path.symlink_to( ++ orig_xc_framework_path.relative_to( ++ xc_framework_path.parent, walk_up=True ++ ) ++ ) ++ print(" done") ++ elif ( ++ sim_framework_path.is_symlink() ++ and not sim_framework_path.readlink().is_absolute() ++ ): ++ print(" Rewriting symlink to simulator framework...", end="", flush=True) ++ # Simulator framework is a relative symlink. Rewrite the symlink ++ # relative to the new location. ++ orig_sim_framework_path = ( ++ source ++ / "Python.XCframework" ++ / sim_framework_path.readlink() ++ ).resolve() ++ sim_framework_path.unlink() ++ sim_framework_path.symlink_to( ++ orig_sim_framework_path.relative_to( ++ sim_framework_path.parent, walk_up=True ++ ) ++ ) ++ print(" done") ++ else: ++ print(" Using pre-existing visionOS framework.") ++ ++ for app_src in apps: ++ print(f" Installing app {app_src.name!r}...", end="", flush=True) ++ app_target = target / f"visionOSTestbed/app/{app_src.name}" ++ if app_target.is_dir(): ++ shutil.rmtree(app_target) ++ shutil.copytree(app_src, app_target) ++ print(" done") ++ ++ print(f"Successfully cloned testbed: {target.resolve()}") ++ ++ ++def update_plist(testbed_path, args): ++ # Add the test runner arguments to the testbed's Info.plist file. ++ info_plist = testbed_path / "visionOSTestbed" / "visionOSTestbed-Info.plist" ++ with info_plist.open("rb") as f: ++ info = plistlib.load(f) ++ ++ info["TestArgs"] = args ++ ++ with info_plist.open("wb") as f: ++ plistlib.dump(info, f) ++ ++ ++async def run_testbed(simulator: str, args: list[str], verbose: bool=False): ++ location = Path(__file__).parent ++ print("Updating plist...", end="", flush=True) ++ update_plist(location, args) ++ print(" done.", flush=True) ++ ++ # We need to get an exclusive lock on simulator creation, to avoid issues ++ # with multiple simulators starting and being unable to tell which ++ # simulator is due to which testbed instance. See ++ # https://github.com/python/cpython/issues/130294 for details. Wait up to ++ # 10 minutes for a simulator to boot. ++ print("Obtaining lock on simulator creation...", flush=True) ++ simulator_lock = SimulatorLock(timeout=10*60) ++ await simulator_lock.acquire() ++ print("Simulator lock acquired.", flush=True) ++ ++ # Get the list of devices that are booted at the start of the test run. ++ # The simulator started by the test suite will be detected as the new ++ # entry that appears on the device list. ++ initial_devices = await list_devices() ++ ++ try: ++ async with asyncio.TaskGroup() as tg: ++ tg.create_task(log_stream_task(initial_devices, simulator_lock)) ++ tg.create_task(xcode_test(location, simulator=simulator, verbose=verbose)) ++ except* MySystemExit as e: ++ raise SystemExit(*e.exceptions[0].args) from None ++ except* subprocess.CalledProcessError as e: ++ # Extract it from the ExceptionGroup so it can be handled by `main`. ++ raise e.exceptions[0] ++ finally: ++ simulator_lock.release() ++ ++ ++def main(): ++ parser = argparse.ArgumentParser( ++ description=( ++ "Manages the process of testing a Python project in the visionOS simulator." ++ ), ++ ) ++ ++ subcommands = parser.add_subparsers(dest="subcommand") ++ ++ clone = subcommands.add_parser( ++ "clone", ++ description=( ++ "Clone the testbed project, copying in an visionOS Python framework and" ++ "any specified application code." ++ ), ++ help="Clone a testbed project to a new location.", ++ ) ++ clone.add_argument( ++ "--framework", ++ help=( ++ "The location of the XCFramework (or simulator-only slice of an " ++ "XCFramework) to use when running the testbed" ++ ), ++ ) ++ clone.add_argument( ++ "--app", ++ dest="apps", ++ action="append", ++ default=[], ++ help="The location of any code to include in the testbed project", ++ ) ++ clone.add_argument( ++ "location", ++ help="The path where the testbed will be cloned.", ++ ) ++ ++ run = subcommands.add_parser( ++ "run", ++ usage="%(prog)s [-h] [--simulator SIMULATOR] -- [ ...]", ++ description=( ++ "Run a testbed project. The arguments provided after `--` will be " ++ "passed to the running visionOS process as if they were arguments to " ++ "`python -m`." ++ ), ++ help="Run a testbed project", ++ ) ++ run.add_argument( ++ "--simulator", ++ default="Apple Vision Pro", ++ help="The name of the simulator to use (default: 'Apple Vision Pro')", ++ ) ++ run.add_argument( ++ "-v", "--verbose", ++ action="store_true", ++ help="Enable verbose output", ++ ) ++ ++ try: ++ pos = sys.argv.index("--") ++ testbed_args = sys.argv[1:pos] ++ test_args = sys.argv[pos + 1 :] ++ except ValueError: ++ testbed_args = sys.argv[1:] ++ test_args = [] ++ ++ context = parser.parse_args(testbed_args) ++ ++ if context.subcommand == "clone": ++ clone_testbed( ++ source=Path(__file__).parent.resolve(), ++ target=Path(context.location).resolve(), ++ framework=Path(context.framework).resolve() if context.framework else None, ++ apps=[Path(app) for app in context.apps], ++ ) ++ elif context.subcommand == "run": ++ if test_args: ++ if not ( ++ Path(__file__).parent / "Python.xcframework/xros-arm64-simulator/bin" ++ ).is_dir(): ++ print( ++ f"Testbed does not contain a compiled visionOS framework. Use " ++ f"`python {sys.argv[0]} clone ...` to create a runnable " ++ f"clone of this testbed." ++ ) ++ sys.exit(20) ++ ++ asyncio.run( ++ run_testbed( ++ simulator=context.simulator, ++ verbose=context.verbose, ++ args=test_args, ++ ) ++ ) ++ else: ++ print(f"Must specify test arguments (e.g., {sys.argv[0]} run -- test)") ++ print() ++ parser.print_help(sys.stderr) ++ sys.exit(21) ++ else: ++ parser.print_help(sys.stderr) ++ sys.exit(1) ++ ++ ++if __name__ == "__main__": ++ main() +--- /dev/null ++++ b/visionOS/testbed/visionOSTestbed.xcodeproj/project.pbxproj +@@ -0,0 +1,581 @@ ++// !$*UTF8*$! ++{ ++ archiveVersion = 1; ++ classes = { ++ }; ++ objectVersion = 56; ++ objects = { ++ ++/* Begin PBXBuildFile section */ ++ 607A66172B0EFA380010BFC8 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66162B0EFA380010BFC8 /* AppDelegate.m */; }; ++ 607A66222B0EFA390010BFC8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607A66212B0EFA390010BFC8 /* Assets.xcassets */; }; ++ 607A66282B0EFA390010BFC8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66272B0EFA390010BFC8 /* main.m */; }; ++ 607A66322B0EFA3A0010BFC8 /* visionOSTestbedTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66312B0EFA3A0010BFC8 /* visionOSTestbedTests.m */; }; ++ 607A66582B0F079F0010BFC8 /* dylib-Info-template.plist in Resources */ = {isa = PBXBuildFile; fileRef = 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */; }; ++ 608619542CB77BA900F46182 /* app_packages in Resources */ = {isa = PBXBuildFile; fileRef = 608619532CB77BA900F46182 /* app_packages */; }; ++ 608619562CB7819B00F46182 /* app in Resources */ = {isa = PBXBuildFile; fileRef = 608619552CB7819B00F46182 /* app */; }; ++ EEB367CE2DADF5C900B9A1D7 /* Python.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EEE9C80C2DAB5ECA0056F8C6 /* Python.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; ++ EEB367CF2DADF5D300B9A1D7 /* Python.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EEE9C80C2DAB5ECA0056F8C6 /* Python.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; ++ EEE9C80D2DAB5ECA0056F8C6 /* Python.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EEE9C80C2DAB5ECA0056F8C6 /* Python.xcframework */; }; ++ EEE9C80E2DAB5ECA0056F8C6 /* Python.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EEE9C80C2DAB5ECA0056F8C6 /* Python.xcframework */; }; ++/* End PBXBuildFile section */ ++ ++/* Begin PBXContainerItemProxy section */ ++ 607A662E2B0EFA3A0010BFC8 /* PBXContainerItemProxy */ = { ++ isa = PBXContainerItemProxy; ++ containerPortal = 607A660A2B0EFA380010BFC8 /* Project object */; ++ proxyType = 1; ++ remoteGlobalIDString = 607A66112B0EFA380010BFC8; ++ remoteInfo = iOSTestbed; ++ }; ++/* End PBXContainerItemProxy section */ ++ ++/* Begin PBXCopyFilesBuildPhase section */ ++ 607A664E2B0EFC080010BFC8 /* Embed Frameworks */ = { ++ isa = PBXCopyFilesBuildPhase; ++ buildActionMask = 2147483647; ++ dstPath = ""; ++ dstSubfolderSpec = 10; ++ files = ( ++ EEB367CF2DADF5D300B9A1D7 /* Python.xcframework in Embed Frameworks */, ++ ); ++ name = "Embed Frameworks"; ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++ 607A66522B0EFFE00010BFC8 /* Embed Frameworks */ = { ++ isa = PBXCopyFilesBuildPhase; ++ buildActionMask = 2147483647; ++ dstPath = ""; ++ dstSubfolderSpec = 10; ++ files = ( ++ EEB367CE2DADF5C900B9A1D7 /* Python.xcframework in Embed Frameworks */, ++ ); ++ name = "Embed Frameworks"; ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++/* End PBXCopyFilesBuildPhase section */ ++ ++/* Begin PBXFileReference section */ ++ 607A66122B0EFA380010BFC8 /* visionOSTestbed.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = visionOSTestbed.app; sourceTree = BUILT_PRODUCTS_DIR; }; ++ 607A66152B0EFA380010BFC8 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; ++ 607A66162B0EFA380010BFC8 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; ++ 607A66212B0EFA390010BFC8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; ++ 607A66272B0EFA390010BFC8 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; ++ 607A662D2B0EFA3A0010BFC8 /* visionOSTestbedTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = visionOSTestbedTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; ++ 607A66312B0EFA3A0010BFC8 /* visionOSTestbedTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = visionOSTestbedTests.m; sourceTree = ""; }; ++ 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "dylib-Info-template.plist"; sourceTree = ""; }; ++ 607A66592B0F08600010BFC8 /* visionOSTestbed-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "visionOSTestbed-Info.plist"; sourceTree = ""; }; ++ 608619532CB77BA900F46182 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = ""; }; ++ 608619552CB7819B00F46182 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = ""; }; ++ EEE9C80C2DAB5ECA0056F8C6 /* Python.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Python.xcframework; sourceTree = ""; }; ++/* End PBXFileReference section */ ++ ++/* Begin PBXFrameworksBuildPhase section */ ++ 607A660F2B0EFA380010BFC8 /* Frameworks */ = { ++ isa = PBXFrameworksBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ EEE9C80D2DAB5ECA0056F8C6 /* Python.xcframework in Frameworks */, ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++ 607A662A2B0EFA3A0010BFC8 /* Frameworks */ = { ++ isa = PBXFrameworksBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ EEE9C80E2DAB5ECA0056F8C6 /* Python.xcframework in Frameworks */, ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++/* End PBXFrameworksBuildPhase section */ ++ ++/* Begin PBXGroup section */ ++ 607A66092B0EFA380010BFC8 = { ++ isa = PBXGroup; ++ children = ( ++ EEE9C80C2DAB5ECA0056F8C6 /* Python.xcframework */, ++ 607A66142B0EFA380010BFC8 /* visionOSTestbed */, ++ 607A66302B0EFA3A0010BFC8 /* visionOSTestbedTests */, ++ 607A66132B0EFA380010BFC8 /* Products */, ++ 607A664F2B0EFFE00010BFC8 /* Frameworks */, ++ ); ++ sourceTree = ""; ++ }; ++ 607A66132B0EFA380010BFC8 /* Products */ = { ++ isa = PBXGroup; ++ children = ( ++ 607A66122B0EFA380010BFC8 /* visionOSTestbed.app */, ++ 607A662D2B0EFA3A0010BFC8 /* visionOSTestbedTests.xctest */, ++ ); ++ name = Products; ++ sourceTree = ""; ++ }; ++ 607A66142B0EFA380010BFC8 /* visionOSTestbed */ = { ++ isa = PBXGroup; ++ children = ( ++ 608619552CB7819B00F46182 /* app */, ++ 608619532CB77BA900F46182 /* app_packages */, ++ 607A66592B0F08600010BFC8 /* visionOSTestbed-Info.plist */, ++ 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */, ++ 607A66152B0EFA380010BFC8 /* AppDelegate.h */, ++ 607A66162B0EFA380010BFC8 /* AppDelegate.m */, ++ 607A66212B0EFA390010BFC8 /* Assets.xcassets */, ++ 607A66272B0EFA390010BFC8 /* main.m */, ++ ); ++ path = visionOSTestbed; ++ sourceTree = ""; ++ }; ++ 607A66302B0EFA3A0010BFC8 /* visionOSTestbedTests */ = { ++ isa = PBXGroup; ++ children = ( ++ 607A66312B0EFA3A0010BFC8 /* visionOSTestbedTests.m */, ++ ); ++ path = visionOSTestbedTests; ++ sourceTree = ""; ++ }; ++ 607A664F2B0EFFE00010BFC8 /* Frameworks */ = { ++ isa = PBXGroup; ++ children = ( ++ ); ++ name = Frameworks; ++ sourceTree = ""; ++ }; ++/* End PBXGroup section */ ++ ++/* Begin PBXNativeTarget section */ ++ 607A66112B0EFA380010BFC8 /* visionOSTestbed */ = { ++ isa = PBXNativeTarget; ++ buildConfigurationList = 607A66412B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "visionOSTestbed" */; ++ buildPhases = ( ++ 607A660E2B0EFA380010BFC8 /* Sources */, ++ 607A660F2B0EFA380010BFC8 /* Frameworks */, ++ 607A66102B0EFA380010BFC8 /* Resources */, ++ 607A66552B0F061D0010BFC8 /* Install Target Specific Python Standard Library */, ++ 607A66562B0F06200010BFC8 /* Prepare Python Binary Modules */, ++ 607A664E2B0EFC080010BFC8 /* Embed Frameworks */, ++ ); ++ buildRules = ( ++ ); ++ dependencies = ( ++ ); ++ name = visionOSTestbed; ++ productName = iOSTestbed; ++ productReference = 607A66122B0EFA380010BFC8 /* visionOSTestbed.app */; ++ productType = "com.apple.product-type.application"; ++ }; ++ 607A662C2B0EFA3A0010BFC8 /* visionOSTestbedTests */ = { ++ isa = PBXNativeTarget; ++ buildConfigurationList = 607A66442B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "visionOSTestbedTests" */; ++ buildPhases = ( ++ 607A66292B0EFA3A0010BFC8 /* Sources */, ++ 607A662A2B0EFA3A0010BFC8 /* Frameworks */, ++ 607A662B2B0EFA3A0010BFC8 /* Resources */, ++ 607A66522B0EFFE00010BFC8 /* Embed Frameworks */, ++ ); ++ buildRules = ( ++ ); ++ dependencies = ( ++ 607A662F2B0EFA3A0010BFC8 /* PBXTargetDependency */, ++ ); ++ name = visionOSTestbedTests; ++ productName = iOSTestbedTests; ++ productReference = 607A662D2B0EFA3A0010BFC8 /* visionOSTestbedTests.xctest */; ++ productType = "com.apple.product-type.bundle.unit-test"; ++ }; ++/* End PBXNativeTarget section */ ++ ++/* Begin PBXProject section */ ++ 607A660A2B0EFA380010BFC8 /* Project object */ = { ++ isa = PBXProject; ++ attributes = { ++ BuildIndependentTargetsInParallel = 1; ++ LastUpgradeCheck = 1500; ++ TargetAttributes = { ++ 607A66112B0EFA380010BFC8 = { ++ CreatedOnToolsVersion = 15.0.1; ++ }; ++ 607A662C2B0EFA3A0010BFC8 = { ++ CreatedOnToolsVersion = 15.0.1; ++ TestTargetID = 607A66112B0EFA380010BFC8; ++ }; ++ }; ++ }; ++ buildConfigurationList = 607A660D2B0EFA380010BFC8 /* Build configuration list for PBXProject "visionOSTestbed" */; ++ compatibilityVersion = "Xcode 14.0"; ++ developmentRegion = en; ++ hasScannedForEncodings = 0; ++ knownRegions = ( ++ en, ++ Base, ++ ); ++ mainGroup = 607A66092B0EFA380010BFC8; ++ productRefGroup = 607A66132B0EFA380010BFC8 /* Products */; ++ projectDirPath = ""; ++ projectRoot = ""; ++ targets = ( ++ 607A66112B0EFA380010BFC8 /* visionOSTestbed */, ++ 607A662C2B0EFA3A0010BFC8 /* visionOSTestbedTests */, ++ ); ++ }; ++/* End PBXProject section */ ++ ++/* Begin PBXResourcesBuildPhase section */ ++ 607A66102B0EFA380010BFC8 /* Resources */ = { ++ isa = PBXResourcesBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ 607A66582B0F079F0010BFC8 /* dylib-Info-template.plist in Resources */, ++ 608619562CB7819B00F46182 /* app in Resources */, ++ 607A66222B0EFA390010BFC8 /* Assets.xcassets in Resources */, ++ 608619542CB77BA900F46182 /* app_packages in Resources */, ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++ 607A662B2B0EFA3A0010BFC8 /* Resources */ = { ++ isa = PBXResourcesBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++/* End PBXResourcesBuildPhase section */ ++ ++/* Begin PBXShellScriptBuildPhase section */ ++ 607A66552B0F061D0010BFC8 /* Install Target Specific Python Standard Library */ = { ++ isa = PBXShellScriptBuildPhase; ++ alwaysOutOfDate = 1; ++ buildActionMask = 2147483647; ++ files = ( ++ ); ++ inputFileListPaths = ( ++ ); ++ inputPaths = ( ++ ); ++ name = "Install Target Specific Python Standard Library"; ++ outputFileListPaths = ( ++ ); ++ outputPaths = ( ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ shellPath = /bin/sh; ++ shellScript = "set -e\n\nmkdir -p \"$CODESIGNING_FOLDER_PATH/python/lib\"\nif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-xrsimulator\" ]; then\n echo \"Installing Python modules for xrOS Simulator\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/xros-arm64-simulator/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nelse\n echo \"Installing Python modules for xrOS Device\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/xros-arm64/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nfi\n"; ++ showEnvVarsInLog = 0; ++ }; ++ 607A66562B0F06200010BFC8 /* Prepare Python Binary Modules */ = { ++ isa = PBXShellScriptBuildPhase; ++ alwaysOutOfDate = 1; ++ buildActionMask = 2147483647; ++ files = ( ++ ); ++ inputFileListPaths = ( ++ ); ++ inputPaths = ( ++ ); ++ name = "Prepare Python Binary Modules"; ++ outputFileListPaths = ( ++ ); ++ outputPaths = ( ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ shellPath = /bin/sh; ++ shellScript = "set -e\n\ninstall_dylib () {\n INSTALL_BASE=$1\n FULL_EXT=$2\n\n # The name of the extension file\n EXT=$(basename \"$FULL_EXT\")\n # The location of the extension file, relative to the bundle\n RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/} \n # The path to the extension file, relative to the install base\n PYTHON_EXT=${RELATIVE_EXT/$INSTALL_BASE/}\n # The full dotted name of the extension module, constructed from the file path.\n FULL_MODULE_NAME=$(echo $PYTHON_EXT | cut -d \".\" -f 1 | tr \"/\" \".\"); \n # A bundle identifier; not actually used, but required by Xcode framework packaging\n FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr \"_\" \"-\")\n # The name of the framework folder.\n FRAMEWORK_FOLDER=\"Frameworks/$FULL_MODULE_NAME.framework\"\n\n # If the framework folder doesn't exist, create it.\n if [ ! -d \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\" ]; then\n echo \"Creating framework for $RELATIVE_EXT\" \n mkdir -p \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\"\n cp \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleExecutable -string \"$FULL_MODULE_NAME\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleIdentifier -string \"$FRAMEWORK_BUNDLE_ID\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n fi\n \n echo \"Installing binary for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" \n mv \"$FULL_EXT\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\"\n # Create a placeholder .fwork file where the .so was\n echo \"$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" > ${FULL_EXT%.so}.fwork\n # Create a back reference to the .so file location in the framework\n echo \"${RELATIVE_EXT%.so}.fwork\" > \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin\" \n}\n\nPYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/python/lib\")\necho \"Install Python $PYTHON_VER standard library extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib python/lib/$PYTHON_VER/lib-dynload/ \"$FULL_EXT\"\ndone\necho \"Install app package extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app_packages\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app_packages/ \"$FULL_EXT\"\ndone\necho \"Install app extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app/ \"$FULL_EXT\"\ndone\n\n# Clean up dylib template \nrm -f \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\"\necho \"Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)...\"\nfind \"$CODESIGNING_FOLDER_PATH/Frameworks\" -name \"*.framework\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der \"{}\" \\; \n"; ++ showEnvVarsInLog = 0; ++ }; ++/* End PBXShellScriptBuildPhase section */ ++ ++/* Begin PBXSourcesBuildPhase section */ ++ 607A660E2B0EFA380010BFC8 /* Sources */ = { ++ isa = PBXSourcesBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ 607A66172B0EFA380010BFC8 /* AppDelegate.m in Sources */, ++ 607A66282B0EFA390010BFC8 /* main.m in Sources */, ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++ 607A66292B0EFA3A0010BFC8 /* Sources */ = { ++ isa = PBXSourcesBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ 607A66322B0EFA3A0010BFC8 /* visionOSTestbedTests.m in Sources */, ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++/* End PBXSourcesBuildPhase section */ ++ ++/* Begin PBXTargetDependency section */ ++ 607A662F2B0EFA3A0010BFC8 /* PBXTargetDependency */ = { ++ isa = PBXTargetDependency; ++ target = 607A66112B0EFA380010BFC8 /* visionOSTestbed */; ++ targetProxy = 607A662E2B0EFA3A0010BFC8 /* PBXContainerItemProxy */; ++ }; ++/* End PBXTargetDependency section */ ++ ++/* Begin XCBuildConfiguration section */ ++ 607A663F2B0EFA3A0010BFC8 /* Debug */ = { ++ isa = XCBuildConfiguration; ++ buildSettings = { ++ ALWAYS_SEARCH_USER_PATHS = NO; ++ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ++ CLANG_ANALYZER_NONNULL = YES; ++ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; ++ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; ++ CLANG_ENABLE_MODULES = YES; ++ CLANG_ENABLE_OBJC_ARC = YES; ++ CLANG_ENABLE_OBJC_WEAK = YES; ++ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; ++ CLANG_WARN_BOOL_CONVERSION = YES; ++ CLANG_WARN_COMMA = YES; ++ CLANG_WARN_CONSTANT_CONVERSION = YES; ++ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; ++ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; ++ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; ++ CLANG_WARN_EMPTY_BODY = YES; ++ CLANG_WARN_ENUM_CONVERSION = YES; ++ CLANG_WARN_INFINITE_RECURSION = YES; ++ CLANG_WARN_INT_CONVERSION = YES; ++ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; ++ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; ++ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; ++ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; ++ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; ++ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; ++ CLANG_WARN_STRICT_PROTOTYPES = YES; ++ CLANG_WARN_SUSPICIOUS_MOVE = YES; ++ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; ++ CLANG_WARN_UNREACHABLE_CODE = YES; ++ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; ++ COPY_PHASE_STRIP = NO; ++ DEBUG_INFORMATION_FORMAT = dwarf; ++ ENABLE_STRICT_OBJC_MSGSEND = YES; ++ ENABLE_TESTABILITY = YES; ++ ENABLE_USER_SCRIPT_SANDBOXING = YES; ++ GCC_C_LANGUAGE_STANDARD = gnu17; ++ GCC_DYNAMIC_NO_PIC = NO; ++ GCC_NO_COMMON_BLOCKS = YES; ++ GCC_OPTIMIZATION_LEVEL = 0; ++ GCC_PREPROCESSOR_DEFINITIONS = ( ++ "DEBUG=1", ++ "$(inherited)", ++ ); ++ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; ++ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; ++ GCC_WARN_UNDECLARED_SELECTOR = YES; ++ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; ++ GCC_WARN_UNUSED_FUNCTION = YES; ++ GCC_WARN_UNUSED_VARIABLE = YES; ++ IPHONEOS_DEPLOYMENT_TARGET = 12.0; ++ LOCALIZATION_PREFERS_STRING_CATALOGS = YES; ++ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; ++ MTL_FAST_MATH = YES; ++ ONLY_ACTIVE_ARCH = YES; ++ SDKROOT = xros; ++ }; ++ name = Debug; ++ }; ++ 607A66402B0EFA3A0010BFC8 /* Release */ = { ++ isa = XCBuildConfiguration; ++ buildSettings = { ++ ALWAYS_SEARCH_USER_PATHS = NO; ++ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ++ CLANG_ANALYZER_NONNULL = YES; ++ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; ++ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; ++ CLANG_ENABLE_MODULES = YES; ++ CLANG_ENABLE_OBJC_ARC = YES; ++ CLANG_ENABLE_OBJC_WEAK = YES; ++ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; ++ CLANG_WARN_BOOL_CONVERSION = YES; ++ CLANG_WARN_COMMA = YES; ++ CLANG_WARN_CONSTANT_CONVERSION = YES; ++ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; ++ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; ++ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; ++ CLANG_WARN_EMPTY_BODY = YES; ++ CLANG_WARN_ENUM_CONVERSION = YES; ++ CLANG_WARN_INFINITE_RECURSION = YES; ++ CLANG_WARN_INT_CONVERSION = YES; ++ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; ++ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; ++ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; ++ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; ++ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; ++ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; ++ CLANG_WARN_STRICT_PROTOTYPES = YES; ++ CLANG_WARN_SUSPICIOUS_MOVE = YES; ++ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; ++ CLANG_WARN_UNREACHABLE_CODE = YES; ++ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; ++ COPY_PHASE_STRIP = NO; ++ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ++ ENABLE_NS_ASSERTIONS = NO; ++ ENABLE_STRICT_OBJC_MSGSEND = YES; ++ ENABLE_USER_SCRIPT_SANDBOXING = YES; ++ GCC_C_LANGUAGE_STANDARD = gnu17; ++ GCC_NO_COMMON_BLOCKS = YES; ++ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; ++ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; ++ GCC_WARN_UNDECLARED_SELECTOR = YES; ++ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; ++ GCC_WARN_UNUSED_FUNCTION = YES; ++ GCC_WARN_UNUSED_VARIABLE = YES; ++ IPHONEOS_DEPLOYMENT_TARGET = 12.0; ++ LOCALIZATION_PREFERS_STRING_CATALOGS = YES; ++ MTL_ENABLE_DEBUG_INFO = NO; ++ MTL_FAST_MATH = YES; ++ SDKROOT = xros; ++ VALIDATE_PRODUCT = YES; ++ }; ++ name = Release; ++ }; ++ 607A66422B0EFA3A0010BFC8 /* Debug */ = { ++ isa = XCBuildConfiguration; ++ buildSettings = { ++ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ++ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ++ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; ++ CODE_SIGN_STYLE = Automatic; ++ CURRENT_PROJECT_VERSION = 1; ++ DEVELOPMENT_TEAM = ""; ++ ENABLE_USER_SCRIPT_SANDBOXING = NO; ++ HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; ++ INFOPLIST_FILE = "visionOSTestbed/visionOSTestbed-Info.plist"; ++ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; ++ INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; ++ INFOPLIST_KEY_UIMainStoryboardFile = Main; ++ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; ++ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; ++ LD_RUNPATH_SEARCH_PATHS = ( ++ "$(inherited)", ++ "@executable_path/Frameworks", ++ ); ++ MARKETING_VERSION = 3.13.0a1; ++ PRODUCT_BUNDLE_IDENTIFIER = org.python.visionOSTestbed; ++ PRODUCT_NAME = "$(TARGET_NAME)"; ++ SUPPORTED_PLATFORMS = "xros xrsimulator"; ++ SUPPORTS_MACCATALYST = NO; ++ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; ++ SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; ++ SWIFT_EMIT_LOC_STRINGS = YES; ++ TARGETED_DEVICE_FAMILY = 7; ++ XROS_DEPLOYMENT_TARGET = 2.0; ++ }; ++ name = Debug; ++ }; ++ 607A66432B0EFA3A0010BFC8 /* Release */ = { ++ isa = XCBuildConfiguration; ++ buildSettings = { ++ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ++ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ++ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; ++ CODE_SIGN_STYLE = Automatic; ++ CURRENT_PROJECT_VERSION = 1; ++ DEVELOPMENT_TEAM = ""; ++ ENABLE_TESTABILITY = YES; ++ ENABLE_USER_SCRIPT_SANDBOXING = NO; ++ HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; ++ INFOPLIST_FILE = "visionOSTestbed/visionOSTestbed-Info.plist"; ++ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; ++ INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; ++ INFOPLIST_KEY_UIMainStoryboardFile = Main; ++ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; ++ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; ++ LD_RUNPATH_SEARCH_PATHS = ( ++ "$(inherited)", ++ "@executable_path/Frameworks", ++ ); ++ MARKETING_VERSION = 3.13.0a1; ++ PRODUCT_BUNDLE_IDENTIFIER = org.python.visionOSTestbed; ++ PRODUCT_NAME = "$(TARGET_NAME)"; ++ SUPPORTED_PLATFORMS = "xros xrsimulator"; ++ SUPPORTS_MACCATALYST = NO; ++ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; ++ SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; ++ SWIFT_EMIT_LOC_STRINGS = YES; ++ TARGETED_DEVICE_FAMILY = 7; ++ XROS_DEPLOYMENT_TARGET = 2.0; ++ }; ++ name = Release; ++ }; ++ 607A66452B0EFA3A0010BFC8 /* Debug */ = { ++ isa = XCBuildConfiguration; ++ buildSettings = { ++ BUNDLE_LOADER = "$(TEST_HOST)"; ++ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; ++ CODE_SIGN_STYLE = Automatic; ++ CURRENT_PROJECT_VERSION = 1; ++ DEVELOPMENT_TEAM = 3HEZE76D99; ++ GENERATE_INFOPLIST_FILE = YES; ++ HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; ++ IPHONEOS_DEPLOYMENT_TARGET = 12.0; ++ MARKETING_VERSION = 1.0; ++ PRODUCT_BUNDLE_IDENTIFIER = org.python.visionOSTestbedTests; ++ PRODUCT_NAME = "$(TARGET_NAME)"; ++ SUPPORTED_PLATFORMS = "xros xrsimulator"; ++ SUPPORTS_MACCATALYST = NO; ++ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; ++ SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; ++ SWIFT_EMIT_LOC_STRINGS = NO; ++ TARGETED_DEVICE_FAMILY = 7; ++ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/visionOSTestbed.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/visionOSTestbed"; ++ }; ++ name = Debug; ++ }; ++ 607A66462B0EFA3A0010BFC8 /* Release */ = { ++ isa = XCBuildConfiguration; ++ buildSettings = { ++ BUNDLE_LOADER = "$(TEST_HOST)"; ++ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; ++ CODE_SIGN_STYLE = Automatic; ++ CURRENT_PROJECT_VERSION = 1; ++ DEVELOPMENT_TEAM = 3HEZE76D99; ++ GENERATE_INFOPLIST_FILE = YES; ++ HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; ++ IPHONEOS_DEPLOYMENT_TARGET = 12.0; ++ MARKETING_VERSION = 1.0; ++ PRODUCT_BUNDLE_IDENTIFIER = org.python.visionOSTestbedTests; ++ PRODUCT_NAME = "$(TARGET_NAME)"; ++ SUPPORTED_PLATFORMS = "xros xrsimulator"; ++ SUPPORTS_MACCATALYST = NO; ++ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; ++ SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; ++ SWIFT_EMIT_LOC_STRINGS = NO; ++ TARGETED_DEVICE_FAMILY = 7; ++ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/visionOSTestbed.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/visionOSTestbed"; ++ }; ++ name = Release; ++ }; ++/* End XCBuildConfiguration section */ ++ ++/* Begin XCConfigurationList section */ ++ 607A660D2B0EFA380010BFC8 /* Build configuration list for PBXProject "visionOSTestbed" */ = { ++ isa = XCConfigurationList; ++ buildConfigurations = ( ++ 607A663F2B0EFA3A0010BFC8 /* Debug */, ++ 607A66402B0EFA3A0010BFC8 /* Release */, ++ ); ++ defaultConfigurationIsVisible = 0; ++ defaultConfigurationName = Release; ++ }; ++ 607A66412B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "visionOSTestbed" */ = { ++ isa = XCConfigurationList; ++ buildConfigurations = ( ++ 607A66422B0EFA3A0010BFC8 /* Debug */, ++ 607A66432B0EFA3A0010BFC8 /* Release */, ++ ); ++ defaultConfigurationIsVisible = 0; ++ defaultConfigurationName = Release; ++ }; ++ 607A66442B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "visionOSTestbedTests" */ = { ++ isa = XCConfigurationList; ++ buildConfigurations = ( ++ 607A66452B0EFA3A0010BFC8 /* Debug */, ++ 607A66462B0EFA3A0010BFC8 /* Release */, ++ ); ++ defaultConfigurationIsVisible = 0; ++ defaultConfigurationName = Release; ++ }; ++/* End XCConfigurationList section */ ++ }; ++ rootObject = 607A660A2B0EFA380010BFC8 /* Project object */; ++} +--- /dev/null ++++ b/visionOS/testbed/visionOSTestbed/AppDelegate.h +@@ -0,0 +1,11 @@ ++// ++// AppDelegate.h ++// visionOSTestbed ++// ++ ++#import ++ ++@interface AppDelegate : UIResponder ++ ++ ++@end +--- /dev/null ++++ b/visionOS/testbed/visionOSTestbed/AppDelegate.m +@@ -0,0 +1,19 @@ ++// ++// AppDelegate.m ++// visionOSTestbed ++// ++ ++#import "AppDelegate.h" ++ ++@interface AppDelegate () ++ ++@end ++ ++@implementation AppDelegate ++ ++ ++- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ++ return YES; ++} ++ ++@end +--- /dev/null ++++ b/visionOS/testbed/visionOSTestbed/Assets.xcassets/AccentColor.colorset/Contents.json +@@ -0,0 +1,11 @@ ++{ ++ "colors" : [ ++ { ++ "idiom" : "universal" ++ } ++ ], ++ "info" : { ++ "author" : "xcode", ++ "version" : 1 ++ } ++} +--- /dev/null ++++ b/visionOS/testbed/visionOSTestbed/Assets.xcassets/AppIcon.appiconset/Contents.json +@@ -0,0 +1,13 @@ ++{ ++ "images" : [ ++ { ++ "idiom" : "universal", ++ "platform" : "ios", ++ "size" : "1024x1024" ++ } ++ ], ++ "info" : { ++ "author" : "xcode", ++ "version" : 1 ++ } ++} +--- /dev/null ++++ b/visionOS/testbed/visionOSTestbed/Assets.xcassets/Contents.json +@@ -0,0 +1,6 @@ ++{ ++ "info" : { ++ "author" : "xcode", ++ "version" : 1 ++ } ++} +--- /dev/null ++++ b/visionOS/testbed/visionOSTestbed/app/README +@@ -0,0 +1,7 @@ ++This folder can contain any Python application code. ++ ++During the build, any binary modules found in this folder will be processed into ++iOS Framework form. ++ ++When the test suite runs, this folder will be on the PYTHONPATH, and will be the ++working directory for the test suite. +--- /dev/null ++++ b/visionOS/testbed/visionOSTestbed/app_packages/README +@@ -0,0 +1,7 @@ ++This folder can be a target for installing any Python dependencies needed by the ++test suite. ++ ++During the build, any binary modules found in this folder will be processed into ++iOS Framework form. ++ ++When the test suite runs, this folder will be on the PYTHONPATH. +--- /dev/null ++++ b/visionOS/testbed/visionOSTestbed/dylib-Info-template.plist +@@ -0,0 +1,26 @@ ++ ++ ++ ++ ++ CFBundleDevelopmentRegion ++ en ++ CFBundleExecutable ++ ++ CFBundleIdentifier ++ ++ CFBundleInfoDictionaryVersion ++ 6.0 ++ CFBundlePackageType ++ APPL ++ CFBundleShortVersionString ++ 1.0 ++ CFBundleSupportedPlatforms ++ ++ VisionOS ++ ++ MinimumOSVersion ++ 12.0 ++ CFBundleVersion ++ 1 ++ ++ +--- /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 @@ ++ ++ ++ ++ ++ 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/visionOS/testbed/visionOSTestbedTests/visionOSTestbedTests.m +@@ -0,0 +1,162 @@ ++#import ++#import ++ ++@interface visionOSTestbedTests : XCTestCase ++ ++@end ++ ++@implementation visionOSTestbedTests ++ ++ ++- (void)testPython { ++ const char **argv; ++ int exit_code; ++ int failed; ++ PyStatus status; ++ PyPreConfig preconfig; ++ PyConfig config; ++ PyObject *sys_module; ++ PyObject *sys_path_attr; ++ NSArray *test_args; ++ NSString *python_home; ++ NSString *path; ++ wchar_t *wtmp_str; ++ ++ NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; ++ ++ // Set some other common environment indicators to disable color, as the ++ // Xcode log can't display color. Stdout will report that it is *not* a ++ // TTY. ++ setenv("NO_COLOR", "1", true); ++ setenv("PYTHON_COLORS", "0", true); ++ ++ // Arguments to pass into the test suite runner. ++ // argv[0] must identify the process; any subsequent arg ++ // will be handled as if it were an argument to `python -m test` ++ test_args = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"TestArgs"]; ++ if (test_args == NULL) { ++ NSLog(@"Unable to identify test arguments."); ++ } ++ argv = malloc(sizeof(char *) * ([test_args count] + 1)); ++ argv[0] = "visionOSTestbed"; ++ for (int i = 1; i < [test_args count]; i++) { ++ argv[i] = [[test_args objectAtIndex:i] UTF8String]; ++ } ++ NSLog(@"Test command: %@", test_args); ++ ++ // Generate an isolated Python configuration. ++ NSLog(@"Configuring isolated Python..."); ++ PyPreConfig_InitIsolatedConfig(&preconfig); ++ PyConfig_InitIsolatedConfig(&config); ++ ++ // Configure the Python interpreter: ++ // Enforce UTF-8 encoding for stderr, stdout, file-system encoding and locale. ++ // See https://docs.python.org/3/library/os.html#python-utf-8-mode. ++ preconfig.utf8_mode = 1; ++ // Use the system logger for stdout/err ++ config.use_system_logger = 1; ++ // Don't buffer stdio. We want output to appears in the log immediately ++ config.buffered_stdio = 0; ++ // Don't write bytecode; we can't modify the app bundle ++ // after it has been signed. ++ config.write_bytecode = 0; ++ // Ensure that signal handlers are installed ++ config.install_signal_handlers = 1; ++ // Run the test module. ++ config.run_module = Py_DecodeLocale([[test_args objectAtIndex:0] UTF8String], NULL); ++ // For debugging - enable verbose mode. ++ // config.verbose = 1; ++ ++ NSLog(@"Pre-initializing Python runtime..."); ++ status = Py_PreInitialize(&preconfig); ++ if (PyStatus_Exception(status)) { ++ XCTFail(@"Unable to pre-initialize Python interpreter: %s", status.err_msg); ++ PyConfig_Clear(&config); ++ return; ++ } ++ ++ // Set the home for the Python interpreter ++ python_home = [NSString stringWithFormat:@"%@/python", resourcePath, nil]; ++ NSLog(@"PythonHome: %@", python_home); ++ wtmp_str = Py_DecodeLocale([python_home UTF8String], NULL); ++ status = PyConfig_SetString(&config, &config.home, wtmp_str); ++ if (PyStatus_Exception(status)) { ++ XCTFail(@"Unable to set PYTHONHOME: %s", status.err_msg); ++ PyConfig_Clear(&config); ++ return; ++ } ++ PyMem_RawFree(wtmp_str); ++ ++ // Read the site config ++ status = PyConfig_Read(&config); ++ if (PyStatus_Exception(status)) { ++ XCTFail(@"Unable to read site config: %s", status.err_msg); ++ PyConfig_Clear(&config); ++ return; ++ } ++ ++ NSLog(@"Configure argc/argv..."); ++ status = PyConfig_SetBytesArgv(&config, [test_args count], (char**) argv); ++ if (PyStatus_Exception(status)) { ++ XCTFail(@"Unable to configure argc/argv: %s", status.err_msg); ++ PyConfig_Clear(&config); ++ return; ++ } ++ ++ NSLog(@"Initializing Python runtime..."); ++ status = Py_InitializeFromConfig(&config); ++ if (PyStatus_Exception(status)) { ++ XCTFail(@"Unable to initialize Python interpreter: %s", status.err_msg); ++ PyConfig_Clear(&config); ++ return; ++ } ++ ++ sys_module = PyImport_ImportModule("sys"); ++ if (sys_module == NULL) { ++ XCTFail(@"Could not import sys module"); ++ return; ++ } ++ ++ sys_path_attr = PyObject_GetAttrString(sys_module, "path"); ++ if (sys_path_attr == NULL) { ++ XCTFail(@"Could not access sys.path"); ++ return; ++ } ++ ++ // Add the app packages path ++ path = [NSString stringWithFormat:@"%@/app_packages", resourcePath, nil]; ++ NSLog(@"App packages path: %@", path); ++ wtmp_str = Py_DecodeLocale([path UTF8String], NULL); ++ failed = PyList_Insert(sys_path_attr, 0, PyUnicode_FromString([path UTF8String])); ++ if (failed) { ++ XCTFail(@"Unable to add app packages to sys.path"); ++ return; ++ } ++ PyMem_RawFree(wtmp_str); ++ ++ path = [NSString stringWithFormat:@"%@/app", resourcePath, nil]; ++ NSLog(@"App path: %@", path); ++ wtmp_str = Py_DecodeLocale([path UTF8String], NULL); ++ failed = PyList_Insert(sys_path_attr, 0, PyUnicode_FromString([path UTF8String])); ++ if (failed) { ++ XCTFail(@"Unable to add app to sys.path"); ++ return; ++ } ++ PyMem_RawFree(wtmp_str); ++ ++ // Ensure the working directory is the app folder. ++ chdir([path UTF8String]); ++ ++ // Start the test suite. Print a separator to differentiate Python startup logs from app logs ++ NSLog(@"---------------------------------------------------------------------------"); ++ ++ exit_code = Py_RunMain(); ++ XCTAssertEqual(exit_code, 0, @"Test suite did not pass"); ++ ++ NSLog(@"---------------------------------------------------------------------------"); ++ ++ Py_Finalize(); ++} ++ ++ ++@end +--- /dev/null +++ b/watchOS/README.rst @@ -0,0 +1,108 @@ +======================== diff --git a/patch/Python/release.visionOS.exclude b/patch/Python/release.visionOS.exclude new file mode 100644 index 00000000..f1d0f75e --- /dev/null +++ b/patch/Python/release.visionOS.exclude @@ -0,0 +1,6 @@ +# This is a list of support package path patterns that we exclude +# from all Python-Apple-support tarballs. +# It is used by `tar -X` during the Makefile build. +# Remove pyc files. These take up space, but since most stdlib modules are +# never imported by user code, they mostly have no value. +*/__pycache__ From f8fd68641122bd637be7038a3d50069271541a8c Mon Sep 17 00:00:00 2001 From: John Date: Wed, 23 Apr 2025 18:14:03 -0500 Subject: [PATCH 083/113] Fix an iOS reference in testbed cloning. (#272) --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 72994c0b..3796f843 100644 --- a/Makefile +++ b/Makefile @@ -663,7 +663,7 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ ifeq ($(filter $(os),iOS visionOS),$(os)) @echo ">>> Clone testbed project for $(os)" - $(HOST_PYTHON) $$(PYTHON_SRCDIR-$$(firstword $$(SDK_TARGETS-$$(firstword $$(SDKS-$(os))))))/iOS/testbed clone --framework $$(PYTHON_XCFRAMEWORK-$(os)) support/$(PYTHON_VER)/$(os)/testbed + $(HOST_PYTHON) $$(PYTHON_SRCDIR-$$(firstword $$(SDK_TARGETS-$$(firstword $$(SDKS-$(os))))))/$(os)/testbed clone --framework $$(PYTHON_XCFRAMEWORK-$(os)) support/$(PYTHON_VER)/$(os)/testbed endif @echo ">>> Create VERSIONS file for $(os)" From 344aaf6dfe3d0c7c4e3c89ab15b7bb5d9a6a2da4 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 25 Apr 2025 11:32:00 +0800 Subject: [PATCH 084/113] Update patch to include visionOS plist changes. --- patch/Python/Python.patch | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 4c59f844..270bf5c0 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -2126,7 +2126,7 @@ index c3e261ecd9e..26ef7a95de4 100644 + %VERSION% + CFBundleSupportedPlatforms + -+ xrOS ++ XROS + + MinimumOSVersion + @XROS_DEPLOYMENT_TARGET@ @@ -2174,31 +2174,35 @@ index c3e261ecd9e..26ef7a95de4 100644 +xcrun --sdk xrsimulator clang -target arm64-apple-xros${XROS_DEPLOYMENT_TARGET}-simulator -E "$@" --- /dev/null +++ b/visionOS/Resources/dylib-Info-template.plist -@@ -0,0 +1,26 @@ +@@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en ++ CFBundleInfoDictionaryVersion ++ 6.0 + CFBundleExecutable + + CFBundleIdentifier + -+ CFBundleInfoDictionaryVersion -+ 6.0 + CFBundlePackageType -+ APPL ++ FMWK + CFBundleShortVersionString + 1.0 + CFBundleSupportedPlatforms + -+ xrOS ++ XROS + -+ MinimumOSVersion -+ 2.0 + CFBundleVersion + 1 ++ MinimumOSVersion ++ 2.0 ++ UIDeviceFamily ++ ++ 7 ++ + + --- /dev/null @@ -3443,31 +3447,35 @@ index c3e261ecd9e..26ef7a95de4 100644 +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,26 @@ +@@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en ++ CFBundleInfoDictionaryVersion ++ 6.0 + CFBundleExecutable + + CFBundleIdentifier + -+ CFBundleInfoDictionaryVersion -+ 6.0 + CFBundlePackageType -+ APPL ++ FMWK + CFBundleShortVersionString + 1.0 + CFBundleSupportedPlatforms + -+ VisionOS ++ XROS + -+ MinimumOSVersion -+ 12.0 + CFBundleVersion + 1 ++ MinimumOSVersion ++ 2.0 ++ UIDeviceFamily ++ ++ 7 ++ + + --- /dev/null From 515625c54edf15843d503ba00d1bc2e0e6b9f5e9 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 26 Apr 2025 21:20:56 -0500 Subject: [PATCH 085/113] visionOS Support and Other Fixes (#276) Add documentation for visionOS support. --- README.rst | 44 +++++++++++++++++++++++++------------------- USAGE.md | 13 +++++++------ 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/README.rst b/README.rst index 2317b749..06fde6c9 100644 --- a/README.rst +++ b/README.rst @@ -2,7 +2,7 @@ Python Apple Support ==================== This is a meta-package for building a version of Python that can be embedded -into a macOS, iOS, tvOS or watchOS project. +into a macOS, iOS, tvOS, watchOS, or visionOS project. **This branch builds a packaged version of Python 3.14**. Other Python versions are available by cloning other branches of the main @@ -16,21 +16,24 @@ repository: It works by downloading, patching, and building a fat binary of Python and selected pre-requisites, and packaging them as frameworks that can be -incorporated into an XCode project. The binary modules in the Python standard +incorporated into an Xcode project. The binary modules in the Python standard library are distributed as binaries that can be dynamically loaded at runtime. The macOS package is a re-bundling of the official macOS binary, modified so that it is relocatable, with the IDLE, Tkinter and turtle packages removed, and the App Store compliance patch applied. -The iOS, tvOS and watchOS packages compiled by this project use the official -`PEP 730 `__ code that is part of Python 3.13 -to provide iOS support; the relevant patches have been backported to 3.9-3.12. -Additional patches have been applied to add tvOS and watchOS support. +The iOS, tvOS, watchOS, and visionOS packages compiled by this project use the +official `PEP 730 `__ code that is part of +Python 3.13 to provide iOS support; the relevant patches have been backported +to 3.9-3.12. Additional patches have been applied to add tvOS, watchOS, and +visionOS support. The binaries support x86_64 and arm64 for macOS; arm64 for iOS and appleTV -devices; and arm64_32 for watchOS devices. It also supports device simulators on -both x86_64 and M1 hardware. This should enable the code to run on: +devices; arm64_32 for watchOS devices; and arm64 for visionOS devices. It also +supports device simulators on both x86_64 and M1 hardware, except for visionOS, +for which x86_64 simulators are officially unsupported. This should enable the +code to run on: * macOS 11 (Big Sur) or later, on: * MacBook (including MacBooks using Apple Silicon) @@ -49,6 +52,8 @@ both x86_64 and M1 hardware. This should enable the code to run on: * Apple TV (4th gen or later) * watchOS 4.0 or later, on: * Apple Watch (4th gen or later) +* visionOS 2.0 or later, on: + * Apple Vision Pro Quickstart ---------- @@ -69,6 +74,7 @@ repository, and then in the root directory, and run: * ``make iOS`` to build everything for iOS. * ``make tvOS`` to build everything for tvOS. * ``make watchOS`` to build everything for watchOS. +* ``make visionOS`` to build everything for visionOS. This should: @@ -76,16 +82,16 @@ This should: 2. Patch them as required for compatibility with the selected OS 3. Build the packages as Xcode-compatible XCFrameworks. -The resulting support packages will be packaged as a ``.tar.gz`` file +The resulting support packages will be packaged as ``.tar.gz`` files in the ``dist`` folder. Each support package contains: * ``VERSIONS``, a text file describing the specific versions of code used to build the support package; -* ``Python.xcframework``, a multi-architecture build of the Python runtime library +* ``Python.xcframework``, a multi-architecture build of the Python runtime library. -On iOS/tvOS/watchOS, the ``Python.xcframework`` contains a +On iOS/tvOS/watchOS/visionOS, the ``Python.xcframework`` contains a slice for each supported ABI (device and simulator). The folder containing the slice can also be used as a ``PYTHONHOME``, as it contains a ``bin``, ``include`` and ``lib`` directory. @@ -96,11 +102,11 @@ needed to build packages. This is required because Xcode uses the ``xcrun`` alias to dynamically generate the name of binaries, but a lot of C tooling expects that ``CC`` will not contain spaces. -Each slice of an iOS/tvOS/watchOS XCframework also contains a +Each slice of an iOS/tvOS/watchOS/visionOS XCframework also contains a ``platform-config`` folder with a subfolder for each supported architecture in that slice. These subfolders can be used to make a macOS Python environment -behave as if it were on an iOS/tvOS/watchOS device. This works in one of two -ways: +behave as if it were on an iOS/tvOS/watchOS/visionOS device. This works in one +of two ways: 1. **A sitecustomize.py script**. If the ``platform-config`` subfolder is on your ``PYTHONPATH`` when a Python interpreter is started, a site @@ -116,9 +122,9 @@ ways: environment to build a wheel, these patches will also be applied to the isolated build environment that is created. -iOS distributions also contain a copy of the iOS ``testbed`` project - an Xcode -project that can be used to run test suites of Python code. See the `CPython -documentation on testing packages +iOS and visionOS distributions also contain a copy of the iOS or visionOS +``testbed`` project - an Xcode project that can be used to run test suites of +Python code. See the `CPython documentation on testing packages `__ for details on how to use this testbed. @@ -131,8 +137,8 @@ Building binary wheels This project packages the Python standard library, but does not address building binary wheels. Binary wheels for macOS can be obtained from PyPI. `Mobile Forge `__ is a project that provides the -tooling to build build binary wheels for iOS (and potentially for tvOS and -watchOS, although that hasn't been tested). +tooling to build build binary wheels for iOS (and potentially for tvOS, watchOS, +and visionOS, although that hasn't been tested). Historical support ------------------ diff --git a/USAGE.md b/USAGE.md index 096f71b0..6565dd40 100644 --- a/USAGE.md +++ b/USAGE.md @@ -2,10 +2,10 @@ ## The easy way -The easist way to use these packages is by creating a project with `Briefcase -`__. Briefcase will download pre-compiled -versions of these support packages, and add them to an Xcode project (or -pre-build stub application, in the case of macOS). +The easist way to use these packages is by creating a project with +(Briefcase)[https://github.com/beeware/briefcase]. Briefcase will download +pre-compiled versions of these support packages, and add them to an Xcode project +(or pre-build stub application, in the case of macOS). ## The manual way @@ -22,8 +22,9 @@ guides: * [macOS](https://docs.python.org/3/using/mac.html) * [iOS](https://docs.python.org/3/using/ios.html#adding-python-to-an-ios-project) -For tvOS and watchOS, you should be able to broadly follow the instructions in -the iOS guide. +For tvOS, watchOS, and visionOS, you should be able to broadly follow the instructions +in the iOS guide. The testbed projects generated on iOS and visionOS may be used as +rough references as well. ### Using Objective C From e29c0fd9cb7d0c2bce222829255faf6523971e73 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Apr 2025 06:43:51 +0800 Subject: [PATCH 086/113] Bump actions/setup-python from 5.5.0 to 5.6.0 (#277) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.5.0 to 5.6.0. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5.5.0...v5.6.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yaml | 2 +- .github/workflows/publish.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1f53f4cc..924ce566 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -105,7 +105,7 @@ jobs: - uses: actions/checkout@v4.1.7 - name: Set up Python - uses: actions/setup-python@v5.5.0 + uses: actions/setup-python@v5.6.0 with: # Appending -dev ensures that we can always build the dev release. # It's a no-op for versions that have been published. diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index c06a6e06..587abe83 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python environment - uses: actions/setup-python@v5.5.0 + uses: actions/setup-python@v5.6.0 with: python-version: "3.X" From 80296f03e68b805ebf7cd8254a50f9d0a3692ea4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Apr 2025 06:44:13 +0800 Subject: [PATCH 087/113] Bump actions/download-artifact from 4.2.1 to 4.3.0 (#278) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.2.1 to 4.3.0. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4.2.1...v4.3.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index e4da86bc..fd78eeec 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -40,7 +40,7 @@ jobs: needs: [ config, ci ] steps: - name: Get build artifacts - uses: actions/download-artifact@v4.2.1 + uses: actions/download-artifact@v4.3.0 with: pattern: Python-* path: dist From b5bed64e1b6ecb0d8322d41dc46648566f59501c Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Thu, 1 May 2025 10:26:15 +0800 Subject: [PATCH 088/113] Ensure base_prefix is set in a cross-venv. (#279) --- patch/Python/_cross_target.py.tmpl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/patch/Python/_cross_target.py.tmpl b/patch/Python/_cross_target.py.tmpl index 9f75af53..fbe178fc 100644 --- a/patch/Python/_cross_target.py.tmpl +++ b/patch/Python/_cross_target.py.tmpl @@ -11,6 +11,8 @@ import sysconfig sys.cross_compiling = True sys.platform = "{{platform}}" sys.implementation._multiarch = "{{arch}}-{{sdk}}" +sys.base_prefix = sysconfig.get_config_var("prefix") +sys.base_exec_prefix = sysconfig.get_config_var("prefix") ########################################################################### # subprocess module patches @@ -67,5 +69,9 @@ def cross_get_sysconfigdata_name(): sysconfig.get_platform = cross_get_platform sysconfig._get_sysconfigdata_name = cross_get_sysconfigdata_name +# Ensure module-level values cached at time of import are updated. +sysconfig._BASE_PREFIX = sys.prefix +sysconfig._BASE_EXEC_PREFIX = sys.base_exec_prefix + # Force sysconfig data to be loaded (and cached). sysconfig._init_config_vars() From 7a7aad1e167082a260b491598a3e35ef4a76d283 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 3 May 2025 22:25:35 -0500 Subject: [PATCH 089/113] Document the Patch Tree Approach. (#281) --- CONTRIBUTING.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 10402ae6..47a30757 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,9 +23,22 @@ repository](https://github.com/python/cpython). macOS and iOS are both officially supported Python platforms, and the code distributed by this project for those platforms is unmodified from the official repository. -Changes to to support other platforms can be included in this PR, but they must -also be submitted as a pull request against the `MAJOR.MINOR-patched` branch on -[the `freakboy3742` fork of the CPython +Changes to to support other platforms can be included in a PR for this repo, but +they must also be submitted as a pull request against the `MAJOR.MINOR-patched` +branch on [the `freakboy3742` fork of the CPython repo](https://github.com/freakboy3742/cpython). This is required to ensure that any contributed changes can be easily reproduced in future patches as more changes are made. + +Note that the `MAJOR.MINOR-patched` branch of that fork is maintained in the format +of a *patch tree*, which is a branch that has an entirely linear sequence of +commits applied on top of another branch (in the case of the fork, `MAJOR.MINOR`), +each of which adding a new feature. Therefore, bug fixes to the patches applied +*will* be merged, but their changes gets squashed into the commit applying the +feature. Feature additions to the patch will be squashed into a single commit, +and then merged. + +This also means that if another contributor on the fork gets a pull request merged +into the fork, you must *rebase*, not merge, your changes on top of the newly pulled +`MAJOR.MINOR-patched` branch, since the "history" of a patch tree branch might +change in a way that is incompatible with merge commits. From 6583edf016d419217a0331a22abc4689bc5c8e1f Mon Sep 17 00:00:00 2001 From: John Date: Sun, 4 May 2025 18:10:02 -0500 Subject: [PATCH 090/113] Update CONTRIBUTING.md for Grammar + Clarity (#282) --- CONTRIBUTING.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 47a30757..dfed555e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,14 +31,15 @@ any contributed changes can be easily reproduced in future patches as more changes are made. Note that the `MAJOR.MINOR-patched` branch of that fork is maintained in the format -of a *patch tree*, which is a branch that has an entirely linear sequence of +of a *patch tree*, which is a branch that consists of an entirely linear sequence of commits applied on top of another branch (in the case of the fork, `MAJOR.MINOR`), -each of which adding a new feature. Therefore, bug fixes to the patches applied -*will* be merged, but their changes gets squashed into the commit applying the -feature. Feature additions to the patch will be squashed into a single commit, -and then merged. - -This also means that if another contributor on the fork gets a pull request merged -into the fork, you must *rebase*, not merge, your changes on top of the newly pulled -`MAJOR.MINOR-patched` branch, since the "history" of a patch tree branch might -change in a way that is incompatible with merge commits. +each of which adds a significant new feature. Therefore, a bug fix for an existing commit +in the patch tree *will* be merged when appropriate, but its changes will get combined +with that existing commit that adds the feature. A feature addition PR will be squashed +into a single, new commit, and then put on top of the patch tree. + +This also means that if another contributor gets a pull request merged into +`MAJOR.MINOR-patched`, you must *rebase* your changes on top of the updated +`MAJOR.MINOR-patched` branch, as opposed to *merging* `MAJOR.MINOR-patched` into your +branch, since the "history" of a patch tree is likely to change in a way that is +incompatible with merge commits. From a0419056635f76a14207014ea7da83a6d69ea35c Mon Sep 17 00:00:00 2001 From: John Date: Mon, 5 May 2025 19:15:06 -0500 Subject: [PATCH 091/113] Remove some useless PATHs in Usage.md (#284) Updated example configuration to remove paths implied by a normal PYTHONHOME configuration. --- USAGE.md | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/USAGE.md b/USAGE.md index 6565dd40..072ae1ec 100644 --- a/USAGE.md +++ b/USAGE.md @@ -23,8 +23,8 @@ guides: * [iOS](https://docs.python.org/3/using/ios.html#adding-python-to-an-ios-project) For tvOS, watchOS, and visionOS, you should be able to broadly follow the instructions -in the iOS guide. The testbed projects generated on iOS and visionOS may be used as -rough references as well. +in the iOS guide, changing some platform names in the first script. The testbed projects +generated on iOS and visionOS may be used as rough references as well. ### Using Objective C @@ -45,19 +45,15 @@ As a *bare minimum*, you can do the following: ```objc NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; NSString *pythonHome = [NSString stringWithFormat:@"%@/python", resourcePath, nil]; - NSString *pythonPath = [NSString stringWithFormat:@"%@/lib/python3.13", python_home, nil]; - NSString *libDynloadPath = [NSString stringWithFormat:@"%@/lib/python3.13/lib-dynload", python_home, nil]; NSString *appPath = [NSString stringWithFormat:@"%@/app", resourcePath, nil]; - setenv("PYTHONHOME", pythonHome, 1); - setenv("PYTHONPATH", [NSString stringWithFormat:@"%@:%@:%@", pythonpath, libDynloadPath, appPath, nil]); + setenv("PYTHONHOME", [pythonHome UTF8String], 1); + setenv("PYTHONPATH", [appPath UTF8String], 1); Py_Initialize(); // we now have a Python interpreter ready to be used ``` - References to a specific Python version should reflect the version of - Python you are using. Again - this is the *bare minimum* initialization. In practice, you will likely need to configure other aspects of the Python interpreter using the @@ -81,12 +77,10 @@ code will look something like this: 2. Initialize the Python interpreter: ```swift guard let pythonHome = Bundle.main.path(forResource: "python", ofType: nil) else { return } - guard let pythonPath = Bundle.main.path(forResource: "python/lib/python3.13", ofType: nil) else { return } - guard let libDynloadPath = Bundle.main.path(forResource: "python/lib/python3.13/lib-dynload", ofType: nil) else { return } let appPath = Bundle.main.path(forResource: "app", ofType: nil) setenv("PYTHONHOME", pythonHome, 1) - setenv("PYTHONPATH", [pythonPath, libDynloadPath, appPath].compactMap { $0 }.joined(separator: ":"), 1) + setenv("PYTHONPATH", appPath, 1) Py_Initialize() // we now have a Python interpreter ready to be used ``` From 76b7f30fbecd8f1e72cfc42b605828b253242f77 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 3 Jun 2025 13:21:23 +0800 Subject: [PATCH 092/113] Bump patch to Python 3.14.0b2. (#290) --- Makefile | 2 +- patch/Python/Python.patch | 195 +++++++++++++++++++------------------- 2 files changed, 99 insertions(+), 98 deletions(-) diff --git a/Makefile b/Makefile index 3796f843..79c2f19f 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.0a7 +PYTHON_VERSION=3.14.0b2 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+") diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 270bf5c0..8db5c523 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1,8 +1,8 @@ diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py -index bba08b99b95..8d03017c223 100644 +index 823a3692fd1..00639dd8488 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py -@@ -361,7 +361,7 @@ +@@ -419,7 +419,7 @@ if name: name = _os.fspath(name) @@ -56,7 +56,7 @@ index 8bcd741c446..d8a6f28edba 100644 suffix.replace(".so", ".fwork") for suffix in _imp.extension_suffixes() diff --git a/Lib/platform.py b/Lib/platform.py -index a62192589af..31f18b026b7 100644 +index 55e211212d4..cad919bc0c4 100644 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -528,6 +528,78 @@ @@ -212,10 +212,10 @@ index a62192589af..31f18b026b7 100644 macos_release = mac_ver()[0] if macos_release: diff --git a/Lib/site.py b/Lib/site.py -index 9da8b6724e1..345f55a5bde 100644 +index f9327197159..74899abecb0 100644 --- a/Lib/site.py +++ b/Lib/site.py -@@ -297,8 +297,8 @@ +@@ -298,8 +298,8 @@ if env_base: return env_base @@ -227,7 +227,7 @@ index 9da8b6724e1..345f55a5bde 100644 def joinuser(*args): diff --git a/Lib/subprocess.py b/Lib/subprocess.py -index da5f5729e09..7401ffbc987 100644 +index 54c2eb515b6..03896a234bf 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -75,7 +75,7 @@ @@ -240,7 +240,7 @@ index da5f5729e09..7401ffbc987 100644 if _mswindows: import _winapi diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py -index 18e6b8d25e5..64603fb1bb1 100644 +index f93b98dd681..0db3dbdce05 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -23,6 +23,9 @@ @@ -262,7 +262,7 @@ index 18e6b8d25e5..64603fb1bb1 100644 return None def joinuser(*args): -@@ -719,6 +722,18 @@ +@@ -730,6 +733,18 @@ release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0") osname = sys.platform machine = sys.implementation._multiarch @@ -282,10 +282,10 @@ index 18e6b8d25e5..64603fb1bb1 100644 import _osx_support osname, release, machine = _osx_support.get_platform_osx( diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py -index ecb37250ceb..67d04491072 100644 +index 1b551254f86..8594f92c097 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py -@@ -7155,9 +7155,9 @@ +@@ -7159,9 +7159,9 @@ self.assertEqual(dt_orig, dt_rt) def test_type_check_in_subinterp(self): @@ -298,10 +298,10 @@ index ecb37250ceb..67d04491072 100644 else: extension_loader = "ExtensionFileLoader" diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py -index 6d670a575b0..8d7d68d1ee1 100644 +index b7cd7940eb1..32243a49e7a 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py -@@ -551,7 +551,7 @@ +@@ -558,7 +558,7 @@ sys.platform == "android", f"Android blocks {name} with SELinux" ) @@ -310,7 +310,7 @@ index 6d670a575b0..8d7d68d1ee1 100644 unix_shell = '/system/bin/sh' if is_android else '/bin/sh' else: unix_shell = None -@@ -567,7 +567,7 @@ +@@ -574,7 +574,7 @@ def skip_wasi_stack_overflow(): return unittest.skipIf(is_wasi, "Exhausts stack on WASI") @@ -319,19 +319,6 @@ index 6d670a575b0..8d7d68d1ee1 100644 is_apple = is_apple_mobile or sys.platform == "darwin" has_fork_support = hasattr(os, "fork") and not ( -diff --git a/Lib/test/support/os_helper.py b/Lib/test/support/os_helper.py -index d82093e375c..2c45fe2369e 100644 ---- a/Lib/test/support/os_helper.py -+++ b/Lib/test/support/os_helper.py -@@ -657,7 +657,7 @@ - """ - if sys.platform.startswith(('linux', 'android', 'freebsd', 'emscripten')): - fd_path = "/proc/self/fd" -- elif sys.platform == "darwin": -+ elif support.is_apple: - fd_path = "/dev/fd" - else: - fd_path = None 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 @@ -346,10 +333,10 @@ index 15603dc3d77..bff6c0fb95f 100644 if WINDOWS: KNOWN_LIBRARIES = ["KERNEL32.DLL"] diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py -index 6ba630ad527..7b447744d12 100644 +index 719c4feace6..92a831a9148 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py -@@ -268,13 +268,21 @@ +@@ -271,13 +271,21 @@ if sys.platform == "android": self.assertEqual(res.system, "Android") self.assertEqual(res.release, platform.android_ver().release) @@ -389,11 +376,11 @@ index 4c3ea1cd8df..04a210e5c86 100644 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 232d3c3a9c5..e042c20ea54 100644 +index f2e2394089d..2efbbfb0014 100644 --- a/Lib/webbrowser.py +++ b/Lib/webbrowser.py @@ -488,7 +488,8 @@ - # OS X can use below Unix support (but we prefer using the OS X + # macOS can use below Unix support (but we prefer using the macOS # specific stuff) - if sys.platform == "ios": @@ -402,7 +389,7 @@ index 232d3c3a9c5..e042c20ea54 100644 register("iosbrowser", None, IOSBrowser(), preferred=True) if sys.platform == "serenityos": -@@ -640,9 +641,10 @@ +@@ -653,9 +654,10 @@ return not rc # @@ -416,7 +403,7 @@ index 232d3c3a9c5..e042c20ea54 100644 if objc: # If objc exists, we know ctypes is also importable. diff --git a/Makefile.pre.in b/Makefile.pre.in -index e10c78d6403..920e707ab26 100644 +index b5703fbe6ae..e436b2efb8e 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -209,6 +209,12 @@ @@ -432,7 +419,7 @@ index e10c78d6403..920e707ab26 100644 # Option to install to strip binaries STRIPFLAG=-s -@@ -2243,7 +2249,7 @@ +@@ -2264,7 +2270,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. @@ -441,7 +428,7 @@ index e10c78d6403..920e707ab26 100644 .PHONY: testios testios: @if test "$(MACHDEP)" != "ios"; then \ -@@ -2263,11 +2269,41 @@ +@@ -2284,11 +2290,41 @@ exit 1;\ fi @@ -487,7 +474,7 @@ index e10c78d6403..920e707ab26 100644 # 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 ec0857a4a99..e52f486cdb3 100644 +index f5cd73bdea8..6c1863c943b 100644 --- a/Misc/platform_triplet.c +++ b/Misc/platform_triplet.c @@ -257,6 +257,32 @@ @@ -546,10 +533,10 @@ index 1bb6a05dc11..49febd56a37 100755 none--*) # None (no kernel, i.e. freestanding / bare metal), diff --git a/configure b/configure -index 1b75ddfa26d..7b79dfc424c 100755 +index 884f8a4b068..7c93d36e717 100755 --- a/configure +++ b/configure -@@ -978,6 +978,10 @@ +@@ -982,6 +982,10 @@ CFLAGS CC HAS_XCRUN @@ -560,7 +547,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 IPHONEOS_DEPLOYMENT_TARGET EXPORT_MACOSX_DEPLOYMENT_TARGET CONFIGURE_MACOSX_DEPLOYMENT_TARGET -@@ -4106,6 +4110,15 @@ +@@ -4116,6 +4120,15 @@ *-apple-ios*) ac_sys_system=iOS ;; @@ -576,7 +563,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 *-*-darwin*) ac_sys_system=Darwin ;; -@@ -4187,7 +4200,7 @@ +@@ -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. # @@ -585,7 +572,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 # 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 -@@ -4202,6 +4215,17 @@ +@@ -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 ;; @@ -603,7 +590,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 *) esac fi -@@ -4210,6 +4234,17 @@ +@@ -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 ;; @@ -621,7 +608,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 *) esac fi -@@ -4218,6 +4253,17 @@ +@@ -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 ;; @@ -639,7 +626,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 *) esac fi -@@ -4226,6 +4272,17 @@ +@@ -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++ ;; @@ -657,7 +644,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 *) esac fi -@@ -4348,8 +4405,11 @@ +@@ -4358,8 +4415,11 @@ case $enableval in yes) case $ac_sys_system in @@ -671,7 +658,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 esac esac -@@ -4358,6 +4418,9 @@ +@@ -4368,6 +4428,9 @@ no) case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -681,7 +668,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4464,6 +4527,51 @@ +@@ -4474,6 +4537,51 @@ ac_config_files="$ac_config_files iOS/Resources/Info.plist" @@ -733,7 +720,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 ;; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 -@@ -4475,6 +4583,9 @@ +@@ -4485,6 +4593,9 @@ e) case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -743,7 +730,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4529,8 +4640,8 @@ +@@ -4539,8 +4650,8 @@ case "$withval" in yes) case $ac_sys_system in @@ -754,7 +741,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 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 ;; -@@ -4548,8 +4659,8 @@ +@@ -4558,8 +4669,8 @@ else case e in #( e) case $ac_sys_system in @@ -765,7 +752,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 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; } -@@ -4567,6 +4678,8 @@ +@@ -4577,6 +4688,8 @@ @@ -774,7 +761,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 if test "$cross_compiling" = yes; then case "$host" in -@@ -4604,6 +4717,78 @@ +@@ -4614,6 +4727,78 @@ ;; esac ;; @@ -853,7 +840,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 *-*-darwin*) case "$host_cpu" in arm*) -@@ -4694,9 +4879,15 @@ +@@ -4704,9 +4889,15 @@ define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; @@ -870,7 +857,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -4759,7 +4950,13 @@ +@@ -4769,7 +4960,13 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' @@ -885,7 +872,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 # checks for alternative programs -@@ -4800,6 +4997,16 @@ +@@ -4810,6 +5007,16 @@ as_fn_append CFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" as_fn_append LDFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" ;; #( @@ -902,7 +889,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 *) : ;; esac -@@ -7169,6 +7376,12 @@ +@@ -7179,6 +7386,12 @@ MULTIARCH="" ;; #( iOS) : MULTIARCH="" ;; #( @@ -915,7 +902,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -7189,7 +7402,7 @@ +@@ -7199,7 +7412,7 @@ printf "%s\n" "$MULTIARCH" >&6; } case $ac_sys_system in #( @@ -924,7 +911,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2` ;; #( *) : SOABI_PLATFORM=$PLATFORM_TRIPLET -@@ -7240,6 +7453,18 @@ +@@ -7250,6 +7463,18 @@ PY_SUPPORT_TIER=3 ;; #( aarch64-apple-ios*/clang) : PY_SUPPORT_TIER=3 ;; #( @@ -943,7 +930,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 aarch64-*-linux-android/clang) : PY_SUPPORT_TIER=3 ;; #( x86_64-*-linux-android/clang) : -@@ -7676,7 +7901,7 @@ +@@ -7686,7 +7911,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; @@ -952,7 +939,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5;; -@@ -7742,7 +7967,7 @@ +@@ -7752,7 +7977,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -961,7 +948,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -13550,7 +13775,7 @@ +@@ -13574,7 +13799,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -970,7 +957,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -13683,7 +13908,7 @@ +@@ -13707,7 +13932,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -979,7 +966,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -13707,7 +13932,7 @@ +@@ -13731,7 +13956,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -988,7 +975,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -15292,7 +15517,7 @@ +@@ -15508,7 +15733,7 @@ ctypes_malloc_closure=yes ;; #( @@ -997,7 +984,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 ctypes_malloc_closure=yes ;; #( -@@ -19044,12 +19269,6 @@ +@@ -19260,12 +19485,6 @@ then : printf "%s\n" "#define HAVE_DUP3 1" >>confdefs.h @@ -1010,7 +997,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 fi ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes -@@ -19110,18 +19329,6 @@ +@@ -19326,18 +19545,6 @@ then : printf "%s\n" "#define HAVE_FEXECVE 1" >>confdefs.h @@ -1029,7 +1016,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 fi ac_fn_c_check_func "$LINENO" "fpathconf" "ac_cv_func_fpathconf" if test "x$ac_cv_func_fpathconf" = xyes -@@ -19548,24 +19755,6 @@ +@@ -19764,24 +19971,6 @@ then : printf "%s\n" "#define HAVE_POSIX_OPENPT 1" >>confdefs.h @@ -1054,7 +1041,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 fi ac_fn_c_check_func "$LINENO" "pread" "ac_cv_func_pread" if test "x$ac_cv_func_pread" = xyes -@@ -19884,12 +20073,6 @@ +@@ -20100,12 +20289,6 @@ then : printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h @@ -1067,7 +1054,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 fi ac_fn_c_check_func "$LINENO" "sigfillset" "ac_cv_func_sigfillset" if test "x$ac_cv_func_sigfillset" = xyes -@@ -20158,11 +20341,11 @@ +@@ -20374,11 +20557,11 @@ fi @@ -1081,7 +1068,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" if test "x$ac_cv_func_getentropy" = xyes then : -@@ -20184,6 +20367,53 @@ +@@ -20400,6 +20583,53 @@ fi @@ -1135,7 +1122,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 { 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} -@@ -23266,7 +23496,8 @@ +@@ -23844,7 +24074,8 @@ # check for openpty, login_tty, and forkpty @@ -1145,7 +1132,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 for ac_func in openpty do : -@@ -23380,7 +23611,7 @@ +@@ -23958,7 +24189,7 @@ fi done @@ -1154,7 +1141,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 printf %s "checking for library containing login_tty... " >&6; } if test ${ac_cv_search_login_tty+y} then : -@@ -23563,6 +23794,7 @@ +@@ -24141,6 +24372,7 @@ fi done @@ -1162,7 +1149,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 # check for long file support functions ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" -@@ -23828,10 +24060,10 @@ +@@ -24406,10 +24638,10 @@ done @@ -1175,7 +1162,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 then for ac_func in clock_settime -@@ -24148,7 +24380,7 @@ +@@ -24726,7 +24958,7 @@ e) if test "$cross_compiling" = yes then : @@ -1184,7 +1171,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 ac_cv_buggy_getaddrinfo="no" elif test "${enable_ipv6+set}" = set; then ac_cv_buggy_getaddrinfo="no -- configured with --(en|dis)able-ipv6" -@@ -26170,8 +26402,8 @@ +@@ -26748,8 +26980,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -1195,7 +1182,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -29041,7 +29273,7 @@ +@@ -29619,7 +29851,7 @@ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for device files" >&5 printf "%s\n" "$as_me: checking for device files" >&6;} @@ -1204,7 +1191,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -29550,7 +29782,7 @@ +@@ -30129,7 +30361,7 @@ with_ensurepip=no ;; #( WASI) : with_ensurepip=no ;; #( @@ -1213,7 +1200,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 with_ensurepip=no ;; #( *) : with_ensurepip=upgrade -@@ -30499,7 +30731,7 @@ +@@ -31078,7 +31310,7 @@ SunOS*) _PYTHREAD_NAME_MAXLEN=31;; NetBSD*) _PYTHREAD_NAME_MAXLEN=15;; # gh-131268 Darwin) _PYTHREAD_NAME_MAXLEN=63;; @@ -1222,7 +1209,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 FreeBSD*) _PYTHREAD_NAME_MAXLEN=19;; # gh-131268 OpenBSD*) _PYTHREAD_NAME_MAXLEN=23;; # gh-131268 *) _PYTHREAD_NAME_MAXLEN=;; -@@ -30531,7 +30763,7 @@ +@@ -31110,7 +31342,7 @@ ;; #( Darwin) : ;; #( @@ -1231,7 +1218,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 -@@ -34605,6 +34837,9 @@ +@@ -35272,6 +35504,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" ;; @@ -1242,7 +1229,7 @@ index 1b75ddfa26d..7b79dfc424c 100755 "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 c449bb5ebb3..29b9a82374b 100644 +index cf25148bad2..7ab0609bf8a 100644 --- a/configure.ac +++ b/configure.ac @@ -330,6 +330,15 @@ @@ -1631,7 +1618,7 @@ index c449bb5ebb3..29b9a82374b 100644 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -3456,7 +3655,7 @@ +@@ -3470,7 +3669,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -1640,7 +1627,7 @@ index c449bb5ebb3..29b9a82374b 100644 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -3580,7 +3779,7 @@ +@@ -3594,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 @@ -1649,7 +1636,7 @@ index c449bb5ebb3..29b9a82374b 100644 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -3604,7 +3803,7 @@ +@@ -3618,7 +3817,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -1658,7 +1645,7 @@ index c449bb5ebb3..29b9a82374b 100644 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -4024,7 +4223,7 @@ +@@ -4106,7 +4305,7 @@ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], @@ -1667,7 +1654,7 @@ index c449bb5ebb3..29b9a82374b 100644 ctypes_malloc_closure=yes ], [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] -@@ -5133,9 +5332,9 @@ +@@ -5215,9 +5414,9 @@ # checks for library functions AC_CHECK_FUNCS([ \ accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ @@ -1679,7 +1666,7 @@ index c449bb5ebb3..29b9a82374b 100644 gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \ getgrnam_r getgrouplist gethostname getitimer getloadavg getlogin \ getpeername getpgid getpid getppid getpriority _getpty \ -@@ -5143,8 +5342,7 @@ +@@ -5225,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 \ @@ -1689,7 +1676,7 @@ index c449bb5ebb3..29b9a82374b 100644 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 -@@ -5154,7 +5352,7 @@ +@@ -5236,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 \ @@ -1698,7 +1685,7 @@ index c449bb5ebb3..29b9a82374b 100644 sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ -@@ -5169,12 +5367,20 @@ +@@ -5251,12 +5449,20 @@ AC_CHECK_FUNCS([lchmod]) fi @@ -1722,7 +1709,7 @@ index c449bb5ebb3..29b9a82374b 100644 fi AC_CHECK_DECL([dirfd], -@@ -5428,20 +5634,22 @@ +@@ -5539,20 +5745,22 @@ ]) # check for openpty, login_tty, and forkpty @@ -1759,7 +1746,7 @@ index c449bb5ebb3..29b9a82374b 100644 # check for long file support functions AC_CHECK_FUNCS([fseek64 fseeko fstatvfs ftell64 ftello statvfs]) -@@ -5480,10 +5688,10 @@ +@@ -5591,10 +5799,10 @@ ]) ]) @@ -1772,7 +1759,7 @@ index c449bb5ebb3..29b9a82374b 100644 then AC_CHECK_FUNCS([clock_settime], [], [ AC_CHECK_LIB([rt], [clock_settime], [ -@@ -5641,7 +5849,7 @@ +@@ -5752,7 +5960,7 @@ [ac_cv_buggy_getaddrinfo=no], [ac_cv_buggy_getaddrinfo=yes], [ @@ -1781,7 +1768,7 @@ index c449bb5ebb3..29b9a82374b 100644 ac_cv_buggy_getaddrinfo="no" elif test "${enable_ipv6+set}" = set; then ac_cv_buggy_getaddrinfo="no -- configured with --(en|dis)able-ipv6" -@@ -6234,8 +6442,8 @@ +@@ -6345,8 +6553,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -1792,7 +1779,7 @@ index c449bb5ebb3..29b9a82374b 100644 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -6894,7 +7102,7 @@ +@@ -7005,7 +7213,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. @@ -1801,7 +1788,7 @@ index c449bb5ebb3..29b9a82374b 100644 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -7195,7 +7403,7 @@ +@@ -7307,7 +7515,7 @@ AS_CASE([$ac_sys_system], [Emscripten], [with_ensurepip=no], [WASI], [with_ensurepip=no], @@ -1810,7 +1797,7 @@ index c449bb5ebb3..29b9a82374b 100644 [with_ensurepip=upgrade] ) ]) -@@ -7582,7 +7790,7 @@ +@@ -7694,7 +7902,7 @@ SunOS*) _PYTHREAD_NAME_MAXLEN=31;; NetBSD*) _PYTHREAD_NAME_MAXLEN=15;; # gh-131268 Darwin) _PYTHREAD_NAME_MAXLEN=63;; @@ -1819,7 +1806,7 @@ index c449bb5ebb3..29b9a82374b 100644 FreeBSD*) _PYTHREAD_NAME_MAXLEN=19;; # gh-131268 OpenBSD*) _PYTHREAD_NAME_MAXLEN=23;; # gh-131268 *) _PYTHREAD_NAME_MAXLEN=;; -@@ -7607,7 +7815,7 @@ +@@ -7719,7 +7927,7 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], @@ -2252,6 +2239,20 @@ index c3e261ecd9e..26ef7a95de4 100644 + + --- /dev/null ++++ b/visionOS/testbed/Python.xcframework/xros-arm64-simulator/README +@@ -0,0 +1,4 @@ ++This directory is intentionally empty. ++ ++It should be used as a target for `--enable-framework` when compiling an visionOS simulator ++build for testing purposes (either x86_64 or ARM64). +--- /dev/null ++++ b/visionOS/testbed/Python.xcframework/xros-arm64/README +@@ -0,0 +1,4 @@ ++This directory is intentionally empty. ++ ++It should be used as a target for `--enable-framework` when compiling an visionOS on-device ++build for testing purposes. +--- /dev/null +++ b/visionOS/testbed/__main__.py @@ -0,0 +1,512 @@ +import argparse From c012e5d4309b3a6767bcc5787f9cd57cfbcd48e9 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 3 Jun 2025 15:23:46 +0800 Subject: [PATCH 093/113] Add xcprivacy and dSYM handling (#285) Adds xcprivacy handling for modules that link OpenSSL, and includes dSYM files as part of build. --- Makefile | 10 ++++++++++ patch/Python/OpenSSL.xcprivacy | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 patch/Python/OpenSSL.xcprivacy diff --git a/Makefile b/Makefile index 79c2f19f..44170ec6 100644 --- a/Makefile +++ b/Makefile @@ -457,6 +457,7 @@ $$(PYTHON_LIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_LIB-$$ mkdir -p $$(dir $$(PYTHON_LIB-$(sdk))) lipo -create -output $$@ $$^ \ 2>&1 | tee -a install/$(os)/$(sdk)/python-$(PYTHON_VERSION).lipo.log + dsymutil $$@ -o $$(PYTHON_INSTALL-$(sdk))/Python.dSYM $$(PYTHON_FRAMEWORK-$(sdk))/Info.plist: $$(PYTHON_LIB-$(sdk)) @echo ">>> Install Info.plist for the $(sdk) SDK" @@ -523,6 +524,14 @@ $$(PYTHON_STDLIB-$(sdk))/LICENSE.TXT: $$(PYTHON_LIB-$(sdk)) $$(PYTHON_FRAMEWORK- # Merge the binary modules from each target in the $(sdk) SDK into a single binary $$(foreach module,$$(wildcard $$(PYTHON_STDLIB-$$(firstword $$(SDK_TARGETS-$(sdk))))/lib-dynload/*),lipo -create -output $$(PYTHON_STDLIB-$(sdk))/lib-dynload/$$(notdir $$(module)) $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_STDLIB-$$(target))/lib-dynload/$$(notdir $$(module))); ) + # Create dSYM files for each module + $$(foreach module,$$(wildcard $$(PYTHON_STDLIB-$$(firstword $$(SDK_TARGETS-$(sdk))))/lib-dynload/*),dsymutil $$(PYTHON_STDLIB-$(sdk))/lib-dynload/$$(notdir $$(module)); ) + + # Copy in known-required xcprivacy files. + # Libraries linking OpenSSL must provide a privacy manifest. The one in this repository + # has been sourced from https://github.com/openssl/openssl/blob/openssl-3.0/os-dep/Apple/PrivacyInfo.xcprivacy + cp $(PROJECT_DIR)/patch/Python/OpenSSL.xcprivacy $$(PYTHON_STDLIB-$(sdk))/lib-dynload/_hashlib.xcprivacy + cp $(PROJECT_DIR)/patch/Python/OpenSSL.xcprivacy $$(PYTHON_STDLIB-$(sdk))/lib-dynload/_ssl.xcprivacy endif $(sdk): $$(PYTHON_STDLIB-$(sdk))/LICENSE.TXT @@ -660,6 +669,7 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/bin $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/lib $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/platform-config $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) + $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/Python.dSYM $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) ifeq ($(filter $(os),iOS visionOS),$(os)) @echo ">>> Clone testbed project for $(os)" diff --git a/patch/Python/OpenSSL.xcprivacy b/patch/Python/OpenSSL.xcprivacy new file mode 100644 index 00000000..95780a09 --- /dev/null +++ b/patch/Python/OpenSSL.xcprivacy @@ -0,0 +1,23 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + + + + NSPrivacyCollectedDataTypes + + NSPrivacyTrackingDomains + + NSPrivacyTracking + + + From 80198354e18fc3d808be1ef2a42ca10c3cfb3fe3 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 3 Jun 2025 21:12:10 +0800 Subject: [PATCH 094/113] Temporarily disable dSYM production. (#293) --- Makefile | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 44170ec6..ab0b3563 100644 --- a/Makefile +++ b/Makefile @@ -457,7 +457,8 @@ $$(PYTHON_LIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_LIB-$$ mkdir -p $$(dir $$(PYTHON_LIB-$(sdk))) lipo -create -output $$@ $$^ \ 2>&1 | tee -a install/$(os)/$(sdk)/python-$(PYTHON_VERSION).lipo.log - dsymutil $$@ -o $$(PYTHON_INSTALL-$(sdk))/Python.dSYM + # Disable dSYM production (for now) + # dsymutil $$@ -o $$(PYTHON_INSTALL-$(sdk))/Python.dSYM $$(PYTHON_FRAMEWORK-$(sdk))/Info.plist: $$(PYTHON_LIB-$(sdk)) @echo ">>> Install Info.plist for the $(sdk) SDK" @@ -524,8 +525,9 @@ $$(PYTHON_STDLIB-$(sdk))/LICENSE.TXT: $$(PYTHON_LIB-$(sdk)) $$(PYTHON_FRAMEWORK- # Merge the binary modules from each target in the $(sdk) SDK into a single binary $$(foreach module,$$(wildcard $$(PYTHON_STDLIB-$$(firstword $$(SDK_TARGETS-$(sdk))))/lib-dynload/*),lipo -create -output $$(PYTHON_STDLIB-$(sdk))/lib-dynload/$$(notdir $$(module)) $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_STDLIB-$$(target))/lib-dynload/$$(notdir $$(module))); ) - # Create dSYM files for each module - $$(foreach module,$$(wildcard $$(PYTHON_STDLIB-$$(firstword $$(SDK_TARGETS-$(sdk))))/lib-dynload/*),dsymutil $$(PYTHON_STDLIB-$(sdk))/lib-dynload/$$(notdir $$(module)); ) + # # Disable dSYM production (for now) + # # Create dSYM files for each module + # $$(foreach module,$$(wildcard $$(PYTHON_STDLIB-$$(firstword $$(SDK_TARGETS-$(sdk))))/lib-dynload/*),dsymutil $$(PYTHON_STDLIB-$(sdk))/lib-dynload/$$(notdir $$(module)); ) # Copy in known-required xcprivacy files. # Libraries linking OpenSSL must provide a privacy manifest. The one in this repository @@ -669,7 +671,8 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/bin $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/lib $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/platform-config $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) - $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/Python.dSYM $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) + # 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)) @echo ">>> Clone testbed project for $(os)" From a7aaafeb767c600b243dce02876503ec398a549f Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 4 Jun 2025 12:29:51 +0800 Subject: [PATCH 095/113] Correct issues with testing in CI (#295) Adds a patch to account for empty testing simulator sets. Also adds CI to confirm the testbed works in a CI environment. --- .github/workflows/ci.yaml | 94 +++++++++++++++++++++++++++++++++------ patch/Python/Python.patch | 13 ++++++ 2 files changed, 94 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 924ce566..717febef 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -90,16 +90,6 @@ jobs: fail-fast: false matrix: target: ['macOS', 'iOS', 'tvOS', 'watchOS', 'visionOS'] - include: - - briefcase-run-args: - - run-tests: false - - - target: macOS - run-tests: true - - - target: iOS - briefcase-run-args: ' -d "iPhone SE (3rd generation)"' - run-tests: true steps: - uses: actions/checkout@v4.1.7 @@ -125,20 +115,98 @@ jobs: name: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz path: dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz + briefcase-testbed: + name: Briefcase testbed (${{ matrix.target }}) + runs-on: macOS-latest + needs: [ config, build ] + strategy: + fail-fast: false + matrix: + target: ["macOS", "iOS"] + include: + - briefcase-run-args: + + - target: iOS + briefcase-run-args: ' -d "iPhone SE (3rd generation)"' + + steps: + - uses: actions/checkout@v4.1.7 + + - name: Get build artifact + uses: actions/download-artifact@v4.3.0 + with: + pattern: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz + path: dist + merge-multiple: true + + - name: Set up Python + uses: actions/setup-python@v5.6.0 + with: + # Appending -dev ensures that we can always build the dev release. + # It's a no-op for versions that have been published. + python-version: ${{ needs.config.outputs.PYTHON_VER }}-dev + # Ensure that we *always* use the latest build, not a cached version. + # It's an edge case, but when a new alpha is released, we need to use it ASAP. + check-latest: true + - uses: actions/checkout@v4.1.7 - if: matrix.run-tests with: repository: beeware/Python-support-testbed path: Python-support-testbed - name: Install dependencies - if: matrix.run-tests run: | # Use the development version of Briefcase python -m pip install git+https://github.com/beeware/briefcase.git - name: Run support testbed check - if: matrix.run-tests timeout-minutes: 10 working-directory: Python-support-testbed run: briefcase run ${{ matrix.target }} Xcode --test ${{ matrix.briefcase-run-args }} -C support_package=\'../dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz\' + + cpython-testbed: + name: CPython testbed (${{ matrix.target }}) + runs-on: macOS-latest + needs: [ config, build ] + strategy: + fail-fast: false + matrix: + target: ["iOS", "visionOS"] + + steps: + - uses: actions/checkout@v4.1.7 + + - name: Get build artifact + uses: actions/download-artifact@v4.3.0 + with: + pattern: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz + path: dist + merge-multiple: true + + - name: Set up Python + uses: actions/setup-python@v5.6.0 + with: + # Appending -dev ensures that we can always build the dev release. + # It's a no-op for versions that have been published. + python-version: ${{ needs.config.outputs.PYTHON_VER }}-dev + # Ensure that we *always* use the latest build, not a cached version. + # It's an edge case, but when a new alpha is released, we need to use it ASAP. + check-latest: true + + - name: Unpack support package + run: | + mkdir support + cd support + tar zxvf ../dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz + + - name: Run CPython testbed + timeout-minutes: 10 + working-directory: support + run: | + # Run a representative subset of CPython core tests: + # - test_builtin as a test of core language tools + # - test_grammar as a test of core language features + # - 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 diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 8db5c523..73c5b5a6 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1835,6 +1835,19 @@ index c3e261ecd9e..26ef7a95de4 100644 CFBundleSupportedPlatforms iPhoneOS +diff --git a/iOS/testbed/__main__.py b/iOS/testbed/__main__.py +index c05497ede3a..1146bf3b988 100644 +--- a/iOS/testbed/__main__.py ++++ b/iOS/testbed/__main__.py +@@ -127,7 +127,7 @@ + async def select_simulator_device(): + # List the testing simulators, in JSON format + raw_json = await async_check_output( +- "xcrun", "simctl", "--set", "testing", "list", "-j" ++ "xcrun", "simctl", "list", "-j" + ) + json_data = json.loads(raw_json) + --- /dev/null +++ b/tvOS/README.rst @@ -0,0 +1,108 @@ From 119de47c967156df1d8c77936bcedc027b1909b0 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 6 Jun 2025 09:49:17 +0800 Subject: [PATCH 096/113] Correct handling of sysconfig._BASE_PREFIX in cross environments. (#297) Ensure that sysconfig._BASE_PREFIX is bound to base_prefix, not prefix. --- patch/Python/_cross_target.py.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patch/Python/_cross_target.py.tmpl b/patch/Python/_cross_target.py.tmpl index fbe178fc..458ec074 100644 --- a/patch/Python/_cross_target.py.tmpl +++ b/patch/Python/_cross_target.py.tmpl @@ -70,7 +70,7 @@ sysconfig.get_platform = cross_get_platform sysconfig._get_sysconfigdata_name = cross_get_sysconfigdata_name # Ensure module-level values cached at time of import are updated. -sysconfig._BASE_PREFIX = sys.prefix +sysconfig._BASE_PREFIX = sys.base_prefix sysconfig._BASE_EXEC_PREFIX = sys.base_exec_prefix # Force sysconfig data to be loaded (and cached). From 5b1bb20974edd9f6bffcf495d959e134493cc2c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 06:25:01 +1000 Subject: [PATCH 097/113] Bump ncipollo/release-action from 1.16.0 to 1.17.0 (#299) Bumps [ncipollo/release-action](https://github.com/ncipollo/release-action) from 1.16.0 to 1.17.0. - [Release notes](https://github.com/ncipollo/release-action/releases) - [Commits](https://github.com/ncipollo/release-action/compare/v1.16.0...v1.17.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index fd78eeec..7c89ab5a 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -47,7 +47,7 @@ jobs: merge-multiple: true - name: Create Release - uses: ncipollo/release-action@v1.16.0 + uses: ncipollo/release-action@v1.17.0 with: name: ${{ needs.ci.outputs.PYTHON_VER }}-${{ needs.config.outputs.BUILD_NUMBER }} tag: ${{ needs.ci.outputs.PYTHON_VER }}-${{ needs.config.outputs.BUILD_NUMBER }} From 6366590c4c79d1df145b2e16bd3306c96d0d6fb2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Jul 2025 07:52:16 +1000 Subject: [PATCH 098/113] Bump ncipollo/release-action from 1.17.0 to 1.18.0 (#300) Bumps [ncipollo/release-action](https://github.com/ncipollo/release-action) from 1.17.0 to 1.18.0. - [Release notes](https://github.com/ncipollo/release-action/releases) - [Commits](https://github.com/ncipollo/release-action/compare/v1.17.0...v1.18.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 7c89ab5a..6767d2ef 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -47,7 +47,7 @@ jobs: merge-multiple: true - name: Create Release - uses: ncipollo/release-action@v1.17.0 + uses: ncipollo/release-action@v1.18.0 with: name: ${{ needs.ci.outputs.PYTHON_VER }}-${{ needs.config.outputs.BUILD_NUMBER }} tag: ${{ needs.ci.outputs.PYTHON_VER }}-${{ needs.config.outputs.BUILD_NUMBER }} From 42c9975afc220a80364cde386724ab174b2acd58 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 8 Jul 2025 13:15:17 -0500 Subject: [PATCH 099/113] Fix markdown (#302) --- USAGE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/USAGE.md b/USAGE.md index 072ae1ec..3ea37aca 100644 --- a/USAGE.md +++ b/USAGE.md @@ -3,7 +3,7 @@ ## The easy way The easist way to use these packages is by creating a project with -(Briefcase)[https://github.com/beeware/briefcase]. Briefcase will download +[Briefcase](https://github.com/beeware/briefcase). Briefcase will download pre-compiled versions of these support packages, and add them to an Xcode project (or pre-build stub application, in the case of macOS). From 162523d08d8b2d7dcfa4821ba1a2fafcf4b0a53b Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Tue, 15 Jul 2025 13:11:43 +1000 Subject: [PATCH 100/113] Fixed typos (#305) --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index ab0b3563..5c0a97d8 100644 --- a/Makefile +++ b/Makefile @@ -89,7 +89,7 @@ update-patch: # comparing between the current state of the 3.X branch against the v3.X.Y # tag associated with the release being built. This allows you to # maintain a branch that contains custom patches against the default Python. - # The patch archived in this respository is based on github.com/freakboy3742/cpython + # The patch archived in this repository is based on github.com/freakboy3742/cpython # Requires patchutils (installable via `brew install patchutils`); this # also means we need to re-introduce homebrew to the path for the filterdiff # call @@ -333,7 +333,7 @@ $$(PYTHON_LIB-$(target)): $$(PYTHON_SRCDIR-$(target))/python.exe $$(PYTHON_PLATFORM_SITECUSTOMIZE-$(target)): - @echo ">>> Create cross-plaform config for $(target)" + @echo ">>> Create cross-platform config for $(target)" mkdir -p $$(PYTHON_PLATFORM_CONFIG-$(target)) # Create the cross-platform site definition echo "import _cross_$$(ARCH-$(target))_$$(SDK-$(target)); import _cross_venv;" \ From 207110a862a56d34a6876ddfe58c44c7e0e781e4 Mon Sep 17 00:00:00 2001 From: yoorimgye-dw Date: Sun, 20 Jul 2025 11:53:15 +0200 Subject: [PATCH 101/113] convert README from .rst to .md (#306) --- README.md | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++++ README.rst | 154 ------------------------------------------------ 2 files changed, 168 insertions(+), 154 deletions(-) create mode 100644 README.md delete mode 100644 README.rst diff --git a/README.md b/README.md new file mode 100644 index 00000000..33e689de --- /dev/null +++ b/README.md @@ -0,0 +1,168 @@ +# Python Apple Support + +This is a meta-package for building a version of Python that can be +embedded into a macOS, iOS, tvOS, watchOS, or visionOS project. + +**This branch builds a packaged version of Python 3.14**. Other Python +versions are available by cloning other branches of the main repository: + +- [Python 3.9](https://github.com/beeware/Python-Apple-support/tree/3.9) +- [Python + 3.10](https://github.com/beeware/Python-Apple-support/tree/3.10) +- [Python + 3.11](https://github.com/beeware/Python-Apple-support/tree/3.11) +- [Python + 3.12](https://github.com/beeware/Python-Apple-support/tree/3.12) +- [Python + 3.13](https://github.com/beeware/Python-Apple-support/tree/3.13) + +It works by downloading, patching, and building a fat binary of Python +and selected pre-requisites, and packaging them as frameworks that can +be incorporated into an Xcode project. The binary modules in the Python +standard library are distributed as binaries that can be dynamically +loaded at runtime. + +The macOS package is a re-bundling of the official macOS binary, +modified so that it is relocatable, with the IDLE, Tkinter and turtle +packages removed, and the App Store compliance patch applied. + +The iOS, tvOS, watchOS, and visionOS packages compiled by this project +use the official [PEP 730](https://peps.python.org/pep-0730/) code that +is part of Python 3.13 to provide iOS support; the relevant patches have +been backported to 3.9-3.12. Additional patches have been applied to add +tvOS, watchOS, and visionOS support. + +The binaries support x86_64 and arm64 for macOS; arm64 for iOS and +appleTV devices; arm64_32 for watchOS devices; and arm64 for visionOS +devices. It also supports device simulators on both x86_64 and M1 +hardware, except for visionOS, for which x86_64 simulators are +officially unsupported. This should enable the code to run on: + +- macOS 11 (Big Sur) or later, on: + - MacBook (including MacBooks using Apple Silicon) + - iMac (including iMacs using Apple Silicon) + - Mac Mini (including Apple Silicon Mac minis) + - Mac Studio (all models) + - Mac Pro (all models) + +- iOS 13.0 or later, on: + - iPhone (6s or later) + - iPad (5th gen or later) + - iPad Air (all models) + - iPad Mini (2 or later) + - iPad Pro (all models) + - iPod Touch (7th gen or later) + +- tvOS 12.0 or later, on: + - Apple TV (4th gen or later) + +- watchOS 4.0 or later, on: + - Apple Watch (4th gen or later) + +- visionOS 2.0 or later, on: + - Apple Vision Pro + +## Quickstart + +The easist way to use these packages is by creating a project with +[Briefcase](https://github.com/beeware/briefcase). Briefcase will +download pre-compiled versions of these support packages, and add them +to an Xcode project (or pre-build stub application, in the case of +macOS). + +Pre-built versions of the frameworks can be downloaded from the [Github +releases page](https://github.com/beeware/Python-Apple-support/releases) +and added to your project. + +Alternatively, to build the frameworks on your own, download/clone this +repository, and then in the root directory, and run: + +- `make` (or `make all`) to build everything. +- `make macOS` to build everything for macOS. +- `make iOS` to build everything for iOS. +- `make tvOS` to build everything for tvOS. +- `make watchOS` to build everything for watchOS. +- `make visionOS` to build everything for visionOS. + +This should: + +1. Download the original source packages +2. Patch them as required for compatibility with the selected OS +3. Build the packages as Xcode-compatible XCFrameworks. + +The resulting support packages will be packaged as `.tar.gz` files in +the `dist` folder. + +Each support package contains: + +- `VERSIONS`, a text file describing the specific versions of code used + to build the support package; +- `Python.xcframework`, a multi-architecture build of the Python runtime + library. + +On iOS/tvOS/watchOS/visionOS, the `Python.xcframework` contains a slice +for each supported ABI (device and simulator). The folder containing the +slice can also be used as a `PYTHONHOME`, as it contains a `bin`, +`include` and `lib` directory. + +The `bin` folder does not contain Python executables (as they can't be +invoked). However, it *does* contain shell aliases for the compilers +that are needed to build packages. This is required because Xcode uses +the `xcrun` alias to dynamically generate the name of binaries, but a +lot of C tooling expects that `CC` will not contain spaces. + +Each slice of an iOS/tvOS/watchOS/visionOS XCframework also contains a +`platform-config` folder with a subfolder for each supported +architecture in that slice. These subfolders can be used to make a macOS +Python environment behave as if it were on an iOS/tvOS/watchOS/visionOS +device. This works in one of two ways: + +1. **A sitecustomize.py script**. If the `platform-config` subfolder is + on your `PYTHONPATH` when a Python interpreter is started, a site + customization will be applied that patches methods in `sys`, + `sysconfig` and `platform` that are used to identify the system. +2. **A make_cross_venv.py script**. If you call `make_cross_venv.py`, + providing the location of a virtual environment, the script will add + some files to the `site-packages` folder of that environment that + will automatically apply the same set of patches as the + `sitecustomize.py` script whenever the environment is activated, + without any need to modify `PYTHONPATH`. If you use `build` to + create an isolated PEP 517 environment to build a wheel, these + patches will also be applied to the isolated build environment that + is created. + +iOS and visionOS distributions also contain a copy of the iOS or +visionOS `testbed` project - an Xcode project that can be used to run +test suites of Python code. See the [CPython documentation on testing +packages](https://docs.python.org/3/using/ios.html#testing-a-python-package) +for details on how to use this testbed. + +For a detailed instructions on using the support package in your own +project, see the [usage guide](./USAGE.md) + +## Building binary wheels + +This project packages the Python standard library, but does not address +building binary wheels. Binary wheels for macOS can be obtained from +PyPI. [Mobile Forge](https://github.com/beeware/mobile-forge) is a +project that provides the tooling to build build binary wheels for iOS +(and potentially for tvOS, watchOS, and visionOS, although that hasn't +been tested). + +## Historical support + +The following versions were supported in the past, but are no longer +maintained: + +- [Python 2.7](https://github.com/beeware/Python-Apple-support/tree/2.7) + (EOL January 2020) +- [Python 3.4](https://github.com/beeware/Python-Apple-support/tree/3.4) + (EOL March 2019) +- [Python 3.5](https://github.com/beeware/Python-Apple-support/tree/3.5) + (EOL February 2021) +- [Python 3.6](https://github.com/beeware/Python-Apple-support/tree/3.6) + (EOL December 2021) +- [Python 3.7](https://github.com/beeware/Python-Apple-support/tree/3.7) + (EOL September 2022) +- [Python 3.8](https://github.com/beeware/Python-Apple-support/tree/3.8) + (EOL October 2024) diff --git a/README.rst b/README.rst deleted file mode 100644 index 06fde6c9..00000000 --- a/README.rst +++ /dev/null @@ -1,154 +0,0 @@ -Python Apple Support -==================== - -This is a meta-package for building a version of Python that can be embedded -into a macOS, iOS, tvOS, watchOS, or visionOS project. - -**This branch builds a packaged version of Python 3.14**. -Other Python versions are available by cloning other branches of the main -repository: - -* `Python 3.9 `__ -* `Python 3.10 `__ -* `Python 3.11 `__ -* `Python 3.12 `__ -* `Python 3.13 `__ - -It works by downloading, patching, and building a fat binary of Python and -selected pre-requisites, and packaging them as frameworks that can be -incorporated into an Xcode project. The binary modules in the Python standard -library are distributed as binaries that can be dynamically loaded at runtime. - -The macOS package is a re-bundling of the official macOS binary, modified so that -it is relocatable, with the IDLE, Tkinter and turtle packages removed, and the -App Store compliance patch applied. - -The iOS, tvOS, watchOS, and visionOS packages compiled by this project use the -official `PEP 730 `__ code that is part of -Python 3.13 to provide iOS support; the relevant patches have been backported -to 3.9-3.12. Additional patches have been applied to add tvOS, watchOS, and -visionOS support. - -The binaries support x86_64 and arm64 for macOS; arm64 for iOS and appleTV -devices; arm64_32 for watchOS devices; and arm64 for visionOS devices. It also -supports device simulators on both x86_64 and M1 hardware, except for visionOS, -for which x86_64 simulators are officially unsupported. This should enable the -code to run on: - -* macOS 11 (Big Sur) or later, on: - * MacBook (including MacBooks using Apple Silicon) - * iMac (including iMacs using Apple Silicon) - * Mac Mini (including Apple Silicon Mac minis) - * Mac Studio (all models) - * Mac Pro (all models) -* iOS 13.0 or later, on: - * iPhone (6s or later) - * iPad (5th gen or later) - * iPad Air (all models) - * iPad Mini (2 or later) - * iPad Pro (all models) - * iPod Touch (7th gen or later) -* tvOS 12.0 or later, on: - * Apple TV (4th gen or later) -* watchOS 4.0 or later, on: - * Apple Watch (4th gen or later) -* visionOS 2.0 or later, on: - * Apple Vision Pro - -Quickstart ----------- - -The easist way to use these packages is by creating a project with `Briefcase -`__. Briefcase will download pre-compiled -versions of these support packages, and add them to an Xcode project (or -pre-build stub application, in the case of macOS). - -Pre-built versions of the frameworks can be downloaded from the `Github releases page -`__ and added to your project. - -Alternatively, to build the frameworks on your own, download/clone this -repository, and then in the root directory, and run: - -* ``make`` (or ``make all``) to build everything. -* ``make macOS`` to build everything for macOS. -* ``make iOS`` to build everything for iOS. -* ``make tvOS`` to build everything for tvOS. -* ``make watchOS`` to build everything for watchOS. -* ``make visionOS`` to build everything for visionOS. - -This should: - -1. Download the original source packages -2. Patch them as required for compatibility with the selected OS -3. Build the packages as Xcode-compatible XCFrameworks. - -The resulting support packages will be packaged as ``.tar.gz`` files -in the ``dist`` folder. - -Each support package contains: - -* ``VERSIONS``, a text file describing the specific versions of code used to build the - support package; -* ``Python.xcframework``, a multi-architecture build of the Python runtime library. - -On iOS/tvOS/watchOS/visionOS, the ``Python.xcframework`` contains a -slice for each supported ABI (device and simulator). The folder containing the -slice can also be used as a ``PYTHONHOME``, as it contains a ``bin``, ``include`` -and ``lib`` directory. - -The ``bin`` folder does not contain Python executables (as they can't be -invoked). However, it *does* contain shell aliases for the compilers that are -needed to build packages. This is required because Xcode uses the ``xcrun`` -alias to dynamically generate the name of binaries, but a lot of C tooling -expects that ``CC`` will not contain spaces. - -Each slice of an iOS/tvOS/watchOS/visionOS XCframework also contains a -``platform-config`` folder with a subfolder for each supported architecture in -that slice. These subfolders can be used to make a macOS Python environment -behave as if it were on an iOS/tvOS/watchOS/visionOS device. This works in one -of two ways: - -1. **A sitecustomize.py script**. If the ``platform-config`` subfolder is on - your ``PYTHONPATH`` when a Python interpreter is started, a site - customization will be applied that patches methods in ``sys``, ``sysconfig`` - and ``platform`` that are used to identify the system. - -2. **A make_cross_venv.py script**. If you call ``make_cross_venv.py``, - providing the location of a virtual environment, the script will add some - files to the ``site-packages`` folder of that environment that will - automatically apply the same set of patches as the ``sitecustomize.py`` - script whenever the environment is activated, without any need to modify - ``PYTHONPATH``. If you use ``build`` to create an isolated PEP 517 - environment to build a wheel, these patches will also be applied to the - isolated build environment that is created. - -iOS and visionOS distributions also contain a copy of the iOS or visionOS -``testbed`` project - an Xcode project that can be used to run test suites of -Python code. See the `CPython documentation on testing packages -`__ for -details on how to use this testbed. - -For a detailed instructions on using the support package in your own project, -see the `usage guide <./USAGE.md>`__ - -Building binary wheels ----------------------- - -This project packages the Python standard library, but does not address building -binary wheels. Binary wheels for macOS can be obtained from PyPI. `Mobile Forge -`__ is a project that provides the -tooling to build build binary wheels for iOS (and potentially for tvOS, watchOS, -and visionOS, although that hasn't been tested). - -Historical support ------------------- - -The following versions were supported in the past, but are no longer -maintained: - -* `Python 2.7 `__ (EOL January 2020) -* `Python 3.4 `__ (EOL March 2019) -* `Python 3.5 `__ (EOL February 2021) -* `Python 3.6 `__ (EOL December 2021) -* `Python 3.7 `__ (EOL September 2022) -* `Python 3.8 `__ (EOL October 2024) From b65dbf5df8c681b103ead427a86bf454272bc3fe Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Thu, 24 Jul 2025 11:01:02 +0800 Subject: [PATCH 102/113] Update patch for 3.14.0rc1 (#307) --- Makefile | 2 +- patch/Python/Python.patch | 165 ++++++++++++++++++++------------------ 2 files changed, 87 insertions(+), 80 deletions(-) diff --git a/Makefile b/Makefile index 5c0a97d8..3579649c 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.0b2 +PYTHON_VERSION=3.14.0rc1 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+") diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 73c5b5a6..f9f6d8e2 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -56,7 +56,7 @@ index 8bcd741c446..d8a6f28edba 100644 suffix.replace(".so", ".fwork") for suffix in _imp.extension_suffixes() diff --git a/Lib/platform.py b/Lib/platform.py -index 55e211212d4..cad919bc0c4 100644 +index 7dd64de6025..a7e3c77f803 100644 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -528,6 +528,78 @@ @@ -282,7 +282,7 @@ index f93b98dd681..0db3dbdce05 100644 import _osx_support osname, release, machine = _osx_support.get_platform_osx( diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py -index 1b551254f86..8594f92c097 100644 +index 1c1cbd03d02..1378985de4a 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -7159,9 +7159,9 @@ @@ -298,10 +298,10 @@ index 1b551254f86..8594f92c097 100644 else: extension_loader = "ExtensionFileLoader" diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py -index b7cd7940eb1..32243a49e7a 100644 +index 001ecec4dcd..e46a5661fe8 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py -@@ -558,7 +558,7 @@ +@@ -559,7 +559,7 @@ sys.platform == "android", f"Android blocks {name} with SELinux" ) @@ -310,7 +310,7 @@ index b7cd7940eb1..32243a49e7a 100644 unix_shell = '/system/bin/sh' if is_android else '/bin/sh' else: unix_shell = None -@@ -574,7 +574,7 @@ +@@ -575,7 +575,7 @@ def skip_wasi_stack_overflow(): return unittest.skipIf(is_wasi, "Exhausts stack on WASI") @@ -403,7 +403,7 @@ index f2e2394089d..2efbbfb0014 100644 if objc: # If objc exists, we know ctypes is also importable. diff --git a/Makefile.pre.in b/Makefile.pre.in -index b5703fbe6ae..e436b2efb8e 100644 +index 01e10d1ab20..ddc430beb80 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -209,6 +209,12 @@ @@ -419,7 +419,7 @@ index b5703fbe6ae..e436b2efb8e 100644 # Option to install to strip binaries STRIPFLAG=-s -@@ -2264,7 +2270,7 @@ +@@ -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. @@ -428,7 +428,7 @@ index b5703fbe6ae..e436b2efb8e 100644 .PHONY: testios testios: @if test "$(MACHDEP)" != "ios"; then \ -@@ -2284,11 +2290,41 @@ +@@ -2314,11 +2320,41 @@ exit 1;\ fi @@ -533,7 +533,7 @@ index 1bb6a05dc11..49febd56a37 100755 none--*) # None (no kernel, i.e. freestanding / bare metal), diff --git a/configure b/configure -index 884f8a4b068..7c93d36e717 100755 +index 345822a6c6d..11acfe0ac9f 100755 --- a/configure +++ b/configure @@ -982,6 +982,10 @@ @@ -930,7 +930,7 @@ index 884f8a4b068..7c93d36e717 100755 aarch64-*-linux-android/clang) : PY_SUPPORT_TIER=3 ;; #( x86_64-*-linux-android/clang) : -@@ -7686,7 +7911,7 @@ +@@ -7688,7 +7913,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; @@ -939,7 +939,7 @@ index 884f8a4b068..7c93d36e717 100755 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5;; -@@ -7752,7 +7977,7 @@ +@@ -7754,7 +7979,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -948,7 +948,7 @@ index 884f8a4b068..7c93d36e717 100755 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -13574,7 +13799,7 @@ +@@ -13570,7 +13795,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -957,7 +957,7 @@ index 884f8a4b068..7c93d36e717 100755 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -13707,7 +13932,7 @@ +@@ -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 @@ -966,7 +966,7 @@ index 884f8a4b068..7c93d36e717 100755 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -13731,7 +13956,7 @@ +@@ -13727,7 +13952,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -975,7 +975,7 @@ index 884f8a4b068..7c93d36e717 100755 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -15508,7 +15733,7 @@ +@@ -15504,7 +15729,7 @@ ctypes_malloc_closure=yes ;; #( @@ -984,7 +984,7 @@ index 884f8a4b068..7c93d36e717 100755 ctypes_malloc_closure=yes ;; #( -@@ -19260,12 +19485,6 @@ +@@ -19256,12 +19481,6 @@ then : printf "%s\n" "#define HAVE_DUP3 1" >>confdefs.h @@ -997,7 +997,7 @@ index 884f8a4b068..7c93d36e717 100755 fi ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes -@@ -19326,18 +19545,6 @@ +@@ -19322,18 +19541,6 @@ then : printf "%s\n" "#define HAVE_FEXECVE 1" >>confdefs.h @@ -1016,7 +1016,7 @@ index 884f8a4b068..7c93d36e717 100755 fi ac_fn_c_check_func "$LINENO" "fpathconf" "ac_cv_func_fpathconf" if test "x$ac_cv_func_fpathconf" = xyes -@@ -19764,24 +19971,6 @@ +@@ -19766,24 +19973,6 @@ then : printf "%s\n" "#define HAVE_POSIX_OPENPT 1" >>confdefs.h @@ -1041,7 +1041,7 @@ index 884f8a4b068..7c93d36e717 100755 fi ac_fn_c_check_func "$LINENO" "pread" "ac_cv_func_pread" if test "x$ac_cv_func_pread" = xyes -@@ -20100,12 +20289,6 @@ +@@ -20102,12 +20291,6 @@ then : printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h @@ -1054,7 +1054,7 @@ index 884f8a4b068..7c93d36e717 100755 fi ac_fn_c_check_func "$LINENO" "sigfillset" "ac_cv_func_sigfillset" if test "x$ac_cv_func_sigfillset" = xyes -@@ -20374,11 +20557,11 @@ +@@ -20376,11 +20559,11 @@ fi @@ -1068,7 +1068,7 @@ index 884f8a4b068..7c93d36e717 100755 ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" if test "x$ac_cv_func_getentropy" = xyes then : -@@ -20400,6 +20583,53 @@ +@@ -20402,6 +20585,53 @@ fi @@ -1122,7 +1122,7 @@ index 884f8a4b068..7c93d36e717 100755 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5 printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; } if test ${ac_cv_c_undeclared_builtin_options+y} -@@ -23844,7 +24074,8 @@ +@@ -23873,7 +24103,8 @@ # check for openpty, login_tty, and forkpty @@ -1132,7 +1132,7 @@ index 884f8a4b068..7c93d36e717 100755 for ac_func in openpty do : -@@ -23958,7 +24189,7 @@ +@@ -23987,7 +24218,7 @@ fi done @@ -1141,7 +1141,7 @@ index 884f8a4b068..7c93d36e717 100755 printf %s "checking for library containing login_tty... " >&6; } if test ${ac_cv_search_login_tty+y} then : -@@ -24141,6 +24372,7 @@ +@@ -24170,6 +24401,7 @@ fi done @@ -1149,7 +1149,7 @@ index 884f8a4b068..7c93d36e717 100755 # check for long file support functions ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" -@@ -24406,10 +24638,10 @@ +@@ -24435,10 +24667,10 @@ done @@ -1162,7 +1162,7 @@ index 884f8a4b068..7c93d36e717 100755 then for ac_func in clock_settime -@@ -24726,7 +24958,7 @@ +@@ -24755,7 +24987,7 @@ e) if test "$cross_compiling" = yes then : @@ -1171,7 +1171,7 @@ index 884f8a4b068..7c93d36e717 100755 ac_cv_buggy_getaddrinfo="no" elif test "${enable_ipv6+set}" = set; then ac_cv_buggy_getaddrinfo="no -- configured with --(en|dis)able-ipv6" -@@ -26748,8 +26980,8 @@ +@@ -26777,8 +27009,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -1182,7 +1182,7 @@ index 884f8a4b068..7c93d36e717 100755 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -29619,7 +29851,7 @@ +@@ -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;} @@ -1191,7 +1191,7 @@ index 884f8a4b068..7c93d36e717 100755 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -30129,7 +30361,7 @@ +@@ -30155,7 +30387,7 @@ with_ensurepip=no ;; #( WASI) : with_ensurepip=no ;; #( @@ -1200,7 +1200,7 @@ index 884f8a4b068..7c93d36e717 100755 with_ensurepip=no ;; #( *) : with_ensurepip=upgrade -@@ -31078,7 +31310,7 @@ +@@ -31104,7 +31336,7 @@ SunOS*) _PYTHREAD_NAME_MAXLEN=31;; NetBSD*) _PYTHREAD_NAME_MAXLEN=15;; # gh-131268 Darwin) _PYTHREAD_NAME_MAXLEN=63;; @@ -1209,7 +1209,7 @@ index 884f8a4b068..7c93d36e717 100755 FreeBSD*) _PYTHREAD_NAME_MAXLEN=19;; # gh-131268 OpenBSD*) _PYTHREAD_NAME_MAXLEN=23;; # gh-131268 *) _PYTHREAD_NAME_MAXLEN=;; -@@ -31110,7 +31342,7 @@ +@@ -31136,7 +31368,7 @@ ;; #( Darwin) : ;; #( @@ -1218,7 +1218,7 @@ index 884f8a4b068..7c93d36e717 100755 -@@ -35272,6 +35504,9 @@ +@@ -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" ;; @@ -1229,7 +1229,7 @@ index 884f8a4b068..7c93d36e717 100755 "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 cf25148bad2..7ab0609bf8a 100644 +index d6059471771..7e1a5ffe604 100644 --- a/configure.ac +++ b/configure.ac @@ -330,6 +330,15 @@ @@ -1587,20 +1587,40 @@ index cf25148bad2..7ab0609bf8a 100644 [SOABI_PLATFORM=$PLATFORM_TRIPLET] ) -@@ -1227,6 +1420,12 @@ - [x86_64-*-freebsd*/clang], [PY_SUPPORT_TIER=3], dnl FreeBSD on AMD64 - [aarch64-apple-ios*-simulator/clang], [PY_SUPPORT_TIER=3], dnl iOS Simulator on arm64 - [aarch64-apple-ios*/clang], [PY_SUPPORT_TIER=3], dnl iOS on ARM64 -+ [aarch64-apple-tvos*-simulator/clang], [PY_SUPPORT_TIER=3], dnl tvOS Simulator on arm64 -+ [aarch64-apple-tvos*/clang], [PY_SUPPORT_TIER=3], dnl tvOS on ARM64 -+ [aarch64-apple-watchos*-simulator/clang], [PY_SUPPORT_TIER=3], dnl watchOS Simulator on arm64 -+ [arm64_32-apple-watchos*/clang], [PY_SUPPORT_TIER=3], dnl watchOS on ARM64 -+ [aarch64-apple-xros*-simulator/clang], [PY_SUPPORT_TIER=3], dnl visionOS Simulator on arm64 -+ [aarch64-apple-xros*/clang], [PY_SUPPORT_TIER=3], dnl visionOS on ARM64 - [aarch64-*-linux-android/clang], [PY_SUPPORT_TIER=3], dnl Android on ARM64 - [x86_64-*-linux-android/clang], [PY_SUPPORT_TIER=3], dnl Android on AMD64 - -@@ -1536,7 +1735,7 @@ +@@ -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)';; @@ -1609,7 +1629,7 @@ index cf25148bad2..7ab0609bf8a 100644 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) AC_MSG_ERROR([Unknown platform for framework build]);; -@@ -1601,7 +1800,7 @@ +@@ -1602,7 +1801,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -1618,7 +1638,7 @@ index cf25148bad2..7ab0609bf8a 100644 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -3470,7 +3669,7 @@ +@@ -3469,7 +3668,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -1627,7 +1647,7 @@ index cf25148bad2..7ab0609bf8a 100644 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -3594,7 +3793,7 @@ +@@ -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 @@ -1636,7 +1656,7 @@ index cf25148bad2..7ab0609bf8a 100644 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -3618,7 +3817,7 @@ +@@ -3617,7 +3816,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -1645,7 +1665,7 @@ index cf25148bad2..7ab0609bf8a 100644 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -4106,7 +4305,7 @@ +@@ -4105,7 +4304,7 @@ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], @@ -1654,7 +1674,7 @@ index cf25148bad2..7ab0609bf8a 100644 ctypes_malloc_closure=yes ], [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] -@@ -5215,9 +5414,9 @@ +@@ -5214,9 +5413,9 @@ # checks for library functions AC_CHECK_FUNCS([ \ accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ @@ -1664,9 +1684,9 @@ index cf25148bad2..7ab0609bf8a 100644 - 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 \ + getgrnam_r getgrouplist gethostname getitimer getloadavg getlogin getlogin_r \ getpeername getpgid getpid getppid getpriority _getpty \ -@@ -5225,8 +5424,7 @@ +@@ -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 \ @@ -1676,7 +1696,7 @@ index cf25148bad2..7ab0609bf8a 100644 pread preadv preadv2 process_vm_readv \ pthread_cond_timedwait_relative_np pthread_condattr_setclock pthread_init \ pthread_kill pthread_get_name_np pthread_getname_np pthread_set_name_np -@@ -5236,7 +5434,7 @@ +@@ -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 \ @@ -1685,7 +1705,7 @@ index cf25148bad2..7ab0609bf8a 100644 sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ -@@ -5251,12 +5449,20 @@ +@@ -5250,12 +5448,20 @@ AC_CHECK_FUNCS([lchmod]) fi @@ -1709,8 +1729,8 @@ index cf25148bad2..7ab0609bf8a 100644 fi AC_CHECK_DECL([dirfd], -@@ -5539,20 +5745,22 @@ - ]) +@@ -5550,20 +5756,22 @@ + [@%:@include ]) # check for openpty, login_tty, and forkpty - @@ -1746,7 +1766,7 @@ index cf25148bad2..7ab0609bf8a 100644 # check for long file support functions AC_CHECK_FUNCS([fseek64 fseeko fstatvfs ftell64 ftello statvfs]) -@@ -5591,10 +5799,10 @@ +@@ -5602,10 +5810,10 @@ ]) ]) @@ -1759,7 +1779,7 @@ index cf25148bad2..7ab0609bf8a 100644 then AC_CHECK_FUNCS([clock_settime], [], [ AC_CHECK_LIB([rt], [clock_settime], [ -@@ -5752,7 +5960,7 @@ +@@ -5763,7 +5971,7 @@ [ac_cv_buggy_getaddrinfo=no], [ac_cv_buggy_getaddrinfo=yes], [ @@ -1768,7 +1788,7 @@ index cf25148bad2..7ab0609bf8a 100644 ac_cv_buggy_getaddrinfo="no" elif test "${enable_ipv6+set}" = set; then ac_cv_buggy_getaddrinfo="no -- configured with --(en|dis)able-ipv6" -@@ -6345,8 +6553,8 @@ +@@ -6356,8 +6564,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -1779,7 +1799,7 @@ index cf25148bad2..7ab0609bf8a 100644 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -7005,7 +7213,7 @@ +@@ -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. @@ -1788,7 +1808,7 @@ index cf25148bad2..7ab0609bf8a 100644 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -7307,7 +7515,7 @@ +@@ -7316,7 +7524,7 @@ AS_CASE([$ac_sys_system], [Emscripten], [with_ensurepip=no], [WASI], [with_ensurepip=no], @@ -1797,7 +1817,7 @@ index cf25148bad2..7ab0609bf8a 100644 [with_ensurepip=upgrade] ) ]) -@@ -7694,7 +7902,7 @@ +@@ -7703,7 +7911,7 @@ SunOS*) _PYTHREAD_NAME_MAXLEN=31;; NetBSD*) _PYTHREAD_NAME_MAXLEN=15;; # gh-131268 Darwin) _PYTHREAD_NAME_MAXLEN=63;; @@ -1806,7 +1826,7 @@ index cf25148bad2..7ab0609bf8a 100644 FreeBSD*) _PYTHREAD_NAME_MAXLEN=19;; # gh-131268 OpenBSD*) _PYTHREAD_NAME_MAXLEN=23;; # gh-131268 *) _PYTHREAD_NAME_MAXLEN=;; -@@ -7719,7 +7927,7 @@ +@@ -7728,7 +7936,7 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], @@ -1835,19 +1855,6 @@ index c3e261ecd9e..26ef7a95de4 100644 CFBundleSupportedPlatforms iPhoneOS -diff --git a/iOS/testbed/__main__.py b/iOS/testbed/__main__.py -index c05497ede3a..1146bf3b988 100644 ---- a/iOS/testbed/__main__.py -+++ b/iOS/testbed/__main__.py -@@ -127,7 +127,7 @@ - async def select_simulator_device(): - # List the testing simulators, in JSON format - raw_json = await async_check_output( -- "xcrun", "simctl", "--set", "testing", "list", "-j" -+ "xcrun", "simctl", "list", "-j" - ) - json_data = json.loads(raw_json) - --- /dev/null +++ b/tvOS/README.rst @@ -0,0 +1,108 @@ From d9eeafc53ef4b4cbe7df5661d5609beac7933b9d Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 28 Jul 2025 13:38:20 +0800 Subject: [PATCH 103/113] Correct cross-platform venv path handling (#310) Corrects for changes in Python 3.14's handling of sysconfig._init_config_vars(), ensuring that cross-platform virtual environments have correct include paths. --- .github/workflows/ci.yaml | 99 ++++++++++++++++++++++++------ .gitignore | 2 + patch/Python/_cross_target.py.tmpl | 4 +- tests/test_cross_env.py | 93 ++++++++++++++++++++++++++++ 4 files changed, 178 insertions(+), 20 deletions(-) create mode 100644 tests/test_cross_env.py diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 717febef..9f5e5f4d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -89,7 +89,7 @@ jobs: strategy: fail-fast: false matrix: - target: ['macOS', 'iOS', 'tvOS', 'watchOS', 'visionOS'] + platform: ['macOS', 'iOS', 'tvOS', 'watchOS', 'visionOS'] steps: - uses: actions/checkout@v4.1.7 @@ -104,29 +104,29 @@ jobs: # It's an edge case, but when a new alpha is released, we need to use it ASAP. check-latest: true - - name: Build ${{ matrix.target }} + - name: Build ${{ matrix.platform }} run: | - # Do the build for the requested target. - make ${{ matrix.target }} BUILD_NUMBER=${{ needs.config.outputs.BUILD_NUMBER }} + # Do the build for the requested platform. + make ${{ matrix.platform }} BUILD_NUMBER=${{ needs.config.outputs.BUILD_NUMBER }} - name: Upload build artefacts uses: actions/upload-artifact@v4.6.2 with: - name: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz - path: dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz + name: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.platform }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz + path: dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.platform }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz briefcase-testbed: - name: Briefcase testbed (${{ matrix.target }}) + name: Briefcase testbed (${{ matrix.platform }}) runs-on: macOS-latest needs: [ config, build ] strategy: fail-fast: false matrix: - target: ["macOS", "iOS"] + platform: ["macOS", "iOS"] include: - briefcase-run-args: - - target: iOS + - platform: iOS briefcase-run-args: ' -d "iPhone SE (3rd generation)"' steps: @@ -135,7 +135,7 @@ jobs: - name: Get build artifact uses: actions/download-artifact@v4.3.0 with: - pattern: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz + pattern: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.platform }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz path: dist merge-multiple: true @@ -162,16 +162,16 @@ jobs: - name: Run support testbed check timeout-minutes: 10 working-directory: Python-support-testbed - run: briefcase run ${{ matrix.target }} Xcode --test ${{ matrix.briefcase-run-args }} -C support_package=\'../dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz\' + run: briefcase run ${{ matrix.platform }} Xcode --test ${{ matrix.briefcase-run-args }} -C support_package=\'../dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.platform }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz\' cpython-testbed: - name: CPython testbed (${{ matrix.target }}) + name: CPython testbed (${{ matrix.platform }}) runs-on: macOS-latest needs: [ config, build ] strategy: fail-fast: false matrix: - target: ["iOS", "visionOS"] + platform: ["iOS", "visionOS"] steps: - uses: actions/checkout@v4.1.7 @@ -179,7 +179,7 @@ jobs: - name: Get build artifact uses: actions/download-artifact@v4.3.0 with: - pattern: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz + pattern: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.platform }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz path: dist merge-multiple: true @@ -195,13 +195,13 @@ jobs: - name: Unpack support package run: | - mkdir support - cd support - tar zxvf ../dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz + mkdir -p support/${{ needs.config.outputs.PYTHON_VER }}/${{ matrix.platform }} + cd support/${{ needs.config.outputs.PYTHON_VER }}/${{ matrix.platform }} + tar zxvf ../../../dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.platform }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz - name: Run CPython testbed timeout-minutes: 10 - working-directory: support + working-directory: support/${{ needs.config.outputs.PYTHON_VER }}/${{ matrix.platform }} run: | # Run a representative subset of CPython core tests: # - test_builtin as a test of core language tools @@ -210,3 +210,66 @@ jobs: # - 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 + + crossenv-test: + name: Cross-platform env test (${{ matrix.multiarch }}) + runs-on: macOS-latest + needs: [ config, build ] + strategy: + fail-fast: false + matrix: + include: + - platform: iOS + slice: ios-arm64_x86_64-simulator + multiarch: arm64-iphonesimulator + - platform: iOS + slice: ios-arm64_x86_64-simulator + multiarch: x86_64-iphonesimulator + - platform: iOS + slice: ios-arm64 + multiarch: arm64-iphoneos + + steps: + - uses: actions/checkout@v4.1.7 + + - name: Get build artifact + uses: actions/download-artifact@v4.3.0 + with: + pattern: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.platform }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz + path: dist + merge-multiple: true + + - name: Set up Python + uses: actions/setup-python@v5.6.0 + with: + # Appending -dev ensures that we can always build the dev release. + # It's a no-op for versions that have been published. + python-version: ${{ needs.config.outputs.PYTHON_VER }}-dev + # Ensure that we *always* use the latest build, not a cached version. + # It's an edge case, but when a new alpha is released, we need to use it ASAP. + check-latest: true + + - name: Unpack support package + run: | + mkdir -p support/${{ needs.config.outputs.PYTHON_VER }}/${{ matrix.platform }} + cd support/${{ needs.config.outputs.PYTHON_VER }}/${{ matrix.platform }} + tar zxvf ../../../dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.platform }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz + + - name: Run cross-platform environment test + env: + PYTHON_CROSS_PLATFORM: ${{ matrix.platform }} + PYTHON_CROSS_SLICE: ${{ matrix.slice }} + PYTHON_CROSS_MULTIARCH: ${{ matrix.multiarch }} + run: | + # Create and activate a native virtual environment + python${{ needs.config.outputs.PYTHON_VER }} -m venv cross-venv + source cross-venv/bin/activate + + # Install pytest + python -m pip install pytest + + # Convert venv into cross-venv + python support/${{ needs.config.outputs.PYTHON_VER }}/${{ matrix.platform }}/Python.xcframework/${{ matrix.slice }}/platform-config/${{ matrix.multiarch }}/make_cross_venv.py cross-venv + + # Run the test suite + python -m pytest tests diff --git a/.gitignore b/.gitignore index f8053935..a87e3e2a 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ tests/testbed/iOS *.log *.gz *.DS_Store +cross-venv/ +temp diff --git a/patch/Python/_cross_target.py.tmpl b/patch/Python/_cross_target.py.tmpl index 458ec074..06303350 100644 --- a/patch/Python/_cross_target.py.tmpl +++ b/patch/Python/_cross_target.py.tmpl @@ -11,8 +11,8 @@ import sysconfig sys.cross_compiling = True sys.platform = "{{platform}}" sys.implementation._multiarch = "{{arch}}-{{sdk}}" -sys.base_prefix = sysconfig.get_config_var("prefix") -sys.base_exec_prefix = sysconfig.get_config_var("prefix") +sys.base_prefix = sysconfig._get_sysconfigdata()["prefix"] +sys.base_exec_prefix = sysconfig._get_sysconfigdata()["prefix"] ########################################################################### # subprocess module patches diff --git a/tests/test_cross_env.py b/tests/test_cross_env.py new file mode 100644 index 00000000..34e8c5eb --- /dev/null +++ b/tests/test_cross_env.py @@ -0,0 +1,93 @@ +import os +import platform +import sys +import sysconfig +from pathlib import Path + +import pytest + +# To run these tests, the following three environment variables must be set, +# reflecting the cross-platform environment that is in effect.' +PYTHON_CROSS_PLATFORM = os.getenv("PYTHON_CROSS_PLATFORM", "unknown") +PYTHON_CROSS_SLICE = os.getenv("PYTHON_CROSS_SLICE", "unknown") +PYTHON_CROSS_MULTIARCH = os.getenv("PYTHON_CROSS_MULTIARCH", "unknown") + +# Determine some file system anchor points for the tests +# Assumes that the tests are run in a virtual environment named +# `cross-venv`, +VENV_PREFIX = Path(__file__).parent.parent / "cross-venv" +default_support_base = f"support/{sys.version_info.major}.{sys.version_info.minor}/{PYTHON_CROSS_PLATFORM}" +SUPPORT_PREFIX = ( + Path(__file__).parent.parent + / os.getenv("PYTHON_SUPPORT_BASE", default_support_base) + / "Python.xcframework" + / PYTHON_CROSS_SLICE +) + + +########################################################################### +# sys +########################################################################### + +def test_sys_platform(): + assert sys.platform == PYTHON_CROSS_PLATFORM.lower() + + +def test_sys_cross_compiling(): + assert sys.cross_compiling + + +def test_sys_multiarch(): + assert sys.implementation._multiarch == PYTHON_CROSS_MULTIARCH + + +def test_sys_base_prefix(): + assert Path(sys.base_prefix) == SUPPORT_PREFIX + + +def test_sys_base_exec_prefix(): + assert Path(sys.base_exec_prefix) == SUPPORT_PREFIX + + +########################################################################### +# platform +########################################################################### + +def test_platform_system(): + assert platform.system() == PYTHON_CROSS_PLATFORM + + +########################################################################### +# sysconfig +########################################################################### + +def test_sysconfig_get_platform(): + parts = sysconfig.get_platform().split("-", 2) + assert parts[0] == PYTHON_CROSS_PLATFORM.lower() + assert parts[2] == PYTHON_CROSS_MULTIARCH + + +def test_sysconfig_get_sysconfigdata_name(): + parts = sysconfig._get_sysconfigdata_name().split("_", 4) + assert parts[3] == PYTHON_CROSS_PLATFORM.lower() + assert parts[4] == PYTHON_CROSS_MULTIARCH + + +@pytest.mark.parametrize( + "name, prefix", + [ + # Paths that should be relative to the support folder + ("stdlib", SUPPORT_PREFIX), + ("include", SUPPORT_PREFIX), + ("platinclude", SUPPORT_PREFIX), + ("stdlib", SUPPORT_PREFIX), + # paths that should be relative to the venv + ("platstdlib", VENV_PREFIX), + ("purelib", VENV_PREFIX), + ("platlib", VENV_PREFIX), + ("scripts", VENV_PREFIX), + ("data", VENV_PREFIX), + ] +) +def test_sysconfig_get_paths(name, prefix): + assert sysconfig.get_paths()[name].startswith(str(prefix)) From 7ec96c724c3508b85b6ab31d28d6a2c08a18a544 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 08:51:28 +0800 Subject: [PATCH 104/113] Bump actions/download-artifact from 4.3.0 to 5.0.0 (#312) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.3.0 to 5.0.0. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4.3.0...v5.0.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yaml | 6 +++--- .github/workflows/release.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9f5e5f4d..36edb3b7 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -133,7 +133,7 @@ jobs: - uses: actions/checkout@v4.1.7 - name: Get build artifact - uses: actions/download-artifact@v4.3.0 + uses: actions/download-artifact@v5.0.0 with: pattern: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.platform }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz path: dist @@ -177,7 +177,7 @@ jobs: - uses: actions/checkout@v4.1.7 - name: Get build artifact - uses: actions/download-artifact@v4.3.0 + uses: actions/download-artifact@v5.0.0 with: pattern: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.platform }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz path: dist @@ -233,7 +233,7 @@ jobs: - uses: actions/checkout@v4.1.7 - name: Get build artifact - uses: actions/download-artifact@v4.3.0 + uses: actions/download-artifact@v5.0.0 with: pattern: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.platform }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz path: dist diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 6767d2ef..dde7312b 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -40,7 +40,7 @@ jobs: needs: [ config, ci ] steps: - name: Get build artifacts - uses: actions/download-artifact@v4.3.0 + uses: actions/download-artifact@v5.0.0 with: pattern: Python-* path: dist From 9a9fcf7b4292b803c64c4e80e7677556555233d5 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Mon, 18 Aug 2025 08:15:26 +1000 Subject: [PATCH 105/113] Fixed typos (#313) --- USAGE.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/USAGE.md b/USAGE.md index 3ea37aca..b205cdcc 100644 --- a/USAGE.md +++ b/USAGE.md @@ -2,7 +2,7 @@ ## The easy way -The easist way to use these packages is by creating a project with +The easiest way to use these packages is by creating a project with [Briefcase](https://github.com/beeware/briefcase). Briefcase will download pre-compiled versions of these support packages, and add them to an Xcode project (or pre-build stub application, in the case of macOS). @@ -10,7 +10,7 @@ pre-compiled versions of these support packages, and add them to an Xcode projec ## The manual way **NOTE** Briefcase usage is the officially supported approach for using this -support package. If you are experiencing diffculties, one approach for debugging +support package. If you are experiencing difficulties, one approach for debugging is to generate a "Hello World" project with Briefcase, and compare the project that Briefcase has generated with your own project. @@ -28,7 +28,7 @@ generated on iOS and visionOS may be used as rough references as well. ### Using Objective C -Once you've added the Python XCframework to your project, you'll need to +Once you've added the Python XCFramework to your project, you'll need to initialize the Python runtime in your Objective C code (This is step 10 of the iOS guide linked above). This initialization should generally be done as early as possible in the application's lifecycle, but definitely needs to be done @@ -97,7 +97,7 @@ There are 2 ways to access the Python runtime in your project code. You can use the [Python Embedded C API](https://docs.python.org/3/extending/embedding.html) to invoke Python code -and interact with Python objects. This is a raw C API that is accesible to both +and interact with Python objects. This is a raw C API that is accessible to both Objective C and Swift. ### PythonKit From ec2920be1d5ede1708f3be93d9a926a46cb66c1b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Aug 2025 19:20:57 +0800 Subject: [PATCH 106/113] Bump actions/checkout from 4 to 5 (#314) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yaml | 46 +++++++++++++++++++++++++--------- .github/workflows/publish.yaml | 2 +- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 36edb3b7..e2782b09 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -56,7 +56,7 @@ jobs: XZ_VERSION: ${{ steps.extract.outputs.XZ_VERSION }} steps: - - uses: actions/checkout@v4.1.7 + - uses: actions/checkout@v5 - name: Extract config variables id: extract @@ -84,7 +84,7 @@ jobs: echo "XZ_VERSION=${XZ_VERSION}" | tee -a ${GITHUB_OUTPUT} build: - runs-on: macOS-latest + runs-on: macOS-15 needs: [ config ] strategy: fail-fast: false @@ -92,7 +92,16 @@ jobs: platform: ['macOS', 'iOS', 'tvOS', 'watchOS', 'visionOS'] steps: - - uses: actions/checkout@v4.1.7 + - uses: actions/checkout@v5 + + - name: Set up Xcode + # GitHub recommends explicitly selecting the desired Xcode version: + # https://github.com/actions/runner-images/issues/12541#issuecomment-3083850140 + # This became a necessity as a result of + # https://github.com/actions/runner-images/issues/12541 and + # https://github.com/actions/runner-images/issues/12751. + run: | + sudo xcode-select --switch /Applications/Xcode_16.4.app - name: Set up Python uses: actions/setup-python@v5.6.0 @@ -117,7 +126,7 @@ jobs: briefcase-testbed: name: Briefcase testbed (${{ matrix.platform }}) - runs-on: macOS-latest + runs-on: macOS-15 needs: [ config, build ] strategy: fail-fast: false @@ -127,10 +136,19 @@ jobs: - briefcase-run-args: - platform: iOS - briefcase-run-args: ' -d "iPhone SE (3rd generation)"' + briefcase-run-args: ' -d "iPhone 16e::iOS 18.5"' steps: - - uses: actions/checkout@v4.1.7 + - uses: actions/checkout@v5 + + - name: Set up Xcode + # GitHub recommends explicitly selecting the desired Xcode version: + # https://github.com/actions/runner-images/issues/12541#issuecomment-3083850140 + # This became a necessity as a result of + # https://github.com/actions/runner-images/issues/12541 and + # https://github.com/actions/runner-images/issues/12751. + run: | + sudo xcode-select --switch /Applications/Xcode_16.4.app - name: Get build artifact uses: actions/download-artifact@v5.0.0 @@ -149,7 +167,7 @@ jobs: # It's an edge case, but when a new alpha is released, we need to use it ASAP. check-latest: true - - uses: actions/checkout@v4.1.7 + - uses: actions/checkout@v5 with: repository: beeware/Python-support-testbed path: Python-support-testbed @@ -160,21 +178,25 @@ jobs: python -m pip install git+https://github.com/beeware/briefcase.git - name: Run support testbed check - timeout-minutes: 10 + timeout-minutes: 15 working-directory: Python-support-testbed run: briefcase run ${{ matrix.platform }} Xcode --test ${{ matrix.briefcase-run-args }} -C support_package=\'../dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.platform }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz\' cpython-testbed: name: CPython testbed (${{ matrix.platform }}) - runs-on: macOS-latest + # For now, CPython testbed can't run on macos-15: https://github.com/actions/runner-images/issues/12777 + runs-on: macOS-14 needs: [ config, build ] strategy: fail-fast: false matrix: platform: ["iOS", "visionOS"] + include: + - platform: "iOS" + testbed-args: '--simulator "iPhone 16e,arch=arm64,OS=18.5"' steps: - - uses: actions/checkout@v4.1.7 + - uses: actions/checkout@v5 - name: Get build artifact uses: actions/download-artifact@v5.0.0 @@ -200,7 +222,7 @@ jobs: tar zxvf ../../../dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.platform }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz - name: Run CPython testbed - timeout-minutes: 10 + timeout-minutes: 15 working-directory: support/${{ needs.config.outputs.PYTHON_VER }}/${{ matrix.platform }} run: | # Run a representative subset of CPython core tests: @@ -230,7 +252,7 @@ jobs: multiarch: arm64-iphoneos steps: - - uses: actions/checkout@v4.1.7 + - uses: actions/checkout@v5 - name: Get build artifact uses: actions/download-artifact@v5.0.0 diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 587abe83..f913f7d9 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -8,7 +8,7 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python environment uses: actions/setup-python@v5.6.0 From b6e5954e37dec453953bdb534ffd9406d92aaddd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Sep 2025 06:22:10 +0800 Subject: [PATCH 107/113] Bump actions/setup-python from 5.6.0 to 6.0.0 (#318) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.6.0 to 6.0.0. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5.6.0...v6.0.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yaml | 8 ++++---- .github/workflows/publish.yaml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e2782b09..e69d4c15 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -104,7 +104,7 @@ jobs: sudo xcode-select --switch /Applications/Xcode_16.4.app - name: Set up Python - uses: actions/setup-python@v5.6.0 + uses: actions/setup-python@v6.0.0 with: # Appending -dev ensures that we can always build the dev release. # It's a no-op for versions that have been published. @@ -158,7 +158,7 @@ jobs: merge-multiple: true - name: Set up Python - uses: actions/setup-python@v5.6.0 + uses: actions/setup-python@v6.0.0 with: # Appending -dev ensures that we can always build the dev release. # It's a no-op for versions that have been published. @@ -206,7 +206,7 @@ jobs: merge-multiple: true - name: Set up Python - uses: actions/setup-python@v5.6.0 + uses: actions/setup-python@v6.0.0 with: # Appending -dev ensures that we can always build the dev release. # It's a no-op for versions that have been published. @@ -262,7 +262,7 @@ jobs: merge-multiple: true - name: Set up Python - uses: actions/setup-python@v5.6.0 + uses: actions/setup-python@v6.0.0 with: # Appending -dev ensures that we can always build the dev release. # It's a no-op for versions that have been published. diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index f913f7d9..9b45abb2 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v5 - name: Set up Python environment - uses: actions/setup-python@v5.6.0 + uses: actions/setup-python@v6.0.0 with: python-version: "3.X" From 26982d88bc5d09152214e347446ed57c4d4b3f49 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Sep 2025 06:23:26 +0800 Subject: [PATCH 108/113] Bump ncipollo/release-action from 1.18.0 to 1.20.0 (#317) Bumps [ncipollo/release-action](https://github.com/ncipollo/release-action) from 1.18.0 to 1.20.0. - [Release notes](https://github.com/ncipollo/release-action/releases) - [Commits](https://github.com/ncipollo/release-action/compare/v1.18.0...v1.20.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index dde7312b..617fbf58 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -47,7 +47,7 @@ jobs: merge-multiple: true - name: Create Release - uses: ncipollo/release-action@v1.18.0 + uses: ncipollo/release-action@v1.20.0 with: name: ${{ needs.ci.outputs.PYTHON_VER }}-${{ needs.config.outputs.BUILD_NUMBER }} tag: ${{ needs.ci.outputs.PYTHON_VER }}-${{ needs.config.outputs.BUILD_NUMBER }} From a3839add56f456add567088d850261c3420e12b3 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Sun, 28 Sep 2025 21:10:15 +0800 Subject: [PATCH 109/113] Bump to Python 3.14.0rc3. (#319) --- .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 e69d4c15..e5c63bb1 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 3579649c..6923b426 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 f9f6d8e2..89f2a548 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 From 48611cb06d2b3c1a8ad19269e0598d35b56a147a Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 29 Sep 2025 09:02:34 +0800 Subject: [PATCH 110/113] Deprecate Python 3.9 --- .github/workflows/ci.yaml | 9 +++++++++ README.md | 15 ++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e5c63bb1..791943b2 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -211,6 +211,15 @@ jobs: path: dist merge-multiple: true + - name: Set up Xcode + # GitHub recommends explicitly selecting the desired Xcode version: + # https://github.com/actions/runner-images/issues/12541#issuecomment-3083850140 + # This became a necessity as a result of + # https://github.com/actions/runner-images/issues/12541 and + # https://github.com/actions/runner-images/issues/12751. + run: | + sudo xcode-select --switch /Applications/Xcode_16.4.app + - name: Set up Python uses: actions/setup-python@v6.0.0 with: diff --git a/README.md b/README.md index 33e689de..3da329cc 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ embedded into a macOS, iOS, tvOS, watchOS, or visionOS project. **This branch builds a packaged version of Python 3.14**. Other Python versions are available by cloning other branches of the main repository: -- [Python 3.9](https://github.com/beeware/Python-Apple-support/tree/3.9) - [Python 3.10](https://github.com/beeware/Python-Apple-support/tree/3.10) - [Python @@ -29,7 +28,7 @@ packages removed, and the App Store compliance patch applied. The iOS, tvOS, watchOS, and visionOS packages compiled by this project use the official [PEP 730](https://peps.python.org/pep-0730/) code that is part of Python 3.13 to provide iOS support; the relevant patches have -been backported to 3.9-3.12. Additional patches have been applied to add +been backported to 3.10-3.12. Additional patches have been applied to add tvOS, watchOS, and visionOS support. The binaries support x86_64 and arm64 for macOS; arm64 for iOS and @@ -38,14 +37,14 @@ devices. It also supports device simulators on both x86_64 and M1 hardware, except for visionOS, for which x86_64 simulators are officially unsupported. This should enable the code to run on: -- macOS 11 (Big Sur) or later, on: +- macOS 11 (Big Sur) or later, on: - MacBook (including MacBooks using Apple Silicon) - iMac (including iMacs using Apple Silicon) - Mac Mini (including Apple Silicon Mac minis) - Mac Studio (all models) - Mac Pro (all models) -- iOS 13.0 or later, on: +- iOS 13.0 or later, on: - iPhone (6s or later) - iPad (5th gen or later) - iPad Air (all models) @@ -53,13 +52,13 @@ officially unsupported. This should enable the code to run on: - iPad Pro (all models) - iPod Touch (7th gen or later) -- tvOS 12.0 or later, on: +- tvOS 12.0 or later, on: - Apple TV (4th gen or later) -- watchOS 4.0 or later, on: +- watchOS 4.0 or later, on: - Apple Watch (4th gen or later) -- visionOS 2.0 or later, on: +- visionOS 2.0 or later, on: - Apple Vision Pro ## Quickstart @@ -166,3 +165,5 @@ maintained: (EOL September 2022) - [Python 3.8](https://github.com/beeware/Python-Apple-support/tree/3.8) (EOL October 2024) +- [Python 3.9](https://github.com/beeware/Python-Apple-support/tree/3.9) + (EOL October 2025) From 7fd6709d71bf1164bc3ddfad7f76254465ae02cc Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 29 Sep 2025 09:47:50 +0800 Subject: [PATCH 111/113] Bump OpenSSL version. --- .github/workflows/release.yaml | 1 + Makefile | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 617fbf58..a2c90694 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -62,4 +62,5 @@ jobs: * mpdecimal ${{ needs.ci.outputs.MPDECIMAL_VERSION }} * OpenSSL ${{ needs.ci.outputs.OPENSSL_VERSION }} * XZ ${{ needs.ci.outputs.XZ_VERSION }} + * Zstandard ${{ needs.ci.outputs.ZSTD_VERSION }} artifacts: "dist/*" diff --git a/Makefile b/Makefile index 6923b426..44cf4ada 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ PYTHON_VER=$(basename $(PYTHON_VERSION)) BZIP2_VERSION=1.0.8-2 LIBFFI_VERSION=3.4.7-2 MPDECIMAL_VERSION=4.0.0-2 -OPENSSL_VERSION=3.0.16-2 +OPENSSL_VERSION=3.0.17-1 XZ_VERSION=5.6.4-2 ZSTD_VERSION=1.5.7-1 From cd4cfb3d01ce2004c5360409d8a9563e7cdacf7e Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 8 Oct 2025 10:21:48 +0800 Subject: [PATCH 112/113] Update patch to 3.14.0 final. (#322) --- Makefile | 2 +- patch/Python/Python.patch | 4043 ++++--------------------------------- 2 files changed, 386 insertions(+), 3659 deletions(-) diff --git a/Makefile b/Makefile index 44cf4ada..d0f36faa 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.0rc3 +PYTHON_VERSION=3.14.0 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+") diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 89f2a548..e40920c3 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1,86 +1,34 @@ ---- /dev/null +diff --git a/Apple/__main__.py b/Apple/__main__.py +index 96c2d34fbe0..eea74ee2209 100644 +--- a/Apple/__main__.py +++ 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. -+# +@@ -5,17 +5,19 @@ + # This script simplifies the process of configuring, compiling and packaging an + # XCframework for an Apple platform. + # +-# At present, it only supports iOS, but it has been constructed so that it +-# could be used on any 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 -+# + # + # 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 + # 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 architecture and ABI +# * 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", -+ }, -+ }, + # * 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 +@@ -76,6 +78,32 @@ + "x86_64-apple-ios-simulator": "x86_64-iphonesimulator", + }, + }, + "tvOS": { + "tvos-arm64": { + "arm64-apple-tvos": "arm64-appletvos", @@ -107,67 +55,13 @@ + "x86_64-apple-watchos-simulator": "x86_64-watchsimulator", + }, + }, -+} -+ -+ -+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 -+ -+ -+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. -+ """ -+ kwargs.setdefault("check", True) -+ if env is None: -+ env = os.environ.copy() -+ -+ 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) -+ -+ -+def join_command(args: str | Path | ArgsT) -> str: -+ """Format a command so it can be copied into a shell. -+ -+ 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)}") -+ -+ + } + + +@@ -137,12 +165,25 @@ + 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(): @@ -178,561 +72,121 @@ + raise KeyError(host) + + -+def apple_env(host: str) -> EnvironmentT: -+ """Construct an Apple development environment for the given host.""" -+ env = { -+ "PATH": ":".join( -+ [ + def apple_env(host: str) -> EnvironmentT: + """Construct an Apple development environment for the given host.""" + env = { + "PATH": ":".join( + [ +- str(PYTHON_DIR / "Apple/iOS/Resources/bin"), + 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", -+ ] -+ ), -+ } -+ -+ 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"). -+ """ -+ 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.*" -+ ) -+ ] -+ ) -+ -+ for path in paths: -+ delete_path(path) -+ -+ -+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 -+ -+ -+@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 "GITHUB_ACTIONS" in os.environ: -+ print(f"::group::{text}") -+ else: -+ print(f"===== {text} " + "=" * (70 - len(text))) -+ -+ yield -+ -+ if "GITHUB_ACTIONS" in os.environ: -+ print("::endgroup::") -+ else: -+ print() -+ -+ -+@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 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) -+ -+ -+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())]) -+ -+ -+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]) -+ -+ raise KeyError(host) -+ -+ -+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 -+ -+ raise KeyError(host) -+ -+ -+def unpack_deps( -+ platform: str, -+ host: str, -+ prefix_dir: Path, -+ cache_dir: Path, -+) -> None: -+ """Unpack binary dependencies into a provided directory. -+ -+ Downloads binaries if they aren't already present. Downloads will be stored -+ in provided cache directory. -+ + str(subdir(host) / "prefix"), + "/usr/bin", + "/bin", +@@ -309,8 +350,8 @@ + Downloads binaries if they aren't already present. Downloads will be stored + in provided cache directory. + +- On iOS, as a safety mechanism, any dynamic libraries will be purged from +- the unpacked dependencies. + 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) -+ + """ + # To create new builds of these dependencies, usually all that's necessary + # is to push a tag to the cpython-apple-source-deps repository, and GitHub +@@ -335,9 +376,9 @@ + ) + shutil.unpack_archive(archive_path, prefix_dir) + +- # Dynamic libraries will be preferentially linked over static; +- # On iOS, ensure that no dylibs are available in the prefix folder. +- if platform == "iOS": + # 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() -+ -+ -+def download(url: str, target_dir: Path) -> Path: -+ """Download the specified URL into the given directory. -+ -+ :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 -+ -+ -+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 -+ -+ if context.clean: -+ clean(context, host) -+ -+ host_dir = subdir(host, create=True) -+ prefix_dir = host_dir / "prefix" -+ -+ 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", + for dylib in prefix_dir.glob("**/*.dylib"): + dylib.unlink() + +@@ -401,6 +442,7 @@ + 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", -+ ] -+ -+ if context.args: -+ command.extend(context.args) -+ run(command, host=host) -+ -+ -+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 -+ -+ with ( -+ group(f"Compiling host Python ({host})"), -+ cwd(subdir(host)), -+ ): -+ run(["make", "-j", str(os.cpu_count())], host=host) -+ run(["make", "install"], host=host) -+ -+ -+def framework_path(host_triple: str, multiarch: str) -> Path: -+ """The path to a built single-architecture framework product. -+ -+ :param host_triple: The host triple (e.g., arm64-apple-ios-simulator) -+ :param multiarch: The multiarch identifier (e.g., arm64-simulator) -+ """ + "--enable-framework", + # Dependent libraries. + f"--with-openssl={prefix_dir}", +@@ -441,7 +483,10 @@ + :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/iOS/Frameworks/{multiarch}" + return ( + CROSS_BUILD_DIR + / f"{host_triple}/Apple/{platform_for_host(host_triple)}/Frameworks/{multiarch}" + ) -+ -+ -+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): + + + def package_version(prefix_path: Path) -> str: +@@ -468,8 +513,7 @@ + + + def lib_platform_files(dirname, names): +- """A file filter that ignores platform-specific files in the lib directory. +- """ + """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 -+ -+ -+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"): + path = Path(dirname) + if ( + path.parts[-3] == "lib" +@@ -499,7 +543,9 @@ + """ + path = Path(dirname) + if path.parts[-2] == "lib" and path.parts[-1].startswith("python"): +- return set(names) - lib_platform_files(dirname, names) - {"lib-dynload"} + return ( + set(names) - lib_platform_files(dirname, names) - {"lib-dynload"} + ) -+ else: -+ return set() -+ -+ -+def create_xcframework(platform: str) -> str: -+ """Build an XCframework from the component parts for the platform. -+ -+ :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" -+ ) -+ -+ 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", -+ ) -+ -+ # Copy the headers -+ shutil.copytree( -+ first_framework / "Headers", -+ slice_framework / "Headers", -+ ) -+ -+ # 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() -+ ] -+ ) -+ -+ # Add this merged slice to the list to be added to the XCframework -+ frameworks.append(slice_framework) -+ -+ 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, -+ ) -+ -+ # Copy in the cross-architecture pyconfig.h -+ shutil.copy( -+ PYTHON_DIR / f"Apple/{platform}/Resources/pyconfig.h", -+ slice_framework / "Headers/pyconfig.h", -+ ) -+ -+ 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 -+ -+ shutil.copytree( -+ framework_path(host_triple, multiarch) / "lib", -+ slice_path / f"lib-{arch}", -+ ignore=lib_non_platform_files, -+ ) -+ -+ # Copy the host's pyconfig.h to an architecture-specific name. -+ arch = multiarch.split("-")[0] -+ host_path = ( -+ CROSS_BUILD_DIR -+ / host_triple + else: + return set() + +@@ -646,7 +692,7 @@ + host_path = ( + CROSS_BUILD_DIR + / host_triple +- / "Apple/iOS/Frameworks" + / 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", -+ ) -+ -+ # 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 = { + / multiarch + ) + host_framework = host_path / "Python.framework" +@@ -659,7 +705,8 @@ + # 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" + "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", -+ ) -+ -+ print(" - build tools") -+ shutil.copytree( -+ PYTHON_DIR / "Apple/testbed/Python.xcframework/build", -+ package_path / "Python.xcframework/build", -+ ) -+ -+ return version -+ -+ -+def package(context: argparse.Namespace) -> None: -+ """The implementation of the "package" command.""" -+ if context.clean: -+ clean(context, "package") -+ -+ with group("Building package"): -+ # Create an XCframework -+ version = create_xcframework(context.platform) -+ + } + print(f" - {multiarch} xcprivacy files") + for module, lib in [ +@@ -690,20 +737,22 @@ + # Create an XCframework + version = create_xcframework(context.platform) + +- # 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", +- ] +- ) + # watchOS doesn't have a testbed (yet!) + if context.platform != "watchOS": + # Clone testbed @@ -749,808 +203,37 @@ + CROSS_BUILD_DIR / context.platform / "testbed", + ] + ) -+ -+ # Build the final archive -+ archive_name = ( -+ CROSS_BUILD_DIR -+ / "dist" -+ / f"python-{version}-{context.platform}-XCframework" -+ ) -+ -+ 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.") -+ -+ -+def build(context: argparse.Namespace, host: str | None = None) -> None: -+ """The implementation of the "build" command.""" -+ if host is None: -+ host = context.host -+ -+ if context.clean: -+ clean(context, host) -+ -+ if host in {"all", "build"}: -+ for step in [ -+ configure_build_python, -+ make_build_python, -+ ]: -+ step(context) -+ -+ if host == "build": -+ hosts = [] -+ elif host in {"all", "hosts"}: -+ hosts = all_host_triples(context.platform) -+ else: -+ hosts = [host] -+ -+ for step_host in hosts: -+ for step in [ -+ configure_host_python, -+ make_host_python, -+ ]: -+ step(context, host=step_host) -+ -+ if host in {"all", "hosts"}: -+ package(context) -+ -+ -+def test(context: argparse.Namespace, host: str | None = None) -> None: -+ """The implementation of the "test" command.""" -+ if host is None: -+ host = context.host -+ -+ if context.clean: -+ clean(context, "test") -+ -+ 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] -+ -+ 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)}" -+ ) -+ -+ run( -+ [ -+ sys.executable, -+ "Apple/testbed", -+ "clone", -+ "--platform", -+ context.platform, -+ "--framework", -+ framework_path, -+ testbed_dir, -+ ] -+ ) -+ -+ 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", -+ ] -+ ) -+ -+ -+def ci(context: argparse.Namespace) -> None: -+ """The implementation of the "ci" command.""" -+ clean(context, "all") -+ build(context, host="all") -+ test(context, host="all") -+ -+ -+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) -+ -+ clean = subcommands.add_parser( -+ "clean", -+ help="Delete all build directories", -+ ) -+ -+ 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", -+ ) -+ -+ # 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", -+ ) -+ -+ # 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'" -+ ), -+ ) -+ -+ # --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", -+ ) -+ -+ # --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.", -+ ) -+ -+ # --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`" -+ ) -+ -+ return parser.parse_args() -+ -+ -+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") -+ -+ # shlex uses single quotes, so we surround the command with double quotes. -+ print( -+ f'Command "{join_command(e.cmd)}" returned exit status {e.returncode}' -+ ) -+ -+ -+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, -+ } -+ -+ 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) -+ -+ -+if __name__ == "__main__": -+ main() ---- /dev/null -+++ b/Apple/iOS/README.md -@@ -0,0 +1,328 @@ -+# Python on iOS README -+ -+**iOS support is [tier 3](https://peps.python.org/pep-0011/#tier-3).** -+ -+This document provides a quick overview of some iOS specific features in the -+Python distribution. -+ -+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. -+ -+## Compilers for building on iOS -+ -+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: -+ -+ - `arm64-apple-ios` for ARM64 iOS devices. -+ - `arm64-apple-ios-simulator` for the iOS simulator running on Apple -+ Silicon devices. -+ - `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 -+ the compiler. This is one of: -+ -+ - `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 -+ 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 iOS won't run on macOS, so -+ you need to provide an external Python interpreter. This interpreter must be -+ 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 + + # Build the final archive + archive_name = ( +diff --git a/Apple/iOS/Resources/Info.plist.in b/Apple/iOS/Resources/Info.plist.in +index c3e261ecd9e..26ef7a95de4 100644 +--- a/Apple/iOS/Resources/Info.plist.in +++ b/Apple/iOS/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 +@@ -17,13 +17,13 @@ + CFBundlePackageType + FMWK + CFBundleShortVersionString +- @VERSION@ + %VERSION% -+ CFBundleLongVersionString -+ %VERSION%, (c) 2001-2024 Python Software Foundation. -+ CFBundleSignature -+ ???? -+ CFBundleVersion + CFBundleLongVersionString + %VERSION%, (c) 2001-2024 Python Software Foundation. + CFBundleSignature + ???? + CFBundleVersion +- 1 + %VERSION% -+ CFBundleSupportedPlatforms -+ -+ iPhoneOS -+ -+ MinimumOSVersion -+ @IPHONEOS_DEPLOYMENT_TARGET@ -+ -+ ---- /dev/null -+++ b/Apple/iOS/Resources/bin/arm64-apple-ios-ar -@@ -0,0 +1,2 @@ -+#!/bin/sh -+xcrun --sdk iphoneos${IOS_SDK_VERSION} ar "$@" ---- /dev/null -+++ b/Apple/iOS/Resources/bin/arm64-apple-ios-clang -@@ -0,0 +1,2 @@ -+#!/bin/sh -+xcrun --sdk iphoneos${IOS_SDK_VERSION} clang -target arm64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET} "$@" ---- /dev/null -+++ b/Apple/iOS/Resources/bin/arm64-apple-ios-clang++ -@@ -0,0 +1,2 @@ -+#!/bin/sh -+xcrun --sdk iphoneos${IOS_SDK_VERSION} clang++ -target arm64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET} "$@" ---- /dev/null -+++ b/Apple/iOS/Resources/bin/arm64-apple-ios-cpp -@@ -0,0 +1,2 @@ -+#!/bin/sh -+xcrun --sdk iphoneos${IOS_SDK_VERSION} clang -target arm64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET} -E "$@" ---- /dev/null -+++ b/Apple/iOS/Resources/bin/arm64-apple-ios-simulator-ar -@@ -0,0 +1,2 @@ -+#!/bin/sh -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} ar "$@" ---- /dev/null -+++ b/Apple/iOS/Resources/bin/arm64-apple-ios-simulator-clang -@@ -0,0 +1,2 @@ -+#!/bin/sh -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target arm64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET}-simulator "$@" ---- /dev/null -+++ b/Apple/iOS/Resources/bin/arm64-apple-ios-simulator-clang++ -@@ -0,0 +1,2 @@ -+#!/bin/sh -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang++ -target arm64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET}-simulator "$@" ---- /dev/null -+++ b/Apple/iOS/Resources/bin/arm64-apple-ios-simulator-cpp -@@ -0,0 +1,2 @@ -+#!/bin/sh -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target arm64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET}-simulator -E "$@" ---- /dev/null -+++ b/Apple/iOS/Resources/bin/arm64-apple-ios-simulator-strip -@@ -0,0 +1,2 @@ -+#!/bin/sh -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} strip -arch arm64 "$@" ---- /dev/null -+++ b/Apple/iOS/Resources/bin/arm64-apple-ios-strip -@@ -0,0 +1,2 @@ -+#!/bin/sh -+xcrun --sdk iphoneos${IOS_SDK_VERSION} strip -arch arm64 "$@" ---- /dev/null -+++ b/Apple/iOS/Resources/bin/x86_64-apple-ios-simulator-ar -@@ -0,0 +1,2 @@ -+#!/bin/sh -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} ar "$@" ---- /dev/null -+++ 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/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/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" -+#endif -+ -+#ifdef __x86_64__ -+#include "pyconfig-x86_64.h" -+#endif ---- /dev/null + CFBundleSupportedPlatforms + + iPhoneOS +diff --git a/Apple/testbed/Python.xcframework/Info.plist b/Apple/testbed/Python.xcframework/Info.plist +index c6418de6e74..0587f4735f7 100644 +--- a/Apple/testbed/Python.xcframework/Info.plist +++ b/Apple/testbed/Python.xcframework/Info.plist -@@ -0,0 +1,136 @@ -+ -+ -+ -+ -+ 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 -+ +@@ -35,6 +35,98 @@ + SupportedPlatformVariant + simulator + + + BinaryPath + Python.framework/Python @@ -1643,42 +326,9 @@ + 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 -+ CFBundlePackageType -+ APPL -+ CFBundleShortVersionString -+ 1.0 -+ CFBundleSupportedPlatforms -+ -+ iPhoneOS -+ -+ MinimumOSVersion -+ 13.0 -+ CFBundleVersion -+ 1 -+ -+ + + CFBundlePackageType + XFWK --- /dev/null +++ b/Apple/testbed/Python.xcframework/build/tvOS-dylib-Info-template.plist @@ -0,0 +1,26 @@ @@ -1696,1854 +346,232 @@ + 6.0 + CFBundlePackageType + APPL -+ CFBundleShortVersionString -+ 1.0 -+ CFBundleSupportedPlatforms -+ -+ tvOS -+ -+ MinimumOSVersion -+ 9.0 -+ CFBundleVersion -+ 1 -+ -+ ---- /dev/null -+++ 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/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/Apple/testbed/Python.xcframework/build/xrOS-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/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/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/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/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 -+ -+@end -+ -+@implementation TestbedTests -+ -+ -+- (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; -+ } -+ -+ 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; -+ } -+ -+ 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); -+ -+ method_args = Py_BuildValue("(O)", app_packages_path); -+ if (method_args == NULL) { -+ XCTFail(@"Could not create arguments for site.addsitedir"); -+ return; -+ } -+ -+ result = PyObject_CallObject(site_addsitedir_attr, method_args); -+ if (result == NULL) { -+ XCTFail(@"Could not add app_packages directory using site.addsitedir"); -+ return; -+ } -+ -+ // Add test code to sys.path -+ 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; -+ } -+ -+ path = [NSString stringWithFormat:@"%@/app", resourcePath, nil]; -+ NSLog(@"App path: %@", path); -+ wtmp_str = Py_DecodeLocale([path UTF8String], NULL); -+ failed = PyList_Insert(sys_path_attr, 0, PyUnicode_FromString([path UTF8String])); -+ if (failed) { -+ XCTFail(@"Unable to add app to sys.path"); -+ return; -+ } -+ PyMem_RawFree(wtmp_str); -+ -+ // Ensure the working directory is the app folder. -+ chdir([path UTF8String]); -+ -+ // Start the test suite. Print a separator to differentiate Python startup logs from app logs -+ NSLog(@"---------------------------------------------------------------------------"); -+ -+ exit_code = Py_RunMain(); -+ XCTAssertEqual(exit_code, 0, @"Test suite did not pass"); -+ -+ NSLog(@"---------------------------------------------------------------------------"); -+ -+ Py_Finalize(); -+} -+ -+ -+@end ---- /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 -+ -+TEST_SLICES = { -+ "iOS": "ios-arm64_x86_64-simulator", -+ "tvOS": "tvos-arm64_x86_64-simulator", -+ "visionOS": "xros-arm64-simulator", -+ "watchOS": "watchos-arm64_x86_64-simulator", -+} -+ -+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 -+ -+ -+def xcode_test(location: Path, platform: str, simulator: str, verbose: bool): -+ # Build and run the test suite on the named simulator. -+ args = [ -+ "-project", -+ str(location / f"{platform}Testbed.xcodeproj"), -+ "-scheme", -+ f"{platform}Testbed", -+ "-destination", -+ f"platform={platform} Simulator,name={simulator}", -+ "-derivedDataPath", -+ str(location / "DerivedData"), -+ ] -+ verbosity_args = [] if verbose else ["-quiet"] -+ -+ print("Building test project...") -+ subprocess.run( -+ ["xcodebuild", "build-for-testing"] + args + verbosity_args, -+ check=True, -+ ) -+ -+ 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, -+ ) -+ 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. -+ -+ 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(): -+ print(f"{target} already exists; aborting without creating project.") -+ sys.exit(10) -+ -+ if framework is None: -+ if not ( -+ source / "Python.xcframework" / TEST_SLICES[platform] / "bin" -+ ).is_dir(): -+ print( -+ f"The testbed being cloned ({source}) does not contain " -+ "a framework with slices. Re-run with --framework" -+ ) -+ sys.exit(11) -+ else: -+ if not framework.is_dir(): -+ print(f"{framework} does not exist.") -+ sys.exit(12) -+ elif not ( -+ framework.suffix == ".xcframework" -+ or (framework / "Python.framework").is_dir() -+ ): -+ print( -+ f"{framework} is not an XCframework, " -+ f"or a simulator slice of a framework build." -+ ) -+ sys.exit(13) -+ -+ print("Cloning testbed project:") -+ print(f" Cloning {source}...", end="") -+ # 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" -+ test_framework_path = xc_framework_path / TEST_SLICES[platform] -+ if framework is not None: -+ if framework.suffix == ".xcframework": -+ 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="") -+ # 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: -+ 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="") -+ resolved_xc_framework_path = ( -+ source / xc_framework_path.readlink() -+ ).resolve() -+ xc_framework_path.unlink() -+ xc_framework_path.symlink_to( -+ resolved_xc_framework_path.relative_to( -+ xc_framework_path.parent, walk_up=True -+ ) -+ ) -+ print(" done") -+ elif ( -+ test_framework_path.is_symlink() -+ and not test_framework_path.readlink().is_absolute() -+ ): -+ print(" Rewriting symlink to simulator framework...", end="") -+ # Simulator framework is a relative symlink. Rewrite the symlink -+ # relative to the new location. -+ orig_test_framework_path = ( -+ source / "Python.XCframework" / test_framework_path.readlink() -+ ).resolve() -+ 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 Python framework.") -+ -+ for app_src in apps: -+ 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) -+ print(" done") -+ -+ print(f"Successfully cloned testbed: {target.resolve()}") -+ -+ -+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) -+ -+ test_plan["defaultOptions"]["commandLineArgumentEntries"] = [ -+ {"argument": arg} for arg in args -+ ] -+ -+ with test_plan_path.open("w", encoding="utf-8") as f: -+ json.dump(test_plan, f, indent=2) -+ -+ -+def run_testbed( -+ platform: str, -+ simulator: str | None, -+ args: list[str], -+ verbose: bool = False, -+): -+ location = Path(__file__).parent -+ 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 an Apple Python project through Xcode." -+ ), -+ ) -+ -+ subcommands = parser.add_subparsers(dest="subcommand") -+ clone = subcommands.add_parser( -+ "clone", -+ description=( -+ "Clone the testbed project, copying in a Python framework and" -+ "any specified application code." -+ ), -+ help="Clone a testbed project to a new location.", -+ ) -+ clone.add_argument( -+ "--framework", -+ help=( -+ "The location of the XCFramework (or simulator-only slice of an " -+ "XCFramework) to use when running the testbed" -+ ), -+ ) -+ clone.add_argument( -+ "--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", -+ default=[], -+ help="The location of any code to include in the testbed project", -+ ) -+ clone.add_argument( -+ "location", -+ help="The path where the testbed will be cloned.", -+ ) -+ -+ run = subcommands.add_parser( -+ "run", -+ usage="%(prog)s [-h] [--simulator SIMULATOR] -- [ ...]", -+ description=( -+ "Run a testbed project. The arguments provided after `--` will be " -+ "passed to the running 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", -+ 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", -+ action="store_true", -+ help="Enable verbose output", -+ ) -+ -+ try: -+ pos = sys.argv.index("--") -+ testbed_args = sys.argv[1:pos] -+ test_args = sys.argv[pos + 1 :] -+ except ValueError: -+ testbed_args = sys.argv[1:] -+ test_args = [] -+ -+ context = parser.parse_args(testbed_args) -+ -+ if context.subcommand == "clone": -+ clone_testbed( -+ source=Path(__file__).parent.resolve(), -+ target=Path(context.location).resolve(), -+ framework=Path(context.framework).resolve() -+ if context.framework -+ else None, -+ 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" -+ / TEST_SLICES[context.platform] -+ / "bin" -+ ).is_dir(): -+ print( -+ 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) -+ -+ 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() -+ parser.print_help(sys.stderr) -+ sys.exit(21) -+ else: -+ parser.print_help(sys.stderr) -+ sys.exit(1) -+ -+ -+if __name__ == "__main__": -+ main() ---- /dev/null -+++ b/Apple/testbed/iOSTestbed.xcodeproj/project.pbxproj -@@ -0,0 +1,557 @@ -+// !$*UTF8*$! -+{ -+ archiveVersion = 1; -+ classes = { -+ }; -+ objectVersion = 56; -+ objects = { -+ -+/* Begin PBXBuildFile section */ -+ 607A66172B0EFA380010BFC8 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66162B0EFA380010BFC8 /* AppDelegate.m */; }; -+ 607A66222B0EFA390010BFC8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607A66212B0EFA390010BFC8 /* Assets.xcassets */; }; -+ 607A66252B0EFA390010BFC8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607A66232B0EFA390010BFC8 /* LaunchScreen.storyboard */; }; -+ 607A66282B0EFA390010BFC8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66272B0EFA390010BFC8 /* main.m */; }; -+ 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 */; }; -+/* 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 = ( -+ 607A664D2B0EFC080010BFC8 /* Python.xcframework in Embed Frameworks */, -+ ); -+ name = "Embed Frameworks"; -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+ 607A66522B0EFFE00010BFC8 /* Embed Frameworks */ = { -+ isa = PBXCopyFilesBuildPhase; -+ buildActionMask = 2147483647; -+ dstPath = ""; -+ dstSubfolderSpec = 10; -+ files = ( -+ 607A66512B0EFFE00010BFC8 /* Python.xcframework in Embed Frameworks */, -+ ); -+ name = "Embed Frameworks"; -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXCopyFilesBuildPhase section */ -+ -+/* Begin PBXFileReference section */ -+ 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 /* 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 = ""; }; -+ 60FE0EFB2E56BB6D00524F87 /* iOSTestbed.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = iOSTestbed.xctestplan; sourceTree = ""; }; -+/* End PBXFileReference section */ -+ -+/* Begin PBXFrameworksBuildPhase section */ -+ 607A660F2B0EFA380010BFC8 /* Frameworks */ = { -+ isa = PBXFrameworksBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 607A664C2B0EFC080010BFC8 /* Python.xcframework in Frameworks */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+ 607A662A2B0EFA3A0010BFC8 /* Frameworks */ = { -+ isa = PBXFrameworksBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 607A66502B0EFFE00010BFC8 /* Python.xcframework in Frameworks */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXFrameworksBuildPhase section */ -+ -+/* Begin PBXGroup section */ -+ 607A66092B0EFA380010BFC8 = { -+ isa = PBXGroup; -+ children = ( -+ 60FE0EFB2E56BB6D00524F87 /* iOSTestbed.xctestplan */, -+ 607A664A2B0EFB310010BFC8 /* Python.xcframework */, -+ 607A66142B0EFA380010BFC8 /* iOSTestbed */, -+ 607A66302B0EFA3A0010BFC8 /* TestbedTests */, -+ 607A66132B0EFA380010BFC8 /* Products */, -+ 607A664F2B0EFFE00010BFC8 /* Frameworks */, -+ ); -+ sourceTree = ""; -+ }; -+ 607A66132B0EFA380010BFC8 /* Products */ = { -+ isa = PBXGroup; -+ children = ( -+ 607A66122B0EFA380010BFC8 /* iOSTestbed.app */, -+ 607A662D2B0EFA3A0010BFC8 /* iOSTestbedTests.xctest */, -+ ); -+ name = Products; -+ sourceTree = ""; -+ }; -+ 607A66142B0EFA380010BFC8 /* iOSTestbed */ = { -+ isa = PBXGroup; -+ children = ( -+ 608619552CB7819B00F46182 /* app */, -+ 608619532CB77BA900F46182 /* app_packages */, -+ 607A66592B0F08600010BFC8 /* iOSTestbed-Info.plist */, -+ 607A66152B0EFA380010BFC8 /* AppDelegate.h */, -+ 607A66162B0EFA380010BFC8 /* AppDelegate.m */, -+ 607A66212B0EFA390010BFC8 /* Assets.xcassets */, -+ 607A66232B0EFA390010BFC8 /* LaunchScreen.storyboard */, -+ 607A66272B0EFA390010BFC8 /* main.m */, -+ ); -+ path = iOSTestbed; -+ 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 /* iOSTestbed */ = { -+ isa = PBXNativeTarget; -+ buildConfigurationList = 607A66412B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "iOSTestbed" */; -+ buildPhases = ( -+ 607A660E2B0EFA380010BFC8 /* Sources */, -+ 607A660F2B0EFA380010BFC8 /* Frameworks */, -+ 607A66102B0EFA380010BFC8 /* Resources */, -+ 607A66552B0F061D0010BFC8 /* Process Python libraries */, -+ 607A664E2B0EFC080010BFC8 /* Embed Frameworks */, -+ ); -+ buildRules = ( -+ ); -+ dependencies = ( -+ ); -+ name = iOSTestbed; -+ productName = iOSTestbed; -+ productReference = 607A66122B0EFA380010BFC8 /* iOSTestbed.app */; -+ productType = "com.apple.product-type.application"; -+ }; -+ 607A662C2B0EFA3A0010BFC8 /* iOSTestbedTests */ = { -+ isa = PBXNativeTarget; -+ buildConfigurationList = 607A66442B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "iOSTestbedTests" */; -+ buildPhases = ( -+ 607A66292B0EFA3A0010BFC8 /* Sources */, -+ 607A662A2B0EFA3A0010BFC8 /* Frameworks */, -+ 607A662B2B0EFA3A0010BFC8 /* Resources */, -+ 607A66522B0EFFE00010BFC8 /* Embed Frameworks */, -+ ); -+ buildRules = ( -+ ); -+ dependencies = ( -+ 607A662F2B0EFA3A0010BFC8 /* PBXTargetDependency */, -+ ); -+ name = iOSTestbedTests; -+ productName = iOSTestbedTests; -+ productReference = 607A662D2B0EFA3A0010BFC8 /* iOSTestbedTests.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 "iOSTestbed" */; -+ compatibilityVersion = "Xcode 14.0"; -+ developmentRegion = en; -+ hasScannedForEncodings = 0; -+ knownRegions = ( -+ en, -+ Base, -+ ); -+ mainGroup = 607A66092B0EFA380010BFC8; -+ productRefGroup = 607A66132B0EFA380010BFC8 /* Products */; -+ projectDirPath = ""; -+ projectRoot = ""; -+ targets = ( -+ 607A66112B0EFA380010BFC8 /* iOSTestbed */, -+ 607A662C2B0EFA3A0010BFC8 /* iOSTestbedTests */, -+ ); -+ }; -+/* End PBXProject section */ -+ -+/* Begin PBXResourcesBuildPhase section */ -+ 607A66102B0EFA380010BFC8 /* Resources */ = { -+ isa = PBXResourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 607A66252B0EFA390010BFC8 /* LaunchScreen.storyboard in Resources */, -+ 608619562CB7819B00F46182 /* app in Resources */, -+ 607A66222B0EFA390010BFC8 /* Assets.xcassets in Resources */, -+ 608619542CB77BA900F46182 /* app_packages in Resources */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+ 607A662B2B0EFA3A0010BFC8 /* Resources */ = { -+ isa = PBXResourcesBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXResourcesBuildPhase section */ -+ -+/* Begin PBXShellScriptBuildPhase section */ -+ 607A66552B0F061D0010BFC8 /* 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\nsource $PROJECT_DIR/Python.xcframework/build/utils.sh\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 /* 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; -+ 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 = 13.0; -+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES; -+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; -+ MTL_FAST_MATH = YES; -+ ONLY_ACTIVE_ARCH = YES; -+ SDKROOT = iphoneos; -+ }; -+ 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 = 13.0; -+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES; -+ MTL_ENABLE_DEBUG_INFO = NO; -+ MTL_FAST_MATH = YES; -+ SDKROOT = iphoneos; -+ 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 = "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.iOSTestbed; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ SWIFT_EMIT_LOC_STRINGS = YES; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ }; -+ 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 = "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.iOSTestbed; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ SWIFT_EMIT_LOC_STRINGS = YES; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ }; -+ 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 = 13.0; -+ MARKETING_VERSION = 1.0; -+ PRODUCT_BUNDLE_IDENTIFIER = org.python.iOSTestbedTests; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ SWIFT_EMIT_LOC_STRINGS = NO; -+ TARGETED_DEVICE_FAMILY = "1,2"; -+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/iOSTestbed.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/iOSTestbed"; -+ }; -+ 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 = 13.0; -+ MARKETING_VERSION = 1.0; -+ PRODUCT_BUNDLE_IDENTIFIER = org.python.iOSTestbedTests; -+ PRODUCT_NAME = "$(TARGET_NAME)"; -+ SWIFT_EMIT_LOC_STRINGS = NO; -+ 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 "iOSTestbed" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 607A663F2B0EFA3A0010BFC8 /* Debug */, -+ 607A66402B0EFA3A0010BFC8 /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+ 607A66412B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "iOSTestbed" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 607A66422B0EFA3A0010BFC8 /* Debug */, -+ 607A66432B0EFA3A0010BFC8 /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+ 607A66442B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "iOSTestbedTests" */ = { -+ isa = XCConfigurationList; -+ buildConfigurations = ( -+ 607A66452B0EFA3A0010BFC8 /* Debug */, -+ 607A66462B0EFA3A0010BFC8 /* Release */, -+ ); -+ defaultConfigurationIsVisible = 0; -+ defaultConfigurationName = Release; -+ }; -+/* End XCConfigurationList section */ -+ }; -+ rootObject = 607A660A2B0EFA380010BFC8 /* Project object */; -+} ---- /dev/null -+++ 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 -+// iOSTestbed -+// -+ -+#import -+ -+@interface AppDelegate : UIResponder -+ -+ -+@end ---- /dev/null -+++ b/Apple/testbed/iOSTestbed/AppDelegate.m -@@ -0,0 +1,19 @@ -+// -+// AppDelegate.m -+// iOSTestbed -+// -+ -+#import "AppDelegate.h" -+ -+@interface AppDelegate () -+ -+@end -+ -+@implementation AppDelegate -+ -+ -+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { -+ return YES; -+} -+ -+@end ---- /dev/null -+++ b/Apple/testbed/iOSTestbed/Assets.xcassets/AccentColor.colorset/Contents.json -@@ -0,0 +1,11 @@ -+{ -+ "colors" : [ -+ { -+ "idiom" : "universal" -+ } -+ ], -+ "info" : { -+ "author" : "xcode", -+ "version" : 1 -+ } -+} ---- /dev/null -+++ b/Apple/testbed/iOSTestbed/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/iOSTestbed/Assets.xcassets/Contents.json -@@ -0,0 +1,6 @@ -+{ -+ "info" : { -+ "author" : "xcode", -+ "version" : 1 -+ } -+} ---- /dev/null -+++ 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 -+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/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 -+Framework form. -+ -+When the test suite runs, this folder will be on the PYTHONPATH. ++ CFBundleShortVersionString ++ 1.0 ++ CFBundleSupportedPlatforms ++ ++ tvOS ++ ++ MinimumOSVersion ++ 9.0 ++ CFBundleVersion ++ 1 ++ ++ +diff --git a/Apple/testbed/Python.xcframework/build/utils.sh b/Apple/testbed/Python.xcframework/build/utils.sh +index 961c46d014b..76172162487 100755 +--- a/Apple/testbed/Python.xcframework/build/utils.sh ++++ b/Apple/testbed/Python.xcframework/build/utils.sh +@@ -34,9 +34,38 @@ + else + SLICE_FOLDER="ios-arm64_x86_64-simulator" + fi +- else ++ 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. --- /dev/null -+++ b/Apple/testbed/iOSTestbed/iOSTestbed-Info.plist -@@ -0,0 +1,52 @@ ++++ b/Apple/testbed/Python.xcframework/build/watchOS-dylib-Info-template.plist +@@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en -+ CFBundleDisplayName -+ ${PRODUCT_NAME} + CFBundleExecutable -+ ${EXECUTABLE_NAME} ++ + CFBundleIdentifier -+ org.python.iOSTestbed ++ + CFBundleInfoDictionaryVersion + 6.0 -+ CFBundleName -+ ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 -+ CFBundleSignature -+ ???? ++ CFBundleSupportedPlatforms ++ ++ watchOS ++ ++ MinimumOSVersion ++ 4.0 + CFBundleVersion + 1 -+ LSRequiresIPhoneOS -+ -+ UIRequiresFullScreen -+ -+ UILaunchStoryboardName -+ Launch Screen -+ UISupportedInterfaceOrientations ++ ++ +--- /dev/null ++++ b/Apple/testbed/Python.xcframework/build/xrOS-dylib-Info-template.plist +@@ -0,0 +1,30 @@ ++ ++ ++ ++ ++ CFBundleDevelopmentRegion ++ en ++ CFBundleInfoDictionaryVersion ++ 6.0 ++ CFBundleExecutable ++ ++ CFBundleIdentifier ++ ++ CFBundlePackageType ++ FMWK ++ CFBundleShortVersionString ++ 1.0 ++ CFBundleSupportedPlatforms + -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight ++ XROS + -+ UISupportedInterfaceOrientations~ipad ++ CFBundleVersion ++ 1 ++ MinimumOSVersion ++ 2.0 ++ UIDeviceFamily + -+ UIInterfaceOrientationPortrait -+ UIInterfaceOrientationPortraitUpsideDown -+ UIInterfaceOrientationLandscapeLeft -+ UIInterfaceOrientationLandscapeRight ++ 7 + -+ UIApplicationSceneManifest -+ -+ UIApplicationSupportsMultipleScenes -+ -+ UISceneConfigurations -+ -+ + + --- /dev/null -+++ b/Apple/testbed/iOSTestbed/main.m -@@ -0,0 +1,16 @@ -+// -+// main.m -+// iOSTestbed -+// ++++ b/Apple/testbed/Python.xcframework/tvos-arm64/README +@@ -0,0 +1,4 @@ ++This directory is intentionally empty. + -+#import -+#import "AppDelegate.h" ++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. + -+int main(int argc, char * argv[]) { -+ NSString * appDelegateClassName; -+ @autoreleasepool { -+ appDelegateClassName = NSStringFromClass([AppDelegate class]); ++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. + -+ return UIApplicationMain(argc, argv, nil, appDelegateClassName); -+ } -+} ++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/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/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. +diff --git a/Apple/testbed/__main__.py b/Apple/testbed/__main__.py +index 4a1333380cd..41877db5b4f 100644 +--- a/Apple/testbed/__main__.py ++++ b/Apple/testbed/__main__.py +@@ -8,6 +8,9 @@ + + TEST_SLICES = { + "iOS": "ios-arm64_x86_64-simulator", ++ "tvOS": "tvos-arm64_x86_64-simulator", ++ "visionOS": "xros-arm64-simulator", ++ "watchOS": "watchos-arm64_x86_64-simulator", + } + + DECODE_ARGS = ("UTF-8", "backslashreplace") +@@ -19,7 +22,7 @@ + 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+iOSTestbed\[\d+:\w+\]" # Process/thread ID ++ r"\s+.*Testbed\[\d+:\w+\]" # Process/thread ID + ) + + +@@ -52,6 +55,24 @@ + ) + ) + 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}") + +@@ -279,7 +300,7 @@ + # many platforms, but when cloned, only one platform is preserved. + available_platforms = [ + platform +- for platform in ["iOS"] ++ for platform in ["iOS", "tvOS", "visionOS", "watchOS"] + if (Path(__file__).parent / f"{platform}Testbed").is_dir() + ] + +@@ -329,7 +350,7 @@ + usage="%(prog)s [-h] [--simulator SIMULATOR] -- [ ...]", + description=( + "Run a testbed project. The arguments provided after `--` will be " +- "passed to the running iOS 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", --- /dev/null +++ b/Apple/testbed/tvOSTestbed.xcodeproj/project.pbxproj @@ -0,0 +1,505 @@ @@ -5806,262 +2834,6 @@ +#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 @@ -6124,10 +2896,10 @@ index 8bcd741c446..db11cbe0945 100644 suffix.replace(".so", ".fwork") for suffix in _imp.extension_suffixes() diff --git a/Lib/platform.py b/Lib/platform.py -index 86141f072d2..5c77e30d8b5 100644 +index 784b6b749b7..4d3be1aca18 100644 --- a/Lib/platform.py +++ b/Lib/platform.py -@@ -534,6 +534,78 @@ +@@ -539,6 +539,78 @@ return IOSVersionInfo(system, release, model, is_simulator) @@ -6206,7 +2978,7 @@ index 86141f072d2..5c77e30d8b5 100644 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 @@ +@@ -738,7 +810,7 @@ default in case the command should fail. """ @@ -6215,7 +2987,7 @@ index 86141f072d2..5c77e30d8b5 100644 # XXX Others too ? return default -@@ -897,14 +969,30 @@ +@@ -902,14 +974,30 @@ csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0) return 'Alpha' if cpu_number >= 128 else 'VAX' @@ -6249,7 +3021,7 @@ index 86141f072d2..5c77e30d8b5 100644 def from_subprocess(): """ Fall back to `uname -p` -@@ -1064,9 +1152,15 @@ +@@ -1069,9 +1157,15 @@ system = 'Android' release = android_ver().release @@ -6266,7 +3038,7 @@ index 86141f072d2..5c77e30d8b5 100644 vals = system, node, release, version, machine # Replace 'unknown' values with the more portable '' -@@ -1356,6 +1450,12 @@ +@@ -1361,6 +1455,12 @@ # macOS and iOS both report as a "Darwin" kernel if sys.platform == "ios": system, release, _, _ = ios_ver() @@ -6308,7 +3080,7 @@ index 54c2eb515b6..e02063aefea 100644 if _mswindows: import _winapi diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py -index f93b98dd681..07ec7853f85 100644 +index 2ecbff222fe..542d3f21cae 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -23,6 +23,9 @@ @@ -6330,7 +3102,7 @@ index f93b98dd681..07ec7853f85 100644 return None def joinuser(*args): -@@ -730,6 +733,18 @@ +@@ -734,6 +737,18 @@ release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0") osname = sys.platform machine = sys.implementation._multiarch @@ -6366,10 +3138,10 @@ index 1c1cbd03d02..1378985de4a 100644 else: extension_loader = "ExtensionFileLoader" diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py -index a719e49ef37..c0cacd2a8d5 100644 +index 88f61103512..4b44e03319c 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py -@@ -563,7 +563,7 @@ +@@ -573,7 +573,7 @@ sys.platform == "android", f"Android blocks {name} with SELinux" ) @@ -6378,7 +3150,7 @@ index a719e49ef37..c0cacd2a8d5 100644 unix_shell = '/system/bin/sh' if is_android else '/bin/sh' else: unix_shell = None -@@ -582,7 +582,7 @@ +@@ -592,7 +592,7 @@ def skip_wasi_stack_overflow(): return unittest.skipIf(is_wasi, "Exhausts stack on WASI") @@ -6413,7 +3185,7 @@ index 15603dc3d77..bff6c0fb95f 100644 if WINDOWS: KNOWN_LIBRARIES = ["KERNEL32.DLL"] diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py -index 6224c989e65..c5ccf225662 100644 +index 187a3d54809..3c6195bea69 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -271,13 +271,21 @@ @@ -6483,7 +3255,7 @@ index f2e2394089d..2efbbfb0014 100644 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 +index ba039794c88..19a332ffdcb 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -209,6 +209,12 @@ @@ -6499,39 +3271,6 @@ index 764eef5be3e..19a332ffdcb 100644 # 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 @@ -6592,7 +3331,7 @@ index 1bb6a05dc11..49febd56a37 100755 none--*) # None (no kernel, i.e. freestanding / bare metal), diff --git a/configure b/configure -index 6383271b477..b177687b0bc 100755 +index d31c24dffa2..b177687b0bc 100755 --- a/configure +++ b/configure @@ -982,6 +982,10 @@ @@ -6708,7 +3447,7 @@ index 6383271b477..b177687b0bc 100755 yes) case $ac_sys_system in - Darwin) enableval=/Library/Frameworks ;; -- iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;; +- iOS) enableval=Apple/iOS/Frameworks/\$\(MULTIARCH\) ;; + Darwin) enableval=/Library/Frameworks ;; + iOS) enableval=Apple/iOS/Frameworks/\$\(MULTIARCH\) ;; + tvOS) enableval=Apple/tvOS/Frameworks/\$\(MULTIARCH\) ;; @@ -6727,15 +3466,10 @@ index 6383271b477..b177687b0bc 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4470,9 +4533,54 @@ +@@ -4474,6 +4537,51 @@ + + ac_config_files="$ac_config_files Apple/iOS/Resources/Info.plist" - 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" @@ -6778,12 +3512,12 @@ index 6383271b477..b177687b0bc 100755 + 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" - ++ ;; *) + as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 @@ -4485,6 +4593,9 @@ e) case $ac_sys_system in @@ -7284,12 +4018,10 @@ index 6383271b477..b177687b0bc 100755 -@@ -35303,7 +35539,10 @@ - "Mac/PythonLauncher/Makefile") CONFIG_FILES="$CONFIG_FILES Mac/PythonLauncher/Makefile" ;; +@@ -35304,6 +35540,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" ;; -+ "Apple/iOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES Apple/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" ;; @@ -7297,7 +4029,7 @@ index 6383271b477..b177687b0bc 100755 "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 +index af7a9623d7b..4cf79576a55 100644 --- a/configure.ac +++ b/configure.ac @@ -330,6 +330,15 @@ @@ -7402,7 +4134,7 @@ index 42d94776cc7..4cf79576a55 100644 yes) case $ac_sys_system in - Darwin) enableval=/Library/Frameworks ;; -- iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;; +- iOS) enableval=Apple/iOS/Frameworks/\$\(MULTIARCH\) ;; + Darwin) enableval=/Library/Frameworks ;; + iOS) enableval=Apple/iOS/Frameworks/\$\(MULTIARCH\) ;; + tvOS) enableval=Apple/tvOS/Frameworks/\$\(MULTIARCH\) ;; @@ -7421,16 +4153,10 @@ index 42d94776cc7..4cf79576a55 100644 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -666,9 +725,51 @@ +@@ -670,6 +729,48 @@ - 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]) -+ ;; + AC_CONFIG_FILES([Apple/iOS/Resources/Info.plist]) + ;; + tvOS) : + FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" + FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " @@ -7472,9 +4198,10 @@ index 42d94776cc7..4cf79576a55 100644 + 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 From ab005855a30bed4caffcff9c884f63f0ccee9dc3 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 8 Oct 2025 10:43:26 +0800 Subject: [PATCH 113/113] Bump OpenSSL to 3.0.18. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d0f36faa..88a32cc7 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ PYTHON_VER=$(basename $(PYTHON_VERSION)) BZIP2_VERSION=1.0.8-2 LIBFFI_VERSION=3.4.7-2 MPDECIMAL_VERSION=4.0.0-2 -OPENSSL_VERSION=3.0.17-1 +OPENSSL_VERSION=3.0.18-1 XZ_VERSION=5.6.4-2 ZSTD_VERSION=1.5.7-1