diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..a9b1b6f --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,83 @@ +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: + fail-fast: false + matrix: + 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 + + 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 + + - name: Build ${{ matrix.target }} + run: | + # Do the build for the requested target. + make ${{ matrix.target }} + + - name: Upload build artefacts + uses: actions/upload-artifact@v4.3.4 + 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.1.7 + 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 + 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 + + - 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\' diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index a4231bc..0cde561 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 c618d40..b7495ab 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -53,11 +53,11 @@ 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) + 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}" | tee -a ${GITHUB_OUTPUT} echo "BZIP2_VERSION=${BZIP2_VERSION}" | tee -a ${GITHUB_OUTPUT} diff --git a/Makefile b/Makefile index 76689c3..5d1c4b7 100644 --- a/Makefile +++ b/Makefile @@ -15,19 +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: -# 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 -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,24 +35,18 @@ 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) +VERSION_MIN-iOS=13.0 # 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 +VERSION_MIN-tvOS=12.0 # 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 HOST_ARCH=$(shell uname -m) @@ -69,7 +61,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))) @@ -89,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 @@ -104,6 +96,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-*)) ########################################################################### @@ -123,31 +121,17 @@ 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") - -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 @@ -189,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 ########################################################################### @@ -247,66 +251,67 @@ 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 \ $$(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)) 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" \ 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)) \ --build=$(HOST_ARCH)-apple-darwin \ --with-build-python=$(HOST_PYTHON) \ - --prefix="$$(PYTHON_INSTALL-$(target))" \ --enable-ipv6 \ --with-openssl="$$(OPENSSL_INSTALL-$(target))" \ - --without-ensurepip \ - ac_cv_file__dev_ptmx=no \ - ac_cv_file__dev_ptc=no \ - $$(PYTHON_CONFIGURE-$(os)) \ + --enable-framework="$$(PYTHON_INSTALL-$(target))" \ + --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)/support/$(PYTHON_VER)/$(os)/bin:$(PATH)" \ - make all \ + PATH="$(PROJECT_DIR)/$$(PYTHON_SRCDIR-$(target))/$(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)/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 @@ -320,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)) @@ -335,19 +340,23 @@ 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))" @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))" @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 @@ -376,172 +385,38 @@ 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)))) -########################################################################### -# 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 +ifeq ($(os),macOS) +# macOS builds are extracted from the official installer package, then +# reprocessed into an XCFramework. -$$(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_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" @@ -549,21 +424,39 @@ $$(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)) + + # 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 - 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_INCLUDE-$(sdk))/pyconfig.h + 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_INSTALL-$$(target))/include/python$(PYTHON_VER)/pyconfig.h $$(PYTHON_INCLUDE-$(sdk))/pyconfig-$$(ARCH-$$(target)).h; ) + $$(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 -$$(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 \ @@ -572,13 +465,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 @@ -593,13 +483,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 @@ -621,9 +512,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)))) @@ -632,60 +520,100 @@ $$(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" + + # 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 $$(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 + 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 $$(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 $$(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 + + # Create XCFramework out of the extracted 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" + 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) + $$(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)); ) + @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 _sysconfigdata modules from every SDK in $(os) into the support folder. - $$(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_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 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 "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: \ $$(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 clean-$(os): @echo ">>> Clean Python build products on $(os)" @@ -695,6 +623,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): @@ -733,6 +662,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)) diff --git a/README.rst b/README.rst index dff2a8a..88b52ab 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. -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, with the IDLE, Tkinter and turtle packages removed. -* ``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: - -* ``curses`` -* ``grp`` -* ``multiprocessing`` -* ``posixshmem`` -* ``posixsubprocess`` -* ``syslog`` +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) @@ -93,10 +82,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 +92,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>`__ @@ -118,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 ------------------ @@ -142,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 8caa4ac..12bc1dc 100644 --- a/USAGE.md +++ b/USAGE.md @@ -9,158 +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 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. - -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". - -5. Select "General" -> "Build Phases", and ensure that the `python-stdlib` folder - is listed in the "Copy Bundle Resources" step. - -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: - -```bash -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 "{}" \; -else - echo "Purging Python modules for iOS Simulator" - find "$CODESIGNING_FOLDER_PATH" -name "*.*-iphonesimulator.dylib" -exec rm -f "{}" \; -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 (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 "/" "_"); - # A bundle identifier; not actually used, but required by Xcode framework packaging - FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FRAMEWORK_NAME | tr "_" "-") - # The name of the framework folder. - FRAMEWORK_FOLDER="Frameworks/$FRAMEWORK_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" -} - -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" -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 "{}" \; -``` - - 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 7267418..aca98f6 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1,1711 +1,403 @@ -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,36 @@ -+from ctypes import cdll, c_void_p, c_char_p -+from ctypes import util -+ -+ -+def get_platform_ios(): -+ 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 -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..0356d2ab5d 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,59 @@ - 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 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. -+ """ -+ def __init__(self, fullname, dylib_file, path): -+ super().__init__(fullname, dylib_file) -+ self.parent_paths = path +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:: + -+ 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 ++ 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 + -+class AppleFrameworkFinder: -+ """A finder for modules that have been packaged as Apple Frameworks -+ for compatibility with Apple's App Store policies. ++ 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. + -+ 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) -+ if _path_isfile(dylib_file): -+ 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 @@ - 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)) ++ .. 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 7bb222088d..0a5ed0361e 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/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py -index b25c097573..7f5f26248f 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 -@@ -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( -diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py -index 9c92e75886..013a414729 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 - - - 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 - - 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): - - def setUp(self): -diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py -index 203dd6fe57..8e0999ecd7 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, verbose - from test.support.import_helper import import_module - from test.support.os_helper import TESTFN, unlink - -@@ -57,7 +57,7 @@ - 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' -diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py -index 9fa6ecf9c0..53eccef97f 100644 ---- a/Lib/test/test_httpservers.py -+++ b/Lib/test/test_httpservers.py -@@ -30,6 +30,7 @@ - - import unittest - from test import support -+from test.support import is_apple_mobile - from test.support import os_helper - from test.support import threading_helper - -@@ -422,7 +423,7 @@ - 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': -+ 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. - for name in os.listdir(self.tempdir): -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..9a9cacd6a5 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 -@@ -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 -+++ 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 ++ 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: -- 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_platform.py b/Lib/test/test_platform.py -index 2169733503..753a137d66 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): -diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py -index 9d72dba159..f12e9bb0cb 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) - -diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py -index d231e66b7b..748839cdfb 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.') - 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") -+ @unittest.skipUnless(support.has_subprocess_support, 'Test requires support for subprocesses.') - @unittest.skipUnless(hasattr(os, 'get_terminal_size'), - 'need os.get_terminal_size()') - def test_stty_match(self): -diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py -index 86701caf05..5f8459beed 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. -@@ -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 ---- 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 -@@ -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_sysconfig.py b/Lib/test/test_sysconfig.py -index 2a6813f00b..c1a7d6eb08 100644 ---- a/Lib/test/test_sysconfig.py -+++ b/Lib/test/test_sysconfig.py -@@ -338,7 +338,7 @@ - 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'] - if HAS_USER_BASE: - wanted.extend(['nt_user', 'osx_framework_user', 'posix_user']) - self.assertEqual(get_scheme_names(), tuple(sorted(wanted))) -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_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 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 \ 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 -+# if __x86_64__ -+PLATFORM_TRIPLET=iphonesimulator-x86_64 -+# else -+PLATFORM_TRIPLET=iphonesimulator-arm64 -+# endif -+# else -+PLATFORM_TRIPLET=iphoneos-arm64 -+# endif -+# elif TARGET_OS_TV -+# 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=appletvsimulator-x86_64 ++PLATFORM_TRIPLET=x86_64-appletvsimulator +# else -+PLATFORM_TRIPLET=appletvsimulator-arm64 ++PLATFORM_TRIPLET=arm64-appletvsimulator +# endif +# else -+PLATFORM_TRIPLET=appletvos-arm64 ++PLATFORM_TRIPLET=arm64-appletvos +# endif -+# elif TARGET_OS_WATCH -+# 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=watchsimulator-x86_64 ++PLATFORM_TRIPLET=x86_64-watchsimulator +# else -+PLATFORM_TRIPLET=watchsimulator-arm64 ++PLATFORM_TRIPLET=arm64-watchsimulator +# endif +# else -+PLATFORM_TRIPLET=watchos-arm64_32 ++PLATFORM_TRIPLET=arm64_32-watchos +# 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/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..95c1b3633c 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,26 @@ - # 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. -+#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 @@ - */ - #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 +1588,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 +1642,7 @@ - Py_DECREF(k); - Py_DECREF(v); - } -+#endif /* !TARGET_OS_TV && !TARGET_OS_WATCH */ - return d; - } - -@@ -5750,6 +5776,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 +5788,7 @@ - Py_BEGIN_ALLOW_THREADS - result = system(bytes); - Py_END_ALLOW_THREADS -+#endif /* TARGET_OS_IPHONE */ - return result; - } - #endif -@@ -15000,6 +15030,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 +15091,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..59b48c0ea4 100644 ---- a/Modules/timemodule.c -+++ b/Modules/timemodule.c -@@ -113,6 +113,11 @@ - } - - -+#ifdef __APPLE__ -+# include "TargetConditionals.h" -+#endif /* __APPLE__ */ -+ -+ - /* 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 @@ - 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); -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..69685cd25a 100755 +index bff4f6ceb4a..1ce51fcffeb 100755 --- a/configure +++ b/configure -@@ -4247,6 +4247,15 @@ - *-*-cygwin*) - ac_sys_system=Cygwin +@@ -978,10 +978,13 @@ + CFLAGS + CC + HAS_XCRUN ++WATCHOS_DEPLOYMENT_TARGET ++TVOS_DEPLOYMENT_TARGET + 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 @@ + *-apple-ios*) + ac_sys_system=iOS ;; -+ *-apple-ios*) -+ ac_sys_system=iOS -+ ;; + *-apple-tvos*) + ac_sys_system=tvOS + ;; @@ -1715,116 +407,256 @@ index c87f518382..69685cd25a 100755 *-*-vxworks*) ac_sys_system=VxWorks ;; -@@ -4303,27 +4312,96 @@ - *-*-linux*) - case "$host_cpu" in - arm*) -- _host_cpu=arm -+ _host_ident=arm - ;; +@@ -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 ;; *) -- _host_cpu=$host_cpu -+ _host_ident=$host_cpu + 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 - ;; - *-*-cygwin*) -- _host_cpu= -+ _host_ident= -+ ;; -+ *-apple-ios*-simulator) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${_host_os_min_version:3}-iphonesimulator-arm64 -+ ;; -+ *) -+ _host_ident=${_host_os_min_version:3}-iphonesimulator-$host_cpu -+ esac -+ ;; -+ *-apple-ios*) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${_host_os_min_version:3}-iphoneos-arm64 -+ ;; -+ *) -+ _host_ident=${_host_os_min_version:3}-iphoneos-$host_cpu -+ esac -+ ;; -+ *-apple-tvos*-simulator) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${_host_os_min_version:3}-appletvsimulator-arm64 -+ ;; -+ *) -+ _host_ident=${_host_os_min_version:3}-appletvsimulator-$host_cpu -+ esac -+ ;; -+ *-apple-tvos*) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${_host_os_min_version:3}-appletvos-arm64 -+ ;; -+ *) -+ _host_ident=${_host_os_min_version:3}-appletvos-$host_cpu + 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 -+ ;; -+ *-apple-watchos*-simulator) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${_host_os_min_version:3}-watchsimualtor-arm64 -+ ;; -+ *) -+ _host_ident=${_host_os_min_version:3}-watchsimualtor-$host_cpu ++ ++else $as_nop ++ ++ 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; } ++ ;; ++ *) ++ # 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 -+ ;; -+ *-apple-watchos*) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` ++ ++fi ++ ++ ++ + + if test "$cross_compiling" = yes; then + case "$host" in +@@ -4468,6 +4597,50 @@ + ;; + 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=${_host_os_min_version:3}-watchosos-arm64_32 -+ ;; -+ *) -+ _host_ident=${_host_os_min_version:3}-watchosos-$host_cpu -+ esac -+ ;; -+ *-apple-*) ++ aarch64) ++ _host_ident=${TVOS_DEPLOYMENT_TARGET}-arm64-appletv${_host_device} ++ ;; ++ *) ++ _host_ident=${TVOS_DEPLOYMENT_TARGET}-$host_cpu-appletv${_host_device} ++ ;; ++ esac ++ ;; ++ *-apple-watchos*) ++ _host_os=`echo $host | cut -d '-' -f3` ++ _host_device=`echo $host | cut -d '-' -f4` ++ _host_device=${_host_device:=os} ++ ++ # WATCHOS_DEPLOYMENT_TARGET is the minimum supported watchOS version ++ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking watchOS deployment target" >&5 ++printf %s "checking watchOS deployment target... " >&6; } ++ WATCHOS_DEPLOYMENT_TARGET=${_host_os:7} ++ WATCHOS_DEPLOYMENT_TARGET=${WATCHOS_DEPLOYMENT_TARGET:=4.0} ++ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $WATCHOS_DEPLOYMENT_TARGET" >&5 ++printf "%s\n" "$WATCHOS_DEPLOYMENT_TARGET" >&6; } ++ + case "$host_cpu" in -+ 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 - ;; - wasm32-*-* | wasm64-*-*) -- _host_cpu=$host_cpu -+ _host_ident=$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 +4468,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/*) @@ -1832,12 +664,39 @@ 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 @@ - case $ac_sys_system in #( - Darwin*) : +@@ -4611,7 +4788,10 @@ + CONFIGURE_MACOSX_DEPLOYMENT_TARGET= + EXPORT_MACOSX_DEPLOYMENT_TARGET='#' + +-# Record the value of IPHONEOS_DEPLOYMENT_TARGET enforced by the selected host triple. ++# Record the value of IPHONEOS_DEPLOYMENT_TARGET / TVOS_DEPLOYMENT_TARGET / ++# WATCHOS_DEPLOYMENT_TARGET enforced by the selected host triple. ++ ++ + + + # checks for alternative programs +@@ -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) : ++ ++ 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 +@@ -6953,6 +7143,10 @@ + MULTIARCH="" ;; #( + iOS) : MULTIARCH="" ;; #( -+ iOS) : -+ MULTIARCH="" ;; #( + tvOS) : + MULTIARCH="" ;; #( + watchOS) : @@ -1845,141 +704,317 @@ index c87f518382..69685cd25a 100755 FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -6753,9 +6844,6 @@ - ;; - esac +@@ -6973,7 +7167,7 @@ + printf "%s\n" "$MULTIARCH" >&6; } --{ 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 @@ - 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,8 +6905,14 @@ + 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 -+ PY_SUPPORT_TIER=0 - ;; - esac - -@@ -12515,6 +12619,7 @@ - esac - ;; - CYGWIN*) SHLIB_SUFFIX=.dll;; -+ iOS|tvOS|watchOS) SHLIB_SUFFIX=.dylib;; - *) SHLIB_SUFFIX=.so;; - esac - fi -@@ -12597,6 +12702,9 @@ ++ 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' + ;; + AIX*) +@@ -12895,7 +13097,7 @@ BLDSHARED="$LDSHARED" fi ;; +- iOS/*) + iOS/*|tvOS/*|watchOS/*) -+ LDSHARED='$(CC) -dynamiclib -undefined dynamic_lookup' -+ LDCXXSHARED='$(CXX) -dynamiclib -undefined dynamic_lookup';; - Emscripten|WASI) - LDSHARED='$(CC) -shared' - LDCXXSHARED='$(CXX) -shared';; -@@ -14138,6 +14246,10 @@ + 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 + ;; +@@ -14451,7 +14653,7 @@ ctypes_malloc_closure=yes ;; #( +- iOS) : + iOS|tvOS|watchOS) : -+ -+ ctypes_malloc_closure=yes -+ ;; #( - sunos5) : - as_fn_append LIBFFI_LIBS " -mimpure-text" - ;; #( -@@ -23651,7 +23763,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; } -@@ -23660,7 +23772,7 @@ - if test "$Py_DEBUG" = 'true'; then - # Similar to SOABI but remove "d" flag from ABIFLAGS + 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 @@ -- 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} + fi - printf "%s\n" "#define ALT_SOABI \"${ALT_SOABI}\"" >>confdefs.h +-# 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 @@ -@@ -27949,6 +28061,28 @@ - ;; #( - Darwin) : - ;; #( -+ iOS|tvOS|watchOS) : + 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 + -+ 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 ++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 + -+ ;; #( - CYGWIN*) : ++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} +@@ -21808,7 +22015,8 @@ -@@ -32186,4 +32320,3 @@ - CPython core team, see https://peps.python.org/pep-0011/ for more information. - " >&2;} - fi + # check for openpty, login_tty, and forkpty - ++# tvOS/watchOS have functions for tty, but can't use them ++if test "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" ; then + + for ac_func in openpty + do : +@@ -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 @@ + + done + +-# On Android and iOS, clock_settime can be linked (so it is found by ++# On Android, iOS, tvOS and watchOS, clock_settime can be linked (so it is found by + # configure), but when used in an unprivileged process, it crashes rather than + # returning an error. Force the symbol off. +-if test "$ac_sys_system" != "Linux-android" && test "$ac_sys_system" != "iOS" ++if test "$ac_sys_system" != "Linux-android" -a "$ac_sys_system" != "iOS" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" + then + + for ac_func in clock_settime +@@ -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) : + + + +@@ -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" ;; + "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 65975732f72..940136f3311 100644 --- a/configure.ac +++ b/configure.ac -@@ -553,6 +553,15 @@ - *-*-cygwin*) - ac_sys_system=Cygwin +@@ -330,6 +330,12 @@ + *-apple-ios*) + ac_sys_system=iOS ;; -+ *-apple-ios*) -+ ac_sys_system=iOS -+ ;; + *-apple-tvos*) + ac_sys_system=tvOS + ;; @@ -1989,116 +1024,244 @@ index cd69f0ede5..135036bf67 100644 *-*-vxworks*) ac_sys_system=VxWorks ;; -@@ -607,27 +616,96 @@ - *-*-linux*) - case "$host_cpu" in - arm*) -- _host_cpu=arm -+ _host_ident=arm - ;; +@@ -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 ;; *) -- _host_cpu=$host_cpu -+ _host_ident=$host_cpu + 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 - ;; - *-*-cygwin*) -- _host_cpu= -+ _host_ident= -+ ;; -+ *-apple-ios*-simulator) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${_host_os_min_version:3}-iphonesimulator-arm64 -+ ;; -+ *) -+ _host_ident=${_host_os_min_version:3}-iphonesimulator-$host_cpu -+ esac -+ ;; -+ *-apple-ios*) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${_host_os_min_version:3}-iphoneos-arm64 -+ ;; -+ *) -+ _host_ident=${_host_os_min_version:3}-iphoneos-$host_cpu -+ esac -+ ;; -+ *-apple-tvos*-simulator) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${_host_os_min_version:3}-appletvsimulator-arm64 -+ ;; -+ *) -+ _host_ident=${_host_os_min_version:3}-appletvsimulator-$host_cpu + 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 @@ + ],[ + 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]) + ++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 -+ ;; -+ *-apple-tvos*) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${_host_os_min_version:3}-appletvos-arm64 -+ ;; -+ *) -+ _host_ident=${_host_os_min_version:3}-appletvos-$host_cpu ++ ],[ ++ 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 -+ ;; -+ *-apple-watchos*-simulator) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${_host_os_min_version:3}-watchsimualtor-arm64 -+ ;; -+ *) -+ _host_ident=${_host_os_min_version:3}-watchsimualtor-$host_cpu -+ esac -+ ;; -+ *-apple-watchos*) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` ++]) ++AC_SUBST([APP_STORE_COMPLIANCE_PATCH]) ++ + AC_SUBST([_PYTHON_HOST_PLATFORM]) + if test "$cross_compiling" = yes; then + case "$host" in +@@ -730,6 +843,46 @@ + ;; + 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=${_host_os_min_version:3}-watchosos-arm64_32 -+ ;; -+ *) -+ _host_ident=${_host_os_min_version:3}-watchosos-$host_cpu -+ esac -+ ;; -+ *-apple-*) ++ aarch64) ++ _host_ident=${TVOS_DEPLOYMENT_TARGET}-arm64-appletv${_host_device} ++ ;; ++ *) ++ _host_ident=${TVOS_DEPLOYMENT_TARGET}-$host_cpu-appletv${_host_device} ++ ;; ++ esac ++ ;; ++ *-apple-watchos*) ++ _host_os=`echo $host | cut -d '-' -f3` ++ _host_device=`echo $host | cut -d '-' -f4` ++ _host_device=${_host_device:=os} ++ ++ # WATCHOS_DEPLOYMENT_TARGET is the minimum supported watchOS version ++ AC_MSG_CHECKING([watchOS deployment target]) ++ WATCHOS_DEPLOYMENT_TARGET=${_host_os:7} ++ WATCHOS_DEPLOYMENT_TARGET=${WATCHOS_DEPLOYMENT_TARGET:=4.0} ++ AC_MSG_RESULT([$WATCHOS_DEPLOYMENT_TARGET]) ++ + case "$host_cpu" in -+ 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 +771,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/*) @@ -2106,117 +1269,748 @@ 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 @@ - AC_MSG_CHECKING([for multiarch]) +@@ -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 Add the compiler flag for the iOS minimum supported OS version. ++dnl Add the compiler flag for the iOS/tvOS/watchOS minimum supported OS version. + AS_CASE([$ac_sys_system], + [iOS], [ + AS_VAR_APPEND([CFLAGS], [" -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"]) + AS_VAR_APPEND([LDFLAGS], [" -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"]) ++ ],[tvOS], [ ++ AS_VAR_APPEND([CFLAGS], [" -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}"]) ++ AS_VAR_APPEND([LDFLAGS], [" -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}"]) ++ ],[watchOS], [ ++ AS_VAR_APPEND([CFLAGS], [" -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}"]) ++ AS_VAR_APPEND([LDFLAGS], [" -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}"]) + ], + ) + +@@ -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 +1042,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 +1078,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] ) -@@ -3085,6 +3181,7 @@ - esac - ;; - CYGWIN*) SHLIB_SUFFIX=.dll;; -+ iOS|tvOS|watchOS) SHLIB_SUFFIX=.dylib;; - *) SHLIB_SUFFIX=.so;; - esac - fi -@@ -3165,6 +3262,9 @@ +@@ -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' + ;; + AIX*) +@@ -3417,7 +3589,7 @@ BLDSHARED="$LDSHARED" fi ;; +- iOS/*) + iOS/*|tvOS/*|watchOS/*) -+ LDSHARED='$(CC) -dynamiclib -undefined dynamic_lookup' -+ LDCXXSHARED='$(CXX) -dynamiclib -undefined dynamic_lookup';; - Emscripten|WASI) - LDSHARED='$(CC) -shared' - LDCXXSHARED='$(CXX) -shared';; -@@ -3682,6 +3782,9 @@ + 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 + ;; +@@ -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 +5817,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]) - - # Release build, debug build (Py_DEBUG), and trace refs build (Py_TRACE_REFS) -@@ -5722,7 +5825,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]) +@@ -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 + +-# 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 +7171,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])], + 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 ++===================== ++ ++: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-2024 Python Software Foundation. ++ CFBundleSignature ++ ???? ++ CFBundleVersion ++ 1 ++ 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${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 $@ +--- /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 $@ +--- /dev/null ++++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-ar +@@ -0,0 +1,2 @@ ++#!/bin/bash ++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 $@ +--- /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 $@ +--- /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 $@ +--- /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 ++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 @@ ++ ++ ++ ++ ++ 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 @@ ++======================== ++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${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-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-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-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 ++ ++ 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 4acff2c..0000000 --- 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 d4afe05..0000000 --- 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 f842b98..0000000 --- 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/release.common.exclude b/patch/Python/release.common.exclude deleted file mode 100644 index bec2b86..0000000 --- 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 fb1f841..f1d0f75 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.macOS.exclude b/patch/Python/release.macOS.exclude index 1428348..f45bf7f 100644 --- a/patch/Python/release.macOS.exclude +++ b/patch/Python/release.macOS.exclude @@ -1,8 +1,14 @@ -# 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 +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/Python/release.tvOS.exclude b/patch/Python/release.tvOS.exclude index e9ff418..f1d0f75 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 c8b1d66..f1d0f75 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/Python/test.exclude b/patch/Python/test.exclude deleted file mode 100644 index add994a..0000000 --- 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 diff --git a/patch/make-relocatable.sh b/patch/make-relocatable.sh new file mode 100755 index 0000000..f268cd7 --- /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 50340e1..0000000 --- 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