diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..ecbb02f7 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + # Check for updates on Sunday, 8PM UTC + interval: "weekly" + day: "sunday" + time: "20:00" diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 00000000..717febef --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,212 @@ +name: CI +on: + pull_request: + workflow_call: + inputs: + build-number: + description: "The build number to add to the built package" + default: "custom" + type: "string" + outputs: + PYTHON_VER: + description: "The Python major.minor version." + value: ${{ jobs.config.outputs.PYTHON_VER }} + PYTHON_VERSION: + description: "The full Python version." + value: ${{ jobs.config.outputs.PYTHON_VERSION }} + BZIP2_VERSION: + description: "The BZip2 version used for the build." + value: ${{ jobs.config.outputs.BZIP2_VERSION }} + LIBFFI_VERSION: + description: "The libFFI version used for the build." + value: ${{ jobs.config.outputs.LIBFFI_VERSION }} + MPDECIMAL_VERSION: + description: "The mpdecimal version used for the build." + value: ${{ jobs.config.outputs.MPDECIMAL_VERSION }} + OPENSSL_VERSION: + description: "The OpenSSL version used for the build." + value: ${{ jobs.config.outputs.OPENSSL_VERSION }} + XZ_VERSION: + description: "The XZ version used for the build." + value: ${{ jobs.config.outputs.XZ_VERSION }} + +env: + FORCE_COLOR: "1" + +defaults: + run: + shell: bash + +# Cancel active CI runs for a PR before starting another run +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true + +jobs: + config: + runs-on: macOS-latest + outputs: + PYTHON_VER: ${{ steps.extract.outputs.PYTHON_VER }} + PYTHON_VERSION: ${{ steps.extract.outputs.PYTHON_VERSION }} + BUILD_NUMBER: ${{ steps.extract.outputs.BUILD_NUMBER }} + BZIP2_VERSION: ${{ steps.extract.outputs.BZIP2_VERSION }} + LIBFFI_VERSION: ${{ steps.extract.outputs.LIBFFI_VERSION }} + MPDECIMAL_VERSION: ${{ steps.extract.outputs.MPDECIMAL_VERSION }} + OPENSSL_VERSION: ${{ steps.extract.outputs.OPENSSL_VERSION }} + XZ_VERSION: ${{ steps.extract.outputs.XZ_VERSION }} + + steps: + - uses: actions/checkout@v4.1.7 + + - name: Extract config variables + id: extract + run: | + PYTHON_VER=$(make config | grep "PYTHON_VER=" | cut -d "=" -f 2) + PYTHON_VERSION=$(make config | grep "PYTHON_VERSION=" | cut -d "=" -f 2) + BZIP2_VERSION=$(make config | grep "BZIP2_VERSION=" | cut -d "=" -f 2) + LIBFFI_VERSION=$(make config | grep "LIBFFI_VERSION=" | cut -d "=" -f 2) + MPDECIMAL_VERSION=$(make config | grep "MPDECIMAL_VERSION=" | cut -d "=" -f 2) + OPENSSL_VERSION=$(make config | grep "OPENSSL_VERSION=" | cut -d "=" -f 2) + XZ_VERSION=$(make config | grep "XZ_VERSION=" | cut -d "=" -f 2) + if [ -z "${{ inputs.build-number }}" ]; then + BUILD_NUMBER=custom + else + BUILD_NUMBER=${{ inputs.build-number }} + fi + + echo "PYTHON_VER=${PYTHON_VER}" | tee -a ${GITHUB_OUTPUT} + echo "PYTHON_VERSION=${PYTHON_VERSION}" | tee -a ${GITHUB_OUTPUT} + echo "BUILD_NUMBER=${BUILD_NUMBER}" | tee -a ${GITHUB_OUTPUT} + echo "BZIP2_VERSION=${BZIP2_VERSION}" | tee -a ${GITHUB_OUTPUT} + echo "LIBFFI_VERSION=${LIBFFI_VERSION}" | tee -a ${GITHUB_OUTPUT} + echo "MPDECIMAL_VERSION=${MPDECIMAL_VERSION}" | tee -a ${GITHUB_OUTPUT} + echo "OPENSSL_VERSION=${OPENSSL_VERSION}" | tee -a ${GITHUB_OUTPUT} + echo "XZ_VERSION=${XZ_VERSION}" | tee -a ${GITHUB_OUTPUT} + + build: + runs-on: macOS-latest + needs: [ config ] + strategy: + fail-fast: false + matrix: + target: ['macOS', 'iOS', 'tvOS', 'watchOS', 'visionOS'] + + steps: + - uses: actions/checkout@v4.1.7 + + - name: Set up Python + uses: actions/setup-python@v5.6.0 + with: + # Appending -dev ensures that we can always build the dev release. + # It's a no-op for versions that have been published. + python-version: ${{ needs.config.outputs.PYTHON_VER }}-dev + # Ensure that we *always* use the latest build, not a cached version. + # It's an edge case, but when a new alpha is released, we need to use it ASAP. + check-latest: true + + - name: Build ${{ matrix.target }} + run: | + # Do the build for the requested target. + make ${{ matrix.target }} BUILD_NUMBER=${{ needs.config.outputs.BUILD_NUMBER }} + + - name: Upload build artefacts + uses: actions/upload-artifact@v4.6.2 + with: + name: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz + path: dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz + + briefcase-testbed: + name: Briefcase testbed (${{ matrix.target }}) + runs-on: macOS-latest + needs: [ config, build ] + strategy: + fail-fast: false + matrix: + target: ["macOS", "iOS"] + include: + - briefcase-run-args: + + - target: iOS + briefcase-run-args: ' -d "iPhone SE (3rd generation)"' + + steps: + - uses: actions/checkout@v4.1.7 + + - name: Get build artifact + uses: actions/download-artifact@v4.3.0 + with: + pattern: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz + path: dist + merge-multiple: true + + - name: Set up Python + uses: actions/setup-python@v5.6.0 + with: + # Appending -dev ensures that we can always build the dev release. + # It's a no-op for versions that have been published. + python-version: ${{ needs.config.outputs.PYTHON_VER }}-dev + # Ensure that we *always* use the latest build, not a cached version. + # It's an edge case, but when a new alpha is released, we need to use it ASAP. + check-latest: true + + - uses: actions/checkout@v4.1.7 + with: + repository: beeware/Python-support-testbed + path: Python-support-testbed + + - name: Install dependencies + run: | + # Use the development version of Briefcase + python -m pip install git+https://github.com/beeware/briefcase.git + + - name: Run support testbed check + timeout-minutes: 10 + working-directory: Python-support-testbed + run: briefcase run ${{ matrix.target }} Xcode --test ${{ matrix.briefcase-run-args }} -C support_package=\'../dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz\' + + cpython-testbed: + name: CPython testbed (${{ matrix.target }}) + runs-on: macOS-latest + needs: [ config, build ] + strategy: + fail-fast: false + matrix: + target: ["iOS", "visionOS"] + + steps: + - uses: actions/checkout@v4.1.7 + + - name: Get build artifact + uses: actions/download-artifact@v4.3.0 + with: + pattern: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz + path: dist + merge-multiple: true + + - name: Set up Python + uses: actions/setup-python@v5.6.0 + with: + # Appending -dev ensures that we can always build the dev release. + # It's a no-op for versions that have been published. + python-version: ${{ needs.config.outputs.PYTHON_VER }}-dev + # Ensure that we *always* use the latest build, not a cached version. + # It's an edge case, but when a new alpha is released, we need to use it ASAP. + check-latest: true + + - name: Unpack support package + run: | + mkdir support + cd support + tar zxvf ../dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz + + - name: Run CPython testbed + timeout-minutes: 10 + working-directory: support + run: | + # Run a representative subset of CPython core tests: + # - test_builtin as a test of core language tools + # - test_grammar as a test of core language features + # - test_os as a test of system library calls + # - test_bz2 as a simple test of third party libraries + # - test_ctypes as a test of FFI + python -m testbed run -- test --single-process --rerun -W test_builtin test_grammar test_os test_bz2 test_ctypes diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index fd2c9623..587abe83 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -8,24 +8,26 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + - name: Set up Python environment - uses: actions/setup-python@v1 + uses: actions/setup-python@v5.6.0 with: python-version: "3.X" + - name: Set build variables + id: build-vars env: TAG_NAME: ${{ github.ref }} run: | - export TAG=$(basename $TAG_NAME) - echo "TAG=${TAG}" - export TAG_VERSION="${TAG%-*}" - export TAG_BUILD="${TAG#*-}" - echo "PY_VERSION=${TAG_VERSION}" - echo "BUILD_NUMBER=${TAG_BUILD}" - echo "TAG=${TAG}" >> $GITHUB_ENV - echo "PY_VERSION=${TAG_VERSION}" >> $GITHUB_ENV - echo "BUILD_NUMBER=${TAG_BUILD}" >> $GITHUB_ENV + TAG=$(basename $TAG_NAME) + PYTHON_VER="${TAG%-*}" + BUILD_NUMBER="${TAG#*-}" + + echo "TAG=${TAG}" | tee -a ${GITHUB_OUTPUT} + echo "PYTHON_VER=${PYTHON_VER}" | tee -a ${GITHUB_OUTPUT} + echo "BUILD_NUMBER=${BUILD_NUMBER}" | tee -a ${GITHUB_OUTPUT} + - name: Update Release Asset to S3 env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} @@ -35,14 +37,17 @@ jobs: python -m pip install -U setuptools python -m pip install awscli # macOS build - curl -o macOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ env.TAG }}/Python-${{ env.PY_VERSION }}-macOS-support.${{ env.BUILD_NUMBER }}.tar.gz - aws s3 cp macOS-artefact.tar.gz s3://briefcase-support/python/${{ env.PY_VERSION }}/macOS/Python-${{ env.PY_VERSION }}-macOS-support.${{ env.BUILD_NUMBER }}.tar.gz + curl -o macOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.outputs.TAG }}/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-macOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + aws s3 cp macOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.outputs.PYTHON_VER }}/macOS/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-macOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz # iOS build - curl -o iOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ env.TAG }}/Python-${{ env.PY_VERSION }}-iOS-support.${{ env.BUILD_NUMBER }}.tar.gz - aws s3 cp iOS-artefact.tar.gz s3://briefcase-support/python/${{ env.PY_VERSION }}/iOS/Python-${{ env.PY_VERSION }}-iOS-support.${{ env.BUILD_NUMBER }}.tar.gz + curl -o iOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.outputs.TAG }}/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-iOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + aws s3 cp iOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.outputs.PYTHON_VER }}/iOS/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-iOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz # tvOS build - curl -o tvOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ env.TAG }}/Python-${{ env.PY_VERSION }}-tvOS-support.${{ env.BUILD_NUMBER }}.tar.gz - aws s3 cp tvOS-artefact.tar.gz s3://briefcase-support/python/${{ env.PY_VERSION }}/tvOS/Python-${{ env.PY_VERSION }}-tvOS-support.${{ env.BUILD_NUMBER }}.tar.gz + curl -o tvOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.outputs.TAG }}/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-tvOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + aws s3 cp tvOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.outputs.PYTHON_VER }}/tvOS/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-tvOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz # watchOS build - curl -o watchOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ env.TAG }}/Python-${{ env.PY_VERSION }}-watchOS-support.${{ env.BUILD_NUMBER }}.tar.gz - aws s3 cp watchOS-artefact.tar.gz s3://briefcase-support/python/${{ env.PY_VERSION }}/watchOS/Python-${{ env.PY_VERSION }}-watchOS-support.${{ env.BUILD_NUMBER }}.tar.gz + curl -o watchOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.outputs.TAG }}/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-watchOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + aws s3 cp watchOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.outputs.PYTHON_VER }}/watchOS/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-watchOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + # visionOS build + curl -o visionOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.outputs.TAG }}/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-visionOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + aws s3 cp visionOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.outputs.PYTHON_VER }}/visionOS/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-visionOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 1dd93032..fd78eeec 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -8,89 +8,58 @@ on: - '*-b*' jobs: - make-release: - runs-on: ubuntu-latest + config: + name: Build vars + runs-on: macOS-latest + outputs: + TAG: ${{ steps.build-vars.outputs.TAG }} + BUILD_NUMBER: ${{ steps.build-vars.outputs.BUILD_NUMBER }} + steps: - - name: Set build variables + - name: Set Build Variables + id: build-vars env: TAG_NAME: ${{ github.ref }} run: | export TAG=$(basename $TAG_NAME) - echo "TAG=${TAG}" - export TAG_VERSION="${TAG%-*}" - export TAG_BUILD="${TAG#*-}" - echo "PY_VERSION=${TAG_VERSION}" - echo "BUILD_NUMBER=${TAG_BUILD}" - echo "TAG=${TAG}" >> $GITHUB_ENV - echo "PY_VERSION=${TAG_VERSION}" >> $GITHUB_ENV - echo "BUILD_NUMBER=${TAG_BUILD}" >> $GITHUB_ENV - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref }} - release_name: ${{ github.ref }} - draft: true - prerelease: false - body: | - Build ${{ env.BUILD_NUMBER }} of the BeeWare support package for Python ${{ env.PY_VERSION }}. + export BUILD_NUMBER="${TAG#*-}" - Includes: - * Python ${{ env.PY_VERSION }}.? - * OpenSSL 3.1.2 - * BZip2 1.0.8 - * XZ 5.4.4 - * LibFFI 3.4.4 - outputs: - upload_url: ${{ steps.create_release.outputs.upload_url }} + echo "TAG=${TAG}" | tee -a ${GITHUB_OUTPUT} + echo "BUILD_NUMBER=${BUILD_NUMBER}" | tee -a ${GITHUB_OUTPUT} - build: - runs-on: macOS-latest - needs: make-release - strategy: - max-parallel: 4 - matrix: - target: ['macOS', 'iOS', 'tvOS', 'watchOS'] + ci: + name: CI + needs: [ config ] + uses: ./.github/workflows/ci.yaml + with: + build-number: ${{ needs.config.outputs.BUILD_NUMBER }} + + make-release: + name: Make Release + runs-on: ubuntu-latest + needs: [ config, ci ] steps: - - uses: actions/checkout@v2 - - name: Set build variables - env: - TAG_NAME: ${{ github.ref }} - run: | - export TAG=$(basename $TAG_NAME) - echo "TAG=${TAG}" - export TAG_VERSION="${TAG%-*}" - export TAG_BUILD="${TAG#*-}" - echo "PY_VERSION=${TAG_VERSION}" - echo "BUILD_NUMBER=${TAG_BUILD}" - echo "TAG=${TAG}" >> $GITHUB_ENV - echo "PY_VERSION=${TAG_VERSION}" >> $GITHUB_ENV - echo "BUILD_NUMBER=${TAG_BUILD}" >> $GITHUB_ENV - - name: Set up Python - uses: actions/setup-python@v3.1.2 + - name: Get build artifacts + uses: actions/download-artifact@v4.3.0 with: - python-version: ${{ env.TAG_VERSION }} - - name: Build ${{ matrix.target }} - env: - BUILD_NUMBER: ${{ env.BUILD_NUMBER }} - run: | - # 2020-06-24: The Homebrew copy of gettext leaks into the macOS build - # if it is present. Uninstall gettext to make sure that doesn't happen. - # 2021-01-02: Uninstall curl as well. We need curl to download the - # source bundles, but Homebrew's curl has a runtime dependency on - # gettext. However macOS provides a version of curl that works fine. - brew uninstall --ignore-dependencies gettext curl + pattern: Python-* + path: dist + merge-multiple: true - # Do the build for the requested target. - make -e ${{ matrix.target }} - - name: Upload ${{ matrix.target }} release asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Create Release + uses: ncipollo/release-action@v1.16.0 with: - upload_url: ${{ needs.make-release.outputs.upload_url }} - asset_path: ./dist/Python-${{ env.PY_VERSION }}-${{ matrix.target }}-support.${{ env.BUILD_NUMBER }}.tar.gz - asset_name: Python-${{ env.PY_VERSION }}-${{ matrix.target }}-support.${{ env.BUILD_NUMBER }}.tar.gz - asset_content_type: application/gzip + name: ${{ needs.ci.outputs.PYTHON_VER }}-${{ needs.config.outputs.BUILD_NUMBER }} + tag: ${{ needs.ci.outputs.PYTHON_VER }}-${{ needs.config.outputs.BUILD_NUMBER }} + draft: true + body: | + Build ${{ needs.config.outputs.BUILD_NUMBER }} of the BeeWare support package for Python ${{ needs.ci.outputs.PYTHON_VER }}. + + Includes: + * Python ${{ needs.ci.outputs.PYTHON_VERSION }} + * BZip2 ${{ needs.ci.outputs.BZIP2_VERSION }} + * libFFI ${{ needs.ci.outputs.LIBFFI_VERSION }} + * mpdecimal ${{ needs.ci.outputs.MPDECIMAL_VERSION }} + * OpenSSL ${{ needs.ci.outputs.OPENSSL_VERSION }} + * XZ ${{ needs.ci.outputs.XZ_VERSION }} + artifacts: "dist/*" diff --git a/.gitignore b/.gitignore index 22c9b3e7..f8053935 100644 --- a/.gitignore +++ b/.gitignore @@ -2,18 +2,12 @@ *.swp .envrc .vscode/ -alias/* -archive/* -build/* -diff/* -dist/* -downloads/* -install/* -local/* -merge/* -src/* -support/* -wheels/* +build +dist +downloads +install +local +support *.dist-info __pycache__ tests/testbed/macOS diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d6aad904..dfed555e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,44 @@ BeeWare <3's contributions! -Please be aware, BeeWare operates under a Code of Conduct. +Please be aware that BeeWare operates under a [Code of +Conduct](https://beeware.org/community/behavior/code-of-conduct/). -See [CONTRIBUTING to BeeWare](https://beeware.org/contributing) for details. +See [CONTRIBUTING to BeeWare](https://beeware.org/contributing) for general +project contribution guidelines. +Unless a fix is version specific, PRs should genereally be made against the +`main` branch of this repo, targeting the current development version of Python. +Project maintainers will manage the process of backporting changes to older +Python versions. + +## Changes to `Python.patch` + +Additional handling is required if you need to make modifications to the patch +applied to Python sources (`patch/Python/Python.patch`). + +Any iOS or macOS-specific changes should be submitted to the [upstream CPython +repository](https://github.com/python/cpython). macOS and iOS are both +officially supported Python platforms, and the code distributed by this project +for those platforms is unmodified from the official repository. + +Changes to to support other platforms can be included in a PR for this repo, but +they must also be submitted as a pull request against the `MAJOR.MINOR-patched` +branch on [the `freakboy3742` fork of the CPython +repo](https://github.com/freakboy3742/cpython). This is required to ensure that +any contributed changes can be easily reproduced in future patches as more +changes are made. + +Note that the `MAJOR.MINOR-patched` branch of that fork is maintained in the format +of a *patch tree*, which is a branch that consists of an entirely linear sequence of +commits applied on top of another branch (in the case of the fork, `MAJOR.MINOR`), +each of which adds a significant new feature. Therefore, a bug fix for an existing commit +in the patch tree *will* be merged when appropriate, but its changes will get combined +with that existing commit that adds the feature. A feature addition PR will be squashed +into a single, new commit, and then put on top of the patch tree. + +This also means that if another contributor gets a pull request merged into +`MAJOR.MINOR-patched`, you must *rebase* your changes on top of the updated +`MAJOR.MINOR-patched` branch, as opposed to *merging* `MAJOR.MINOR-patched` into your +branch, since the "history" of a patch tree is likely to change in a way that is +incompatible with merge commits. diff --git a/Makefile b/Makefile index add83aaa..ab0b3563 100644 --- a/Makefile +++ b/Makefile @@ -5,30 +5,7 @@ # - iOS - build everything for iOS # - tvOS - build everything for tvOS # - watchOS - build everything for watchOS -# - BZip2 - build BZip2 for all platforms -# - BZip2-macOS - build BZip2 for macOS -# - BZip2-iOS - build BZip2 for iOS -# - BZip2-tvOS - build BZip2 for tvOS -# - BZip2-watchOS - build BZip2 for watchOS -# - XZ - build XZ for all platforms -# - XZ-macOS - build XZ for macOS -# - XZ-iOS - build XZ for iOS -# - XZ-tvOS - build XZ for tvOS -# - XZ-watchOS - build XZ for watchOS -# - OpenSSL - build OpenSSL for all platforms -# - OpenSSL-macOS - build OpenSSL for macOS -# - OpenSSL-iOS - build OpenSSL for iOS -# - OpenSSL-tvOS - build OpenSSL for tvOS -# - OpenSSL-watchOS - build OpenSSL for watchOS -# - libFFI - build libFFI for all platforms (except macOS) -# - libFFI-iOS - build libFFI for iOS -# - libFFI-tvOS - build libFFI for tvOS -# - libFFI-watchOS - build libFFI for watchOS -# - Python - build Python for all platforms -# - Python-macOS - build Python for macOS -# - Python-iOS - build Python for iOS -# - Python-tvOS - build Python for tvOS -# - Python-watchOS - build Python for watchOS +# - visionOS - build everything for visionOS # Current directory PROJECT_DIR=$(shell pwd) @@ -37,59 +14,57 @@ BUILD_NUMBER=custom # Version of packages that will be compiled by this meta-package # PYTHON_VERSION is the full version number (e.g., 3.10.0b3) +# PYTHON_PKG_VERSION is the version number with binary package releases to use +# for macOS binaries. This will be less than PYTHON_VERSION towards the end +# of a release cycle, as official binaries won't be published. # PYTHON_MICRO_VERSION is the full version number, without any alpha/beta/rc suffix. (e.g., 3.10.0) # PYTHON_VER is the major/minor version (e.g., 3.10) -PYTHON_VERSION=3.13.0a1 +PYTHON_VERSION=3.14.0b2 +PYTHON_PKG_VERSION=$(PYTHON_VERSION) PYTHON_MICRO_VERSION=$(shell echo $(PYTHON_VERSION) | grep -Eo "\d+\.\d+\.\d+") +PYTHON_PKG_MICRO_VERSION=$(shell echo $(PYTHON_PKG_VERSION) | grep -Eo "\d+\.\d+\.\d+") PYTHON_VER=$(basename $(PYTHON_VERSION)) -BZIP2_VERSION=1.0.8 +# The binary releases of dependencies, published at: +# https://github.com/beeware/cpython-apple-source-deps/releases +BZIP2_VERSION=1.0.8-2 +LIBFFI_VERSION=3.4.7-2 +MPDECIMAL_VERSION=4.0.0-2 +OPENSSL_VERSION=3.0.16-2 +XZ_VERSION=5.6.4-2 -XZ_VERSION=5.4.4 - -# Preference is to use OpenSSL 3; however, Cryptography 3.4.8 (and -# probably some other packages as well) only works with 1.1.1, so -# we need to preserve the ability to build the older OpenSSL (for now...) -OPENSSL_VERSION=3.1.2 -# OPENSSL_VERSION_NUMBER=1.1.1 -# OPENSSL_REVISION=v -# OPENSSL_VERSION=$(OPENSSL_VERSION_NUMBER)$(OPENSSL_REVISION) - -LIBFFI_VERSION=3.4.4 - -# Supported OS and dependencies -DEPENDENCIES=BZip2 XZ OpenSSL libFFI -OS_LIST=macOS iOS tvOS watchOS +# Supported OS +OS_LIST=macOS iOS tvOS watchOS visionOS CURL_FLAGS=--disable --fail --location --create-dirs --progress-bar # macOS targets TARGETS-macOS=macosx.x86_64 macosx.arm64 +TRIPLE_OS-macOS=macos VERSION_MIN-macOS=11.0 -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) +TRIPLE_OS-iOS=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 +TRIPLE_OS-tvOS=tvos +VERSION_MIN-tvOS=12.0 # watchOS targets TARGETS-watchOS=watchsimulator.x86_64 watchsimulator.arm64 watchos.arm64_32 +TRIPLE_OS-watchOS=watchos VERSION_MIN-watchOS=4.0 -CFLAGS-watchOS=-mwatchos-version-min=$(VERSION_MIN-watchOS) -PYTHON_CONFIGURE-watchOS=ac_cv_func_sigaltstack=no + +TARGETS-visionOS=xrsimulator.arm64 xros.arm64 +TRIPLE_OS-visionOS=xros +VERSION_MIN-visionOS=2.0 # The architecture of the machine doing the build HOST_ARCH=$(shell uname -m) -HOST_PYTHON=install/macOS/macosx/python-$(PYTHON_VERSION) -HOST_PYTHON_EXE=$(HOST_PYTHON)/bin/python3 -BDIST_WHEEL=$(HOST_PYTHON)/lib/python$(PYTHON_VER)/site-packages/wheel/bdist_wheel.py +HOST_PYTHON=$(shell which python$(PYTHON_VER)) # Force the path to be minimal. This ensures that anything in the user environment # (in particular, homebrew and user-provided Python installs) aren't inadvertently @@ -97,28 +72,17 @@ BDIST_WHEEL=$(HOST_PYTHON)/lib/python$(PYTHON_VER)/site-packages/wheel/bdist_whe PATH=/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin # Build for all operating systems -all: $(OS_LIST) wheels +all: $(OS_LIST) .PHONY: \ - all clean distclean update-patch vars wheels \ - $(foreach product,$(PRODUCTS),$(product) $(foreach os,$(OS_LIST),$(product)-$(os) clean-$(product) clean-$(product)-$(os))) \ - $(foreach product,$(PRODUCTS),$(product)-wheels $(foreach os,$(OS_LIST),$(product)-$(os)-wheels clean-$(product)-$(os)-wheels)) \ - $(foreach os,$(OS_LIST),$(os) $(os)-wheels clean-$(os) clean-$(os)-wheels vars-$(os)) - -# Clean all builds -clean: - rm -rf build install merge dist support wheels + 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))) # Full clean - includes all downloaded products distclean: clean - rm -rf downloads - -downloads: \ - downloads/bzip2-$(BZIP2_VERSION).tar.gz \ - downloads/xz-$(XZ_VERSION).tar.gz \ - downloads/openssl-$(OPENSSL_VERSION).tar.gz \ - downloads/libffi-$(LIBFFI_VERSION).tar.gz \ - downloads/Python-$(PYTHON_VERSION).tar.gz + rm -rf downloads build dist install support update-patch: # Generate a diff from the clone of the python/cpython Github repository, @@ -131,64 +95,26 @@ 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 -########################################################################### -# Setup: BZip2 -########################################################################### - -# Download original BZip2 source code archive. -downloads/bzip2-$(BZIP2_VERSION).tar.gz: - @echo ">>> Download BZip2 sources" - curl $(CURL_FLAGS) -o $@ \ - https://sourceware.org/pub/bzip2/$(notdir $@) - -########################################################################### -# Setup: XZ (LZMA) -########################################################################### - -# Download original XZ source code archive. -downloads/xz-$(XZ_VERSION).tar.gz: - @echo ">>> Download XZ sources" - curl $(CURL_FLAGS) -o $@ \ - https://tukaani.org/xz/$(notdir $@) - -########################################################################### -# Setup: OpenSSL -# These build instructions adapted from the scripts developed by -# Felix Shchulze (@x2on) https://github.com/x2on/OpenSSL-for-iPhone -########################################################################### - -# Download original OpenSSL source code archive. -downloads/openssl-$(OPENSSL_VERSION).tar.gz: - @echo ">>> Download OpenSSL sources" - curl $(CURL_FLAGS) -o $@ \ - https://openssl.org/source/$(notdir $@) \ - || curl $(CURL_FLAGS) -o $@ \ - https://openssl.org/source/old/$(basename $(OPENSSL_VERSION))/$(notdir $@) - -########################################################################### -# Setup: libFFI -########################################################################### - -# Download original libFFI source code archive. -downloads/libffi-$(LIBFFI_VERSION).tar.gz: - @echo ">>> Download libFFI sources" - curl $(CURL_FLAGS) -o $@ \ - https://github.com/libffi/libffi/releases/download/v$(LIBFFI_VERSION)/$(notdir $@) - ########################################################################### # Setup: Python ########################################################################### -# Download original Python source code archive. downloads/Python-$(PYTHON_VERSION).tar.gz: @echo ">>> Download Python sources" + mkdir -p downloads + curl $(CURL_FLAGS) -o $@ \ + https://www.python.org/ftp/python/$(PYTHON_MICRO_VERSION)/Python-$(PYTHON_VERSION).tgz + +downloads/python-$(PYTHON_PKG_VERSION)-macos11.pkg: + @echo ">>> Download macOS Python package" + mkdir -p downloads curl $(CURL_FLAGS) -o $@ \ - https://www.python.org/ftp/python/$(PYTHON_MICRO_VERSION)/$(basename $(basename $(notdir $@))).tgz + https://www.python.org/ftp/python/$(PYTHON_PKG_MICRO_VERSION)/python-$(PYTHON_PKG_VERSION)-macos11.pkg ########################################################################### # Build for specified target (from $(TARGETS-*)) @@ -209,280 +135,97 @@ 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_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(TRIPLE_OS-$(os))$$(VERSION_MIN-$(os)) +IS_SIMULATOR-$(target)=False else -TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(OS_LOWER-$(target))$$(VERSION_MIN-$(os))-simulator +TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(TRIPLE_OS-$(os))$$(VERSION_MIN-$(os))-simulator +IS_SIMULATOR-$(target)=True endif endif SDK_ROOT-$(target)=$$(shell xcrun --sdk $$(SDK-$(target)) --show-sdk-path) -CFLAGS-$(target)=$$(CFLAGS-$(os)) -LDFLAGS-$(target)=$$(CFLAGS-$(os)) - -########################################################################### -# Target: Aliases -########################################################################### - -install/$(os)/bin/$$(TARGET_TRIPLE-$(target))-gcc: - patch/make-xcrun-alias $$@ "--sdk $$(SDK-$(target)) clang -target $$(TARGET_TRIPLE-$(target))" - -install/$(os)/bin/$$(TARGET_TRIPLE-$(target))-cpp: - patch/make-xcrun-alias $$@ "--sdk $$(SDK-$(target)) clang -target $$(TARGET_TRIPLE-$(target)) -E" ########################################################################### # Target: BZip2 ########################################################################### -BZIP2_SRCDIR-$(target)=build/$(os)/$(target)/bzip2-$(BZIP2_VERSION) BZIP2_INSTALL-$(target)=$(PROJECT_DIR)/install/$(os)/$(target)/bzip2-$(BZIP2_VERSION) BZIP2_LIB-$(target)=$$(BZIP2_INSTALL-$(target))/lib/libbz2.a -BZIP2_WHEEL-$(target)=wheels/$(os)/bzip2-$(BZIP2_VERSION)-1-$$(WHEEL_TAG-$(target)).whl -BZIP2_WHEEL_DISTINFO-$(target)=$$(BZIP2_INSTALL-$(target))/wheel/bzip2-$(BZIP2_VERSION).dist-info - -$$(BZIP2_SRCDIR-$(target))/Makefile: downloads/bzip2-$(BZIP2_VERSION).tar.gz - @echo ">>> Unpack BZip2 sources for $(target)" - mkdir -p $$(BZIP2_SRCDIR-$(target)) - tar zxf $$< --strip-components 1 -C $$(BZIP2_SRCDIR-$(target)) - # Touch the makefile to ensure that Make identifies it as up to date. - touch $$(BZIP2_SRCDIR-$(target))/Makefile - -$$(BZIP2_LIB-$(target)): install/$(os)/bin/$$(TARGET_TRIPLE-$(target))-gcc $$(BZIP2_SRCDIR-$(target))/Makefile - @echo ">>> Build BZip2 for $(target)" - cd $$(BZIP2_SRCDIR-$(target)) && \ - PATH="$(PROJECT_DIR)/install/$(os)/bin:$(PATH)" \ - make install \ - PREFIX="$$(BZIP2_INSTALL-$(target))" \ - CC=$$(TARGET_TRIPLE-$(target))-gcc \ - CFLAGS="$$(CFLAGS-$(target))" \ - LDFLAGS="$$(LDFLAGS-$(target))" \ - 2>&1 | tee -a ../bzip2-$(BZIP2_VERSION).build.log - -$$(BZIP2_WHEEL-$(target)): $$(BZIP2_LIB-$(target)) $$(BDIST_WHEEL) - @echo ">>> Build BZip2 wheel for $(target)" - mkdir -p $$(BZIP2_WHEEL_DISTINFO-$(target)) - mkdir -p $$(BZIP2_INSTALL-$(target))/wheel/opt - - # Copy distributable content - cp -r $$(BZIP2_INSTALL-$(target))/include $$(BZIP2_INSTALL-$(target))/wheel/opt/include - cp -r $$(BZIP2_INSTALL-$(target))/lib $$(BZIP2_INSTALL-$(target))/wheel/opt/lib - - # Copy LICENSE file - cp $$(BZIP2_SRCDIR-$(target))/LICENSE $$(BZIP2_WHEEL_DISTINFO-$(target)) - - # Write package metadata - echo "Metadata-Version: 1.2" > $$(BZIP2_WHEEL_DISTINFO-$(target))/METADATA - echo "Name: bzip2" >> $$(BZIP2_WHEEL_DISTINFO-$(target))/METADATA - echo "Version: $(BZIP2_VERSION)" >> $$(BZIP2_WHEEL_DISTINFO-$(target))/METADATA - echo "Summary: " >> $$(BZIP2_WHEEL_DISTINFO-$(target))/METADATA - echo "Download-URL: " >> $$(BZIP2_WHEEL_DISTINFO-$(target))/METADATA - - # Write wheel metadata - echo "Wheel-Version: 1.0" > $$(BZIP2_WHEEL_DISTINFO-$(target))/WHEEL - echo "Root-Is-Purelib: false" >> $$(BZIP2_WHEEL_DISTINFO-$(target))/WHEEL - echo "Generator: Python-Apple-support.BeeWare" >> $$(BZIP2_WHEEL_DISTINFO-$(target))/WHEEL - echo "Build: 1" >> $$(BZIP2_WHEEL_DISTINFO-$(target))/WHEEL - echo "Tag: $$(WHEEL_TAG-$(target))" >> $$(BZIP2_WHEEL_DISTINFO-$(target))/WHEEL - - # Pack the wheel - mkdir -p wheels/$(os) - $(HOST_PYTHON_EXE) -m wheel pack $$(BZIP2_INSTALL-$(target))/wheel --dest-dir wheels/$(os) --build-number 1 + +downloads/bzip2-$(BZIP2_VERSION)-$(target).tar.gz: + @echo ">>> Download BZip2 for $(target)" + mkdir -p downloads + curl $(CURL_FLAGS) -o $$@ \ + https://github.com/beeware/cpython-apple-source-deps/releases/download/BZip2-$(BZIP2_VERSION)/bzip2-$(BZIP2_VERSION)-$(target).tar.gz + +$$(BZIP2_LIB-$(target)): downloads/bzip2-$(BZIP2_VERSION)-$(target).tar.gz + @echo ">>> Install BZip2 for $(target)" + mkdir -p $$(BZIP2_INSTALL-$(target)) + cd $$(BZIP2_INSTALL-$(target)) && tar zxvf $(PROJECT_DIR)/downloads/bzip2-$(BZIP2_VERSION)-$(target).tar.gz --exclude="*.dylib" + # Ensure the target is marked as clean. + touch $$(BZIP2_LIB-$(target)) ########################################################################### # Target: XZ (LZMA) ########################################################################### -XZ_SRCDIR-$(target)=build/$(os)/$(target)/xz-$(XZ_VERSION) XZ_INSTALL-$(target)=$(PROJECT_DIR)/install/$(os)/$(target)/xz-$(XZ_VERSION) XZ_LIB-$(target)=$$(XZ_INSTALL-$(target))/lib/liblzma.a -XZ_WHEEL-$(target)=wheels/$(os)/xz-$(XZ_VERSION)-1-$$(WHEEL_TAG-$(target)).whl -XZ_WHEEL_DISTINFO-$(target)=$$(XZ_INSTALL-$(target))/wheel/xz-$(XZ_VERSION).dist-info - -$$(XZ_SRCDIR-$(target))/configure: downloads/xz-$(XZ_VERSION).tar.gz - @echo ">>> Unpack XZ sources for $(target)" - mkdir -p $$(XZ_SRCDIR-$(target)) - tar zxf $$< --strip-components 1 -C $$(XZ_SRCDIR-$(target)) - # Patch the source to add support for new platforms - cd $$(XZ_SRCDIR-$(target)) && patch -p1 < $(PROJECT_DIR)/patch/xz-$(XZ_VERSION).patch - # Touch the configure script to ensure that Make identifies it as up to date. - touch $$(XZ_SRCDIR-$(target))/configure -$$(XZ_SRCDIR-$(target))/Makefile: install/$(os)/bin/$$(TARGET_TRIPLE-$(target))-gcc $$(XZ_SRCDIR-$(target))/configure - # Configure the build - cd $$(XZ_SRCDIR-$(target)) && \ - PATH="$(PROJECT_DIR)/install/$(os)/bin:$(PATH)" \ - ./configure \ - CC=$$(TARGET_TRIPLE-$(target))-gcc \ - CFLAGS="$$(CFLAGS-$(target))" \ - LDFLAGS="$$(LDFLAGS-$(target))" \ - --disable-shared \ - --enable-static \ - --host=$$(TARGET_TRIPLE-$(target)) \ - --build=$(HOST_ARCH)-apple-darwin \ - --prefix="$$(XZ_INSTALL-$(target))" \ - 2>&1 | tee -a ../xz-$(XZ_VERSION).config.log - -$$(XZ_LIB-$(target)): $$(XZ_SRCDIR-$(target))/Makefile - @echo ">>> Build and install XZ for $(target)" - cd $$(XZ_SRCDIR-$(target)) && \ - PATH="$(PROJECT_DIR)/install/$(os)/bin:$(PATH)" \ - make install \ - 2>&1 | tee -a ../xz-$(XZ_VERSION).build.log - -$$(XZ_WHEEL-$(target)): $$(XZ_LIB-$(target)) $$(BDIST_WHEEL) - @echo ">>> Build XZ wheel for $(target)" - mkdir -p $$(XZ_WHEEL_DISTINFO-$(target)) - mkdir -p $$(XZ_INSTALL-$(target))/wheel/opt - - # Copy distributable content - cp -r $$(XZ_INSTALL-$(target))/include $$(XZ_INSTALL-$(target))/wheel/opt/include - cp -r $$(XZ_INSTALL-$(target))/lib $$(XZ_INSTALL-$(target))/wheel/opt/lib - - # Copy license files - cp $$(XZ_SRCDIR-$(target))/COPYING $$(XZ_WHEEL_DISTINFO-$(target))/LICENSE - cp $$(XZ_SRCDIR-$(target))/COPYING.* $$(XZ_WHEEL_DISTINFO-$(target)) - - # Write package metadata - echo "Metadata-Version: 1.2" > $$(XZ_WHEEL_DISTINFO-$(target))/METADATA - echo "Name: xz" >> $$(XZ_WHEEL_DISTINFO-$(target))/METADATA - echo "Version: $(XZ_VERSION)" >> $$(XZ_WHEEL_DISTINFO-$(target))/METADATA - echo "Summary: " >> $$(XZ_WHEEL_DISTINFO-$(target))/METADATA - echo "Download-URL: " >> $$(XZ_WHEEL_DISTINFO-$(target))/METADATA - - # Write wheel metadata - echo "Wheel-Version: 1.0" > $$(XZ_WHEEL_DISTINFO-$(target))/WHEEL - echo "Root-Is-Purelib: false" >> $$(XZ_WHEEL_DISTINFO-$(target))/WHEEL - echo "Generator: Python-Apple-support.BeeWare" >> $$(XZ_WHEEL_DISTINFO-$(target))/WHEEL - echo "Build: 1" >> $$(XZ_WHEEL_DISTINFO-$(target))/WHEEL - echo "Tag: $$(WHEEL_TAG-$(target))" >> $$(XZ_WHEEL_DISTINFO-$(target))/WHEEL - - # Pack the wheel - mkdir -p wheels/$(os) - $(HOST_PYTHON_EXE) -m wheel pack $$(XZ_INSTALL-$(target))/wheel --dest-dir wheels/$(os) --build-number 1 +downloads/xz-$(XZ_VERSION)-$(target).tar.gz: + @echo ">>> Download XZ for $(target)" + mkdir -p downloads + curl $(CURL_FLAGS) -o $$@ \ + https://github.com/beeware/cpython-apple-source-deps/releases/download/XZ-$(XZ_VERSION)/xz-$(XZ_VERSION)-$(target).tar.gz + +$$(XZ_LIB-$(target)): downloads/xz-$(XZ_VERSION)-$(target).tar.gz + @echo ">>> Install XZ for $(target)" + mkdir -p $$(XZ_INSTALL-$(target)) + cd $$(XZ_INSTALL-$(target)) && tar zxvf $(PROJECT_DIR)/downloads/xz-$(XZ_VERSION)-$(target).tar.gz --exclude="*.dylib" + # 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 --exclude="*.dylib" + # Ensure the target is marked as clean. + touch $$(MPDECIMAL_LIB-$(target)) ########################################################################### # Target: OpenSSL ########################################################################### -OPENSSL_SRCDIR-$(target)=build/$(os)/$(target)/openssl-$(OPENSSL_VERSION) OPENSSL_INSTALL-$(target)=$(PROJECT_DIR)/install/$(os)/$(target)/openssl-$(OPENSSL_VERSION) OPENSSL_SSL_LIB-$(target)=$$(OPENSSL_INSTALL-$(target))/lib/libssl.a -OPENSSL_CRYPTO_LIB-$(target)=$$(OPENSSL_INSTALL-$(target))/lib/libcrypto.a -OPENSSL_WHEEL-$(target)=wheels/$(os)/openssl-$(OPENSSL_VERSION)-1-$$(WHEEL_TAG-$(target)).whl -OPENSSL_WHEEL_DISTINFO-$(target)=$$(OPENSSL_INSTALL-$(target))/wheel/openssl-$(OPENSSL_VERSION).dist-info - -$$(OPENSSL_SRCDIR-$(target))/Configure: downloads/openssl-$(OPENSSL_VERSION).tar.gz - @echo ">>> Unpack and configure OpenSSL sources for $(target)" - mkdir -p $$(OPENSSL_SRCDIR-$(target)) - tar zxf $$< --strip-components 1 -C $$(OPENSSL_SRCDIR-$(target)) - -ifneq ($(os),macOS) - # Patch code to disable the use of fork as it's not available on $(os) -ifeq ($(OPENSSL_VERSION_NUMBER),1.1.1) - sed -ie 's/define HAVE_FORK 1/define HAVE_FORK 0/' $$(OPENSSL_SRCDIR-$(target))/apps/speed.c - sed -ie 's/define HAVE_FORK 1/define HAVE_FORK 0/' $$(OPENSSL_SRCDIR-$(target))/apps/ocsp.c -else - sed -ie 's/define HAVE_FORK 1/define HAVE_FORK 0/' $$(OPENSSL_SRCDIR-$(target))/apps/include/http_server.h - sed -ie 's/define HAVE_FORK 1/define HAVE_FORK 0/' $$(OPENSSL_SRCDIR-$(target))/apps/speed.c -endif -endif - # Touch the Configure script to ensure that Make identifies it as up to date. - touch $$(OPENSSL_SRCDIR-$(target))/Configure +downloads/openssl-$(OPENSSL_VERSION)-$(target).tar.gz: + @echo ">>> Download OpenSSL for $(target)" + mkdir -p downloads + curl $(CURL_FLAGS) -o $$@ \ + https://github.com/beeware/cpython-apple-source-deps/releases/download/OpenSSL-$(OPENSSL_VERSION)/openssl-$(OPENSSL_VERSION)-$(target).tar.gz -$$(OPENSSL_SRCDIR-$(target))/is_configured: install/$(os)/bin/$$(TARGET_TRIPLE-$(target))-gcc $$(OPENSSL_SRCDIR-$(target))/Configure - # Configure the OpenSSL build -ifeq ($(os),macOS) - cd $$(OPENSSL_SRCDIR-$(target)) && \ - PATH="$(PROJECT_DIR)/install/$(os)/bin:$(PATH)" \ - CC="$$(TARGET_TRIPLE-$(target))-gcc $$(CFLAGS-$(target))" \ - ./Configure darwin64-$$(ARCH-$(target))-cc no-tests \ - --prefix="$$(OPENSSL_INSTALL-$(target))" \ - --openssldir=/etc/ssl \ - 2>&1 | tee -a ../openssl-$(OPENSSL_VERSION).config.log -else - cd $$(OPENSSL_SRCDIR-$(target)) && \ - PATH="$(PROJECT_DIR)/install/$(os)/bin:$(PATH)" \ - CC="$$(TARGET_TRIPLE-$(target))-gcc $$(CFLAGS-$(target))" \ - CROSS_TOP="$$(dir $$(SDK_ROOT-$(target))).." \ - CROSS_SDK="$$(notdir $$(SDK_ROOT-$(target)))" \ - ./Configure iphoneos-cross no-asm no-tests \ - --prefix="$$(OPENSSL_INSTALL-$(target))" \ - --openssldir=/etc/ssl \ - 2>&1 | tee -a ../openssl-$(OPENSSL_VERSION).config.log -endif - # The OpenSSL Makefile is... interesting. Invoking `make all` or `make - # install` *modifies the Makefile*. Therefore, we can't use the Makefile as - # a build dependency, because building/installing dirties the target that - # was used as a dependency. To compensate, create a dummy file as a marker - # for whether OpenSSL has been configured, and use *that* as a reference. - date > $$(OPENSSL_SRCDIR-$(target))/is_configured - -$$(OPENSSL_SRCDIR-$(target))/libssl.a: $$(OPENSSL_SRCDIR-$(target))/is_configured - @echo ">>> Build OpenSSL for $(target)" - # OpenSSL's `all` target modifies the Makefile; - # use the raw targets that make up all and it's dependencies - cd $$(OPENSSL_SRCDIR-$(target)) && \ - PATH="$(PROJECT_DIR)/install/$(os)/bin:$(PATH)" \ - CC="$$(TARGET_TRIPLE-$(target))-gcc $$(CFLAGS-$(target))" \ - CROSS_TOP="$$(dir $$(SDK_ROOT-$(target))).." \ - CROSS_SDK="$$(notdir $$(SDK_ROOT-$(target)))" \ - make build_sw \ - 2>&1 | tee -a ../openssl-$(OPENSSL_VERSION).build.log - -$$(OPENSSL_SSL_LIB-$(target)): $$(OPENSSL_SRCDIR-$(target))/libssl.a +$$(OPENSSL_SSL_LIB-$(target)): downloads/openssl-$(OPENSSL_VERSION)-$(target).tar.gz @echo ">>> Install OpenSSL for $(target)" - # Install just the software (not the docs) - cd $$(OPENSSL_SRCDIR-$(target)) && \ - PATH="$(PROJECT_DIR)/install/$(os)/bin:$(PATH)" \ - CC="$$(TARGET_TRIPLE-$(target))-gcc $$(CFLAGS-$(target))" \ - CROSS_TOP="$$(dir $$(SDK_ROOT-$(target))).." \ - CROSS_SDK="$$(notdir $$(SDK_ROOT-$(target)))" \ - make install_sw \ - 2>&1 | tee -a ../openssl-$(OPENSSL_VERSION).install.log - -$$(OPENSSL_WHEEL-$(target)): $$(OPENSSL_LIB-$(target)) $$(BDIST_WHEEL) - @echo ">>> Build OpenSSL wheel for $(target)" - mkdir -p $$(OPENSSL_WHEEL_DISTINFO-$(target)) - mkdir -p $$(OPENSSL_INSTALL-$(target))/wheel/opt - - # Copy distributable content - cp -r $$(OPENSSL_INSTALL-$(target))/include $$(OPENSSL_INSTALL-$(target))/wheel/opt/include - cp -r $$(OPENSSL_INSTALL-$(target))/lib $$(OPENSSL_INSTALL-$(target))/wheel/opt/lib - - # Remove dynamic library content - rm -f $$(OPENSSL_INSTALL-$(target))/wheel/opt/lib/*.dylib - - # Copy LICENSE file - # OpenSSL 1.1.1 uses LICENSE; OpenSSL 3 uses LICENSE.txt - if [ -f "$$(OPENSSL_SRCDIR-$(target))/LICENSE.txt" ]; then \ - cp $$(OPENSSL_SRCDIR-$(target))/LICENSE.txt $$(OPENSSL_WHEEL_DISTINFO-$(target))/LICENSE; \ - else \ - cp $$(OPENSSL_SRCDIR-$(target))/LICENSE $$(OPENSSL_WHEEL_DISTINFO-$(target))/LICENSE; \ - fi - - # Write package metadata - echo "Metadata-Version: 1.2" > $$(OPENSSL_WHEEL_DISTINFO-$(target))/METADATA - echo "Name: openssl" >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/METADATA - echo "Version: $(OPENSSL_VERSION)" >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/METADATA - echo "Summary: " >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/METADATA - echo "Download-URL: " >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/METADATA - - # Write wheel metadata - echo "Wheel-Version: 1.0" > $$(OPENSSL_WHEEL_DISTINFO-$(target))/WHEEL - echo "Root-Is-Purelib: false" >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/WHEEL - echo "Generator: Python-Apple-support.BeeWare" >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/WHEEL - echo "Build: 1" >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/WHEEL - echo "Tag: $$(WHEEL_TAG-$(target))" >> $$(OPENSSL_WHEEL_DISTINFO-$(target))/WHEEL - - # Pack the wheel - mkdir -p wheels/$(os) - $(HOST_PYTHON_EXE) -m wheel pack $$(OPENSSL_INSTALL-$(target))/wheel --dest-dir wheels/$(os) --build-number 1 + mkdir -p $$(OPENSSL_INSTALL-$(target)) + cd $$(OPENSSL_INSTALL-$(target)) && tar zxvf $(PROJECT_DIR)/downloads/openssl-$(OPENSSL_VERSION)-$(target).tar.gz --exclude="*.dylib" + # Ensure the target is marked as clean. + touch $$(OPENSSL_SSL_LIB-$(target)) ########################################################################### # Target: libFFI @@ -493,50 +236,21 @@ $$(OPENSSL_WHEEL-$(target)): $$(OPENSSL_LIB-$(target)) $$(BDIST_WHEEL) # The configure step is performed as part of the OS-level build. ifneq ($(os),macOS) -LIBFFI_SRCDIR-$(os)=build/$(os)/libffi-$(LIBFFI_VERSION) -LIBFFI_SRCDIR-$(target)=$$(LIBFFI_SRCDIR-$(os))/build_$$(SDK-$(target))-$$(ARCH-$(target)) -LIBFFI_LIB-$(target)=$$(LIBFFI_SRCDIR-$(target))/.libs/libffi.a -LIBFFI_WHEEL-$(target)=wheels/$(os)/libffi-$(LIBFFI_VERSION)-1-$$(WHEEL_TAG-$(target)).whl -LIBFFI_WHEEL_DISTINFO-$(target)=$$(LIBFFI_SRCDIR-$(target))/wheel/libffi-$(LIBFFI_VERSION).dist-info - -$$(LIBFFI_LIB-$(target)): $$(LIBFFI_SRCDIR-$(os))/darwin_common/include/ffi.h - @echo ">>> Build libFFI for $(target)" - cd $$(LIBFFI_SRCDIR-$(target)) && \ - make \ - 2>&1 | tee -a ../../libffi-$(LIBFFI_VERSION).build.log - -$$(LIBFFI_WHEEL-$(target)): $$(LIBFFI_LIB-$(target)) $$(BDIST_WHEEL) - @echo ">>> Build libFFI wheel for $(target)" - mkdir -p $$(LIBFFI_WHEEL_DISTINFO-$(target)) - mkdir -p $$(LIBFFI_SRCDIR-$(target))/wheel/opt - - # Copy distributable content - cp -r $$(LIBFFI_SRCDIR-$(target))/include $$(LIBFFI_SRCDIR-$(target))/wheel/opt/include - cp -r $$(LIBFFI_SRCDIR-$(target))/.libs $$(LIBFFI_SRCDIR-$(target))/wheel/opt/lib - - # Remove dynamic library content - rm -f $$(LIBFFI_SRCDIR-$(target))/wheel/opt/lib/*.dylib - - # Copy LICENSE file - cp $$(LIBFFI_SRCDIR-$(os))/LICENSE $$(LIBFFI_WHEEL_DISTINFO-$(target)) - - # Write package metadata - echo "Metadata-Version: 1.2" > $$(LIBFFI_WHEEL_DISTINFO-$(target))/METADATA - echo "Name: libffi" >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/METADATA - echo "Version: $(LIBFFI_VERSION)" >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/METADATA - echo "Summary: " >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/METADATA - echo "Download-URL: " >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/METADATA - - # Write wheel metadata - echo "Wheel-Version: 1.0" > $$(LIBFFI_WHEEL_DISTINFO-$(target))/WHEEL - echo "Root-Is-Purelib: false" >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/WHEEL - echo "Generator: Python-Apple-support.BeeWare" >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/WHEEL - echo "Build: 1" >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/WHEEL - echo "Tag: $$(WHEEL_TAG-$(target))" >> $$(LIBFFI_WHEEL_DISTINFO-$(target))/WHEEL - - # Pack the wheel - mkdir -p wheels/$(os) - $(HOST_PYTHON_EXE) -m wheel pack $$(LIBFFI_SRCDIR-$(target))/wheel --dest-dir wheels/$(os) --build-number 1 +LIBFFI_INSTALL-$(target)=$(PROJECT_DIR)/install/$(os)/$(target)/libffi-$(LIBFFI_VERSION) +LIBFFI_LIB-$(target)=$$(LIBFFI_INSTALL-$(target))/lib/libffi.a + +downloads/libffi-$(LIBFFI_VERSION)-$(target).tar.gz: + @echo ">>> Download libFFI for $(target)" + mkdir -p downloads + curl $(CURL_FLAGS) -o $$@ \ + https://github.com/beeware/cpython-apple-source-deps/releases/download/libFFI-$(LIBFFI_VERSION)/libffi-$(LIBFFI_VERSION)-$(target).tar.gz + +$$(LIBFFI_LIB-$(target)): downloads/libffi-$(LIBFFI_VERSION)-$(target).tar.gz + @echo ">>> Install libFFI for $(target)" + mkdir -p $$(LIBFFI_INSTALL-$(target)) + cd $$(LIBFFI_INSTALL-$(target)) && tar zxvf $(PROJECT_DIR)/downloads/libffi-$(LIBFFI_VERSION)-$(target).tar.gz --exclude="*.dylib" + # Ensure the target is marked as clean. + touch $$(LIBFFI_LIB-$(target)) endif @@ -551,83 +265,101 @@ 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_PLATFORM_CONFIG-$(target)=$$(PYTHON_INSTALL-$(target))/platform-config/$$(ARCH-$(target))-$$(SDK-$(target)) +PYTHON_PLATFORM_SITECUSTOMIZE-$(target)=$$(PYTHON_PLATFORM_CONFIG-$(target))/sitecustomize.py + $$(PYTHON_SRCDIR-$(target))/configure: \ downloads/Python-$(PYTHON_VERSION).tar.gz \ - $$(BZIP2_FATLIB-$$(SDK-$(target))) \ - $$(XZ_FATLIB-$$(SDK-$(target))) \ - $$(OPENSSL_FATINCLUDE-$$(SDK-$(target))) $$(OPENSSL_SSL_FATLIB-$$(SDK-$(target))) $$(OPENSSL_CRYPTO_FATLIB-$$(SDK-$(target))) \ - $$(LIBFFI_FATLIB-$$(SDK-$(target))) \ - $$(PYTHON_LIB-macOS) + $$(BZIP2_LIB-$(target)) \ + $$(LIBFFI_LIB-$(target)) \ + $$(MPDECIMAL_LIB-$(target)) \ + $$(OPENSSL_SSL_LIB-$(target)) \ + $$(XZ_LIB-$(target)) @echo ">>> Unpack and configure Python for $(target)" mkdir -p $$(PYTHON_SRCDIR-$(target)) tar zxf downloads/Python-$(PYTHON_VERSION).tar.gz --strip-components 1 -C $$(PYTHON_SRCDIR-$(target)) # 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: \ - install/$(os)/bin/$$(TARGET_TRIPLE-$$(SDK-$(target)))-ar \ - install/$(os)/bin/$$(TARGET_TRIPLE-$(target))-gcc \ - install/$(os)/bin/$$(TARGET_TRIPLE-$(target))-cpp \ $$(PYTHON_SRCDIR-$(target))/configure # Configure target Python cd $$(PYTHON_SRCDIR-$(target)) && \ - PATH="$(PROJECT_DIR)/install/$(os)/bin:$(PATH)" \ + PATH="$(PROJECT_DIR)/$$(PYTHON_SRCDIR-$(target))/$(os)/Resources/bin:$(PATH)" \ ./configure \ - AR=$$(TARGET_TRIPLE-$$(SDK-$(target)))-ar \ - CC=$$(TARGET_TRIPLE-$(target))-gcc \ - CPP=$$(TARGET_TRIPLE-$(target))-cpp \ - CXX=$$(TARGET_TRIPLE-$(target))-gcc \ - CFLAGS="$$(CFLAGS-$(target))" \ - LDFLAGS="$$(LDFLAGS-$(target))" \ - LIBLZMA_CFLAGS="-I$$(XZ_MERGE-$$(SDK-$(target)))/include" \ - LIBLZMA_LIBS="-L$$(XZ_MERGE-$$(SDK-$(target)))/lib -llzma" \ - BZIP2_CFLAGS="-I$$(BZIP2_MERGE-$$(SDK-$(target)))/include" \ - BZIP2_LIBS="-L$$(BZIP2_MERGE-$$(SDK-$(target)))/lib -lbz2" \ - LIBFFI_CFLAGS="-I$$(LIBFFI_MERGE-$$(SDK-$(target)))/include" \ - LIBFFI_LIBS="-L$$(LIBFFI_MERGE-$$(SDK-$(target)))/lib -lffi" \ + LIBLZMA_CFLAGS="-I$$(XZ_INSTALL-$(target))/include" \ + LIBLZMA_LIBS="-L$$(XZ_INSTALL-$(target))/lib -llzma" \ + BZIP2_CFLAGS="-I$$(BZIP2_INSTALL-$(target))/include" \ + BZIP2_LIBS="-L$$(BZIP2_INSTALL-$(target))/lib -lbz2" \ + 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=$$(PYTHON_INSTALL-macosx)/bin/python$(PYTHON_VER) \ - --prefix="$$(PYTHON_INSTALL-$(target))" \ + --with-build-python=$(HOST_PYTHON) \ --enable-ipv6 \ - --with-openssl="$$(OPENSSL_MERGE-$$(SDK-$(target)))" \ - --without-ensurepip \ - ac_cv_file__dev_ptmx=no \ - ac_cv_file__dev_ptc=no \ - $$(PYTHON_CONFIGURE-$(os)) \ + --with-openssl="$$(OPENSSL_INSTALL-$(target))" \ + --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)/install/$(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)/install/$(os)/bin:$(PATH)" \ + PATH="$(PROJECT_DIR)/$$(PYTHON_SRCDIR-$(target))/$(os)/Resources/bin:$(PATH)" \ make install \ 2>&1 | tee -a ../python-$(PYTHON_VERSION).install.log -endif - -PYTHON_SITECUSTOMIZE-$(target)=$(PROJECT_DIR)/support/$(PYTHON_VER)/$(os)/platform-site/$(target)/sitecustomize.py - -$$(PYTHON_SITECUSTOMIZE-$(target)): - @echo ">>> Create cross-platform sitecustomize.py for $(target)" - mkdir -p $$(dir $$(PYTHON_SITECUSTOMIZE-$(target))) - cat $(PROJECT_DIR)/patch/Python/sitecustomize.$(os).py \ + # Remove any .orig files produced by the compliance patching process + find $$(PYTHON_INSTALL-$(target)) -name "*.orig" -exec rm {} \; + + +$$(PYTHON_PLATFORM_SITECUSTOMIZE-$(target)): + @echo ">>> Create cross-plaform config for $(target)" + mkdir -p $$(PYTHON_PLATFORM_CONFIG-$(target)) + # Create the cross-platform site definition + echo "import _cross_$$(ARCH-$(target))_$$(SDK-$(target)); import _cross_venv;" \ + > $$(PYTHON_PLATFORM_CONFIG-$(target))/_cross_venv.pth + cp $(PROJECT_DIR)/patch/Python/make_cross_venv.py \ + $$(PYTHON_PLATFORM_CONFIG-$(target))/make_cross_venv.py + cp $(PROJECT_DIR)/patch/Python/_cross_venv.py \ + $$(PYTHON_PLATFORM_CONFIG-$(target))/_cross_venv.py + cp $$(PYTHON_STDLIB-$(target))/_sysconfig* \ + $$(PYTHON_PLATFORM_CONFIG-$(target)) + cat $(PROJECT_DIR)/patch/Python/_cross_target.py.tmpl \ | sed -e "s/{{os}}/$(os)/g" \ + | sed -e "s/{{platform}}/$$(OS_LOWER-$(target))/g" \ + | sed -e "s/{{arch}}/$$(ARCH-$(target))/g" \ + | sed -e "s/{{sdk}}/$$(SDK-$(target))/g" \ + | sed -e "s/{{version_min}}/$$(VERSION_MIN-$(os))/g" \ + | sed -e "s/{{is_simulator}}/$$(IS_SIMULATOR-$(target))/g" \ + > $$(PYTHON_PLATFORM_CONFIG-$(target))/_cross_$$(ARCH-$(target))_$$(SDK-$(target)).py + cat $(PROJECT_DIR)/patch/Python/sitecustomize.py.tmpl \ | sed -e "s/{{arch}}/$$(ARCH-$(target))/g" \ - | sed -e "s/{{tag}}/$$(OS_LOWER-$(target))-$$(VERSION_MIN-$(os))-$$(SDK-$(target))-$$(ARCH-$(target))/g" \ - > $$(PYTHON_SITECUSTOMIZE-$(target)) + | sed -e "s/{{sdk}}/$$(SDK-$(target))/g" \ + > $$(PYTHON_PLATFORM_SITECUSTOMIZE-$(target)) + +endif +$(target): $$(PYTHON_PLATFORM_SITECUSTOMIZE-$(target)) $$(PYTHON_LIB-$(target)) ########################################################################### # Target: Debug @@ -639,31 +371,25 @@ 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_SRCDIR-$(target): $$(BZIP2_SRCDIR-$(target))" @echo "BZIP2_INSTALL-$(target): $$(BZIP2_INSTALL-$(target))" @echo "BZIP2_LIB-$(target): $$(BZIP2_LIB-$(target))" - @echo "BZIP2_WHEEL-$(target): $$(BZIP2_WHEEL-$(target))" - @echo "BZIP2_WHEEL_DISTINFO-$(target): $$(BZIP2_WHEEL_DISTINFO-$(target))" - @echo "XZ_SRCDIR-$(target): $$(XZ_SRCDIR-$(target))" - @echo "XZ_INSTALL-$(target): $$(XZ_INSTALL-$(target))" - @echo "XZ_LIB-$(target): $$(XZ_LIB-$(target))" - @echo "XZ_WHEEL-$(target): $$(XZ_WHEEL-$(target))" - @echo "XZ_WHEEL_DISTINFO-$(target): $$(XZ_WHEEL_DISTINFO-$(target))" - @echo "OPENSSL_SRCDIR-$(target): $$(OPENSSL_SRCDIR-$(target))" + @echo "LIBFFI_INSTALL-$(target): $$(LIBFFI_INSTALL-$(target))" + @echo "LIBFFI_LIB-$(target): $$(LIBFFI_LIB-$(target))" + @echo "MPDECIMAL_INSTALL-$(target): $$(MPDECIMAL_INSTALL-$(target))" + @echo "MPDECIMAL_LIB-$(target): $$(MPDECIMAL_LIB-$(target))" @echo "OPENSSL_INSTALL-$(target): $$(OPENSSL_INSTALL-$(target))" @echo "OPENSSL_SSL_LIB-$(target): $$(OPENSSL_SSL_LIB-$(target))" - @echo "OPENSSL_CRYPTO_LIB-$(target): $$(OPENSSL_CRYPTO_LIB-$(target))" - @echo "OPENSSL_WHEEL-$(target): $$(OPENSSL_WHEEL-$(target))" - @echo "OPENSSL_WHEEL_DISTINFO-$(target): $$(OPENSSL_WHEEL_DISTINFO-$(target))" - @echo "LIBFFI_SRCDIR-$(target): $$(LIBFFI_SRCDIR-$(target))" - @echo "LIBFFI_LIB-$(target): $$(LIBFFI_LIB-$(target))" - @echo "LIBFFI_WHEEL-$(target): $$(LIBFFI_WHEEL-$(target))" - @echo "LIBFFI_WHEEL_DISTINFO-$(target): $$(LIBFFI_WHEEL_DISTINFO-$(target))" + @echo "XZ_INSTALL-$(target): $$(XZ_INSTALL-$(target))" + @echo "XZ_LIB-$(target): $$(XZ_LIB-$(target))" @echo "PYTHON_SRCDIR-$(target): $$(PYTHON_SRCDIR-$(target))" @echo "PYTHON_INSTALL-$(target): $$(PYTHON_INSTALL-$(target))" + @echo "PYTHON_FRAMEWORK-$(target): $$(PYTHON_FRAMEWORK-$(target))" @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 "PYTHON_PLATFORM_CONFIG-$(target): $$(PYTHON_PLATFORM_CONFIG-$(target))" + @echo "PYTHON_PLATFORM_SITECUSTOMIZE-$(target): $$(PYTHON_PLATFORM_SITECUSTOMIZE-$(target))" @echo endef # build-target @@ -681,255 +407,136 @@ define build-sdk sdk=$1 os=$2 -OS_LOWER-$(sdk)=$(shell echo $(os) | tr '[:upper:]' '[:lower:]') - SDK_TARGETS-$(sdk)=$$(filter $(sdk).%,$$(TARGETS-$(os))) SDK_ARCHES-$(sdk)=$$(sort $$(subst .,,$$(suffix $$(SDK_TARGETS-$(sdk))))) ifeq ($$(findstring simulator,$(sdk)),) -SDK_SLICE-$(sdk)=$$(OS_LOWER-$(sdk))-$$(shell echo $$(SDK_ARCHES-$(sdk)) | sed "s/ /_/g") -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 - -BZIP2_MERGE-$(sdk)=$(PROJECT_DIR)/merge/$(os)/$(sdk)/bzip2-$(BZIP2_VERSION) -BZIP2_FATLIB-$(sdk)=$$(BZIP2_MERGE-$(sdk))/lib/libbz2.a - -XZ_MERGE-$(sdk)=$(PROJECT_DIR)/merge/$(os)/$(sdk)/xz-$(XZ_VERSION) -XZ_FATLIB-$(sdk)=$$(XZ_MERGE-$(sdk))/lib/liblzma.a - -OPENSSL_MERGE-$(sdk)=$(PROJECT_DIR)/merge/$(os)/$(sdk)/openssl-$(OPENSSL_VERSION) -OPENSSL_FATINCLUDE-$(sdk)=$$(OPENSSL_MERGE-$(sdk))/include -OPENSSL_SSL_FATLIB-$(sdk)=$$(OPENSSL_MERGE-$(sdk))/lib/libssl.a -OPENSSL_CRYPTO_FATLIB-$(sdk)=$$(OPENSSL_MERGE-$(sdk))/lib/libcrypto.a - -LIBFFI_MERGE-$(sdk)=$(PROJECT_DIR)/merge/$(os)/$(sdk)/libffi-$(LIBFFI_VERSION) -LIBFFI_FATLIB-$(sdk)=$$(LIBFFI_MERGE-$(sdk))/lib/libffi.a - -PYTHON_MERGE-$(sdk)=$(PROJECT_DIR)/merge/$(os)/$(sdk)/python-$(PYTHON_VERSION) -PYTHON_FATLIB-$(sdk)=$$(PYTHON_MERGE-$(sdk))/libPython$(PYTHON_VER).a -PYTHON_FATINCLUDE-$(sdk)=$$(PYTHON_MERGE-$(sdk))/Headers -PYTHON_FATSTDLIB-$(sdk)=$$(PYTHON_MERGE-$(sdk))/python-stdlib - -ifeq ($(os),macOS) -TARGET_TRIPLE-$(sdk)=apple-darwin +SDK_SLICE-$(sdk)=$$(TRIPLE_OS-$(os))-$$(shell echo $$(SDK_ARCHES-$(sdk)) | sed "s/ /_/g") 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 +SDK_SLICE-$(sdk)=$$(TRIPLE_OS-$(os))-$$(shell echo $$(SDK_ARCHES-$(sdk)) | sed "s/ /_/g")-simulator endif # Expand the build-target macro for target on this OS $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(eval $$(call build-target,$$(target),$(os)))) -########################################################################### -# SDK: Aliases -########################################################################### - -install/$(os)/bin/$$(TARGET_TRIPLE-$(sdk))-gcc: - patch/make-xcrun-alias $$@ "--sdk $(sdk) clang" - -install/$(os)/bin/$$(TARGET_TRIPLE-$(sdk))-cpp: - patch/make-xcrun-alias $$@ "--sdk $(sdk) clang -E" - -install/$(os)/bin/$$(TARGET_TRIPLE-$(sdk))-ar: - patch/make-xcrun-alias $$@ "--sdk $(sdk) ar" - -########################################################################### -# SDK: BZip2 -########################################################################### - -$$(BZIP2_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(BZIP2_LIB-$$(target))) - @echo ">>> Build BZip2 fat library for $(sdk)" - mkdir -p $$(BZIP2_MERGE-$(sdk))/lib - lipo -create -output $$@ $$^ \ - 2>&1 | tee -a merge/$(os)/$(sdk)/bzip2-$(BZIP2_VERSION).lipo.log - # Copy headers from the first target associated with the $(sdk) SDK - cp -r $$(BZIP2_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/include $$(BZIP2_MERGE-$(sdk)) - -########################################################################### -# SDK: XZ (LZMA) -########################################################################### - -$$(XZ_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(XZ_LIB-$$(target))) - @echo ">>> Build XZ fat library for $(sdk)" - mkdir -p $$(XZ_MERGE-$(sdk))/lib - lipo -create -output $$@ $$^ \ - 2>&1 | tee -a merge/$(os)/$(sdk)/xz-$(XZ_VERSION).lipo.log - # Copy headers from the first target associated with the $(sdk) SDK - cp -r $$(XZ_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/include $$(XZ_MERGE-$(sdk)) - -########################################################################### -# SDK: OpenSSL -########################################################################### - -$$(OPENSSL_FATINCLUDE-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_SSL_LIB-$$(target))) - @echo ">>> Copy OpenSSL headers from the first target associated with the SDK" - mkdir -p $$(OPENSSL_MERGE-$(sdk)) - cp -r $$(OPENSSL_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/include $$(OPENSSL_MERGE-$(sdk)) - -$$(OPENSSL_SSL_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_SSL_LIB-$$(target))) - @echo ">>> Build OpenSSL ssl fat library for $(sdk)" - mkdir -p $$(OPENSSL_MERGE-$(sdk))/lib - lipo -create -output $$@ \ - $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_SSL_LIB-$$(target))) \ - 2>&1 | tee -a merge/$(os)/$(sdk)/openssl-$(OPENSSL_VERSION).ssl.lipo.log - -$$(OPENSSL_CRYPTO_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_CRYPTO_LIB-$$(target))) - @echo ">>> Build OpenSSL crypto fat library for $(sdk)" - mkdir -p $$(OPENSSL_MERGE-$(sdk))/lib - lipo -create -output $$@ \ - $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(OPENSSL_CRYPTO_LIB-$$(target))) \ - 2>&1 | tee -a merge/$(os)/$(sdk)/openssl-$(OPENSSL_VERSION).crypto.lipo.log - -########################################################################### -# SDK: libFFI -########################################################################### - -$$(LIBFFI_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(LIBFFI_LIB-$$(target))) - @echo ">>> Build libFFI fat library for $(sdk)" - mkdir -p $$(LIBFFI_MERGE-$(sdk))/lib - lipo -create -output $$@ $$^ \ - 2>&1 | tee -a merge/$(os)/$(sdk)/libffi-$(LIBFFI_VERSION).lipo.log - # Copy headers from the first target associated with the $(sdk) SDK - cp -f -r $$(LIBFFI_SRCDIR-$(os))/darwin_common/include \ - $$(LIBFFI_MERGE-$(sdk)) - cp -f -r $$(LIBFFI_SRCDIR-$(os))/darwin_$$(OS_LOWER-$(sdk))/include/* \ - $$(LIBFFI_MERGE-$(sdk))/include - ########################################################################### # 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) +# macOS builds are extracted from the official installer package, then +# reprocessed into an XCFramework. -PYTHON_SRCDIR-$(sdk)=build/$(os)/$(sdk)/python-$(PYTHON_VERSION) PYTHON_INSTALL-$(sdk)=$(PROJECT_DIR)/install/$(os)/$(sdk)/python-$(PYTHON_VERSION) -PYTHON_LIB-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/lib/libpython$(PYTHON_VER).a - -$$(PYTHON_SRCDIR-$(sdk))/configure: \ - $$(BZIP2_FATLIB-$$(sdk)) \ - $$(XZ_FATLIB-$$(sdk)) \ - $$(OPENSSL_FATINCLUDE-$$(sdk)) $$(OPENSSL_SSL_FATLIB-$$(sdk)) $$(OPENSSL_CRYPTO_FATLIB-$$(sdk)) \ - 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: \ - install/$(os)/bin/$$(TARGET_TRIPLE-$(sdk))-gcc \ - install/$(os)/bin/$$(TARGET_TRIPLE-$(sdk))-cpp \ - $$(PYTHON_SRCDIR-$(sdk))/configure - # Configure target Python - cd $$(PYTHON_SRCDIR-$(sdk)) && \ - PATH="$(PROJECT_DIR)/install/$(os)/bin:$(PATH)" \ - ./configure \ - CC=$$(TARGET_TRIPLE-$(sdk))-gcc \ - CPP=$$(TARGET_TRIPLE-$(sdk))-cpp \ - CFLAGS="$$(CFLAGS-$(sdk))" \ - LDFLAGS="$$(LDFLAGS-$(sdk))" \ - LIBLZMA_CFLAGS="-I$$(XZ_MERGE-$(sdk))/include" \ - LIBLZMA_LIBS="-L$$(XZ_MERGE-$(sdk))/lib -llzma" \ - BZIP2_CFLAGS="-I$$(BZIP2_MERGE-$(sdk))/include" \ - BZIP2_LIBS="-L$$(BZIP2_MERGE-$(sdk))/lib -lbz2" \ - MACOSX_DEPLOYMENT_TARGET="$$(VERSION_MIN-$(os))" \ - --prefix="$$(PYTHON_INSTALL-$(sdk))" \ - --enable-ipv6 \ - --enable-universalsdk \ - --with-openssl="$$(OPENSSL_MERGE-$(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)/install/$(os)/bin:$(PATH)" \ - make all \ - 2>&1 | tee -a ../python-$(PYTHON_VERSION).build.log - -$(HOST_PYTHON_EXE) $$(PYTHON_LIB-$(sdk)): $$(PYTHON_SRCDIR-$(sdk))/python.exe - @echo ">>> Install Python for $(sdk)" - cd $$(PYTHON_SRCDIR-$(sdk)) && \ - make install \ - 2>&1 | tee -a ../python-$(PYTHON_VERSION).install.log - -$$(PYTHON_FATLIB-$(sdk)): $$(PYTHON_LIB-$(sdk)) - @echo ">>> Build Python fat library for $(sdk)" - mkdir -p $$(dir $$(PYTHON_FATLIB-$(sdk))) - # The macosx static library is already fat; copy it as-is - cp $$(PYTHON_LIB-$(sdk)) $$(PYTHON_FATLIB-$(sdk)) - -$$(PYTHON_FATINCLUDE-$(sdk)): $$(PYTHON_LIB-$(sdk)) - @echo ">>> Build Python fat library for $(sdk)" - # The macosx headers are already fat; copy as-is - cp -r $$(PYTHON_INSTALL-$(sdk))/include/python$(PYTHON_VER) $$(PYTHON_FATINCLUDE-$(sdk)) - -$$(PYTHON_FATSTDLIB-$(sdk)): $$(PYTHON_LIB-$(sdk)) - @echo ">>> Build Python stdlib library for $(sdk)" - # The macosx stdlib is already fat; copy it as-is - cp -r $$(PYTHON_INSTALL-$(sdk))/lib/python$(PYTHON_VER) $$(PYTHON_FATSTDLIB-$(sdk)) +PYTHON_FRAMEWORK-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/Python.framework +PYTHON_INSTALL_VERSION-$(sdk)=$$(PYTHON_FRAMEWORK-$(sdk))/Versions/$(PYTHON_VER) +PYTHON_LIB-$(sdk)=$$(PYTHON_INSTALL_VERSION-$(sdk))/Python +PYTHON_INCLUDE-$(sdk)=$$(PYTHON_INSTALL_VERSION-$(sdk))/include/python$(PYTHON_VER) +PYTHON_MODULEMAP-$(sdk)=$$(PYTHON_INCLUDE-$(sdk))/module.modulemap +PYTHON_STDLIB-$(sdk)=$$(PYTHON_INSTALL_VERSION-$(sdk))/lib/python$(PYTHON_VER) else - # 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_FATLIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_LIB-$$(target))) +PYTHON_INSTALL-$(sdk)=$(PROJECT_DIR)/install/$(os)/$(sdk)/python-$(PYTHON_VERSION) +PYTHON_MODULEMAP-$(sdk)=$$(PYTHON_INCLUDE-$(sdk))/module.modulemap +PYTHON_FRAMEWORK-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/Python.framework +PYTHON_LIB-$(sdk)=$$(PYTHON_FRAMEWORK-$(sdk))/Python +PYTHON_BIN-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/bin +PYTHON_INCLUDE-$(sdk)=$$(PYTHON_FRAMEWORK-$(sdk))/Headers +PYTHON_STDLIB-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/lib/python$(PYTHON_VER) +PYTHON_PLATFORM_CONFIG-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/platform-config + +$$(PYTHON_LIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_LIB-$$(target))) @echo ">>> Build Python fat library for the $(sdk) SDK" - mkdir -p $$(dir $$(PYTHON_FATLIB-$(sdk))) + mkdir -p $$(dir $$(PYTHON_LIB-$(sdk))) lipo -create -output $$@ $$^ \ - 2>&1 | tee -a merge/$(os)/$(sdk)/python-$(PYTHON_VERSION).lipo.log + 2>&1 | tee -a install/$(os)/$(sdk)/python-$(PYTHON_VERSION).lipo.log + # Disable dSYM production (for now) + # dsymutil $$@ -o $$(PYTHON_INSTALL-$(sdk))/Python.dSYM + +$$(PYTHON_FRAMEWORK-$(sdk))/Info.plist: $$(PYTHON_LIB-$(sdk)) + @echo ">>> Install Info.plist for the $(sdk) SDK" + # 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_FATINCLUDE-$(sdk)): $$(PYTHON_LIB-$(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 - cp -r $$(PYTHON_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/include/python$(PYTHON_VER) $$(PYTHON_FATINCLUDE-$(sdk)) - # Copy the cross-target header from the patch folder - cp $(PROJECT_DIR)/patch/Python/pyconfig-$(os).h $$(PYTHON_FATINCLUDE-$(sdk))/pyconfig.h + cp -r $$(PYTHON_INCLUDE-$$(firstword $$(SDK_TARGETS-$(sdk)))) $$(PYTHON_INCLUDE-$(sdk)) + + # Create the modulemap file + cp -r patch/Python/module.modulemap.prefix $$(PYTHON_MODULEMAP-$(sdk)) + echo "" >> $$(PYTHON_MODULEMAP-$(sdk)) + cd $$(PYTHON_SRCDIR-$$(firstword $$(SDK_TARGETS-$(sdk))))/Include && \ + find cpython -name "*.h" | sort | sed -e 's/^/ exclude header "/' | sed 's/$$$$/"/' >> $$(PYTHON_MODULEMAP-$(sdk)) && \ + echo "" >> $$(PYTHON_MODULEMAP-$(sdk)) && \ + find internal -name "*.h" | sort | sed -e 's/^/ exclude header "/' | sed 's/$$$$/"/' >> $$(PYTHON_MODULEMAP-$(sdk)) + echo "\n}" >> $$(PYTHON_MODULEMAP-$(sdk)) + + # Link the PYTHONHOME version of the headers + mkdir -p $$(PYTHON_INSTALL-$(sdk))/include + ln -si ../Python.framework/Headers $$(PYTHON_INSTALL-$(sdk))/include/python$(PYTHON_VER) + +ifeq ($(os), visionOS) + echo "Skipping arch-specific header copying for visionOS" +else # Add the individual headers from each target in an arch-specific name - $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_INSTALL-$$(target))/include/python$(PYTHON_VER)/pyconfig.h $$(PYTHON_FATINCLUDE-$(sdk))/pyconfig-$$(ARCH-$$(target)).h; ) + $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_INCLUDE-$$(target))/pyconfig.h $$(PYTHON_INCLUDE-$(sdk))/pyconfig-$$(ARCH-$$(target)).h; ) -$$(PYTHON_FATSTDLIB-$(sdk)): $$(PYTHON_FATLIB-$(sdk)) + # Copy the cross-target header from the source folder of the first target in the $(sdk) SDK + cp $$(PYTHON_SRCDIR-$$(firstword $$(SDK_TARGETS-$(sdk))))/$(os)/Resources/pyconfig.h $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h +endif + + +$$(PYTHON_STDLIB-$(sdk))/LICENSE.TXT: $$(PYTHON_LIB-$(sdk)) $$(PYTHON_FRAMEWORK-$(sdk))/Info.plist $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_PLATFORM_SITECUSTOMIZE-$$(target))) @echo ">>> Build Python stdlib for the $(sdk) SDK" - mkdir -p $$(PYTHON_FATSTDLIB-$(sdk))/lib-dynload + mkdir -p $$(PYTHON_STDLIB-$(sdk))/lib-dynload # Copy stdlib from the first target associated with the $(sdk) SDK - cp -r $$(PYTHON_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/lib/python$(PYTHON_VER)/ $$(PYTHON_FATSTDLIB-$(sdk)) + cp -r $$(PYTHON_STDLIB-$$(firstword $$(SDK_TARGETS-$(sdk))))/ $$(PYTHON_STDLIB-$(sdk)) # Delete the single-SDK parts of the standard library rm -rf \ - $$(PYTHON_FATSTDLIB-$(sdk))/_sysconfigdata__*.py \ - $$(PYTHON_FATSTDLIB-$(sdk))/config-* \ - $$(PYTHON_FATSTDLIB-$(sdk))/lib-dynload/* + $$(PYTHON_STDLIB-$(sdk))/_sysconfigdata__*.py \ + $$(PYTHON_STDLIB-$(sdk))/_sysconfig_vars__*.json \ + $$(PYTHON_STDLIB-$(sdk))/config-* \ + $$(PYTHON_STDLIB-$(sdk))/lib-dynload/* # Copy the individual _sysconfigdata modules into names that include the architecture - $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_INSTALL-$$(target))/lib/python$(PYTHON_VER)/_sysconfigdata_* $$(PYTHON_FATSTDLIB-$(sdk))/; ) + $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_STDLIB-$$(target))/_sysconfigdata_* $$(PYTHON_STDLIB-$(sdk))/; ) + $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_STDLIB-$$(target))/_sysconfig_vars_* $$(PYTHON_STDLIB-$(sdk))/; ) - # Copy the individual config modules directories into names that include the architecture - $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp -r $$(PYTHON_INSTALL-$$(target))/lib/python$(PYTHON_VER)/config-$(PYTHON_VER)-$(sdk)-$$(ARCH-$$(target)) $$(PYTHON_FATSTDLIB-$(sdk))/; ) + # Copy the platform site folders for each architecture + mkdir -p $$(PYTHON_PLATFORM_CONFIG-$(sdk)) + $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp -r $$(PYTHON_PLATFORM_CONFIG-$$(target)) $$(PYTHON_PLATFORM_CONFIG-$(sdk)); ) # Merge the binary modules from each target in the $(sdk) SDK into a single binary - $$(foreach module,$$(wildcard $$(PYTHON_INSTALL-$$(firstword $$(SDK_TARGETS-$(sdk))))/lib/python$(PYTHON_VER)/lib-dynload/*),lipo -create -output $$(PYTHON_FATSTDLIB-$(sdk))/lib-dynload/$$(notdir $$(module)) $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_INSTALL-$$(target))/lib/python$(PYTHON_VER)/lib-dynload/$$(notdir $$(module))); ) + $$(foreach module,$$(wildcard $$(PYTHON_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))); ) + # # Disable dSYM production (for now) + # # Create dSYM files for each module + # $$(foreach module,$$(wildcard $$(PYTHON_STDLIB-$$(firstword $$(SDK_TARGETS-$(sdk))))/lib-dynload/*),dsymutil $$(PYTHON_STDLIB-$(sdk))/lib-dynload/$$(notdir $$(module)); ) + + # Copy in known-required xcprivacy files. + # Libraries linking OpenSSL must provide a privacy manifest. The one in this repository + # has been sourced from https://github.com/openssl/openssl/blob/openssl-3.0/os-dep/Apple/PrivacyInfo.xcprivacy + cp $(PROJECT_DIR)/patch/Python/OpenSSL.xcprivacy $$(PYTHON_STDLIB-$(sdk))/lib-dynload/_hashlib.xcprivacy + cp $(PROJECT_DIR)/patch/Python/OpenSSL.xcprivacy $$(PYTHON_STDLIB-$(sdk))/lib-dynload/_ssl.xcprivacy endif +$(sdk): $$(PYTHON_STDLIB-$(sdk))/LICENSE.TXT ########################################################################### # SDK: Debug @@ -940,25 +547,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 "BZIP2_MERGE-$(sdk): $$(BZIP2_MERGE-$(sdk))" - @echo "BZIP2_FATLIB-$(sdk): $$(BZIP2_FATLIB-$(sdk))" - @echo "XZ_MERGE-$(sdk): $$(XZ_MERGE-$(sdk))" - @echo "XZ_FATLIB-$(sdk): $$(XZ_FATLIB-$(sdk))" - @echo "OPENSSL_MERGE-$(sdk): $$(OPENSSL_MERGE-$(sdk))" - @echo "OPENSSL_FATINCLUDE-$(sdk): $$(OPENSSL_FATINCLUDE-$(sdk))" - @echo "OPENSSL_SSL_FATLIB-$(sdk): $$(OPENSSL_SSL_FATLIB-$(sdk))" - @echo "OPENSSL_CRYPTO_FATLIB-$(sdk): $$(OPENSSL_CRYPTO_FATLIB-$(sdk))" - @echo "LIBFFI_MERGE-$(sdk): $$(LIBFFI_MERGE-$(sdk))" - @echo "LIBFFI_FATLIB-$(sdk): $$(LIBFFI_FATLIB-$(sdk))" - @echo "PYTHON_MERGE-$(sdk): $$(PYTHON_MERGE-$(sdk))" - @echo "PYTHON_FATLIB-$(sdk): $$(PYTHON_FATLIB-$(sdk))" - @echo "PYTHON_FATINCLUDE-$(sdk): $$(PYTHON_FATINCLUDE-$(sdk))" - @echo "PYTHON_FATSTDLIB-$(sdk): $$(PYTHON_FATSTDLIB-$(sdk))" - @echo "PYTHON_SRCDIR-$(sdk): $$(PYTHON_SRCDIR-$(sdk))" @echo "PYTHON_INSTALL-$(sdk): $$(PYTHON_INSTALL-$(sdk))" + @echo "PYTHON_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 @@ -980,210 +576,149 @@ 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)))) ########################################################################### -# Build: BZip2 -########################################################################### - -BZip2-$(os): $$(foreach sdk,$$(SDKS-$(os)),$$(BZIP2_FATLIB-$$(sdk))) -BZip2-$(os)-wheels: $$(foreach target,$$(TARGETS-$(os)),$$(BZIP2_WHEEL-$$(target))) - -clean-BZip2-$(os): - @echo ">>> Clean BZip2 build products on $(os)" - rm -rf \ - build/$(os)/*/bzip2-$(BZIP2_VERSION) \ - build/$(os)/*/bzip2-$(BZIP2_VERSION).*.log \ - install/$(os)/*/bzip2-$(BZIP2_VERSION) \ - install/$(os)/*/bzip2-$(BZIP2_VERSION).*.log \ - merge/$(os)/*/bzip2-$(BZIP2_VERSION) \ - merge/$(os)/*/bzip2-$(BZIP2_VERSION).*.log \ - wheels/$(os)/bzip2-$(BZIP2_VERSION)-* - -clean-BZip2-$(os)-wheels: - rm -rf \ - install/$(os)/*/bzip2-$(BZIP2_VERSION)/wheel \ - wheels/$(os)/bzip2-$(BZIP2_VERSION)-* - -########################################################################### -# Build: XZ (LZMA) -########################################################################### - -XZ-$(os): $$(foreach sdk,$$(SDKS-$(os)),$$(XZ_FATLIB-$$(sdk))) -XZ-$(os)-wheels: $$(foreach target,$$(TARGETS-$(os)),$$(XZ_WHEEL-$$(target))) - -clean-XZ-$(os): - @echo ">>> Clean XZ build products on $(os)" - rm -rf \ - build/$(os)/*/xz-$(XZ_VERSION) \ - build/$(os)/*/xz-$(XZ_VERSION).*.log \ - install/$(os)/*/xz-$(XZ_VERSION) \ - install/$(os)/*/xz-$(XZ_VERSION).*.log \ - merge/$(os)/*/xz-$(XZ_VERSION) \ - merge/$(os)/*/xz-$(XZ_VERSION).*.log \ - wheels/$(os)/xz-$(XZ_VERSION)-* - -clean-XZ-$(os)-wheels: - rm -rf \ - install/$(os)/*/xz-$(XZ_VERSION)/wheel \ - wheels/$(os)/xz-$(XZ_VERSION)-* - -########################################################################### -# Build: OpenSSL -########################################################################### - -OpenSSL-$(os): $$(foreach sdk,$$(SDKS-$(os)),$$(OPENSSL_FATINCLUDE-$$(sdk)) $$(OPENSSL_SSL_FATLIB-$$(sdk)) $$(OPENSSL_CRYPTO_FATLIB-$$(sdk))) -OpenSSL-$(os)-wheels: $$(foreach target,$$(TARGETS-$(os)),$$(OPENSSL_WHEEL-$$(target))) - -clean-OpenSSL-$(os): - @echo ">>> Clean OpenSSL build products on $(os)" - rm -rf \ - build/$(os)/*/openssl-$(OPENSSL_VERSION) \ - build/$(os)/*/openssl-$(OPENSSL_VERSION).*.log \ - install/$(os)/*/openssl-$(OPENSSL_VERSION) \ - install/$(os)/*/openssl-$(OPENSSL_VERSION).*.log \ - merge/$(os)/*/openssl-$(OPENSSL_VERSION) \ - merge/$(os)/*/openssl-$(OPENSSL_VERSION).*.log \ - wheels/$(os)/openssl-$(OPENSSL_VERSION)-* - -clean-OpenSSL-$(os)-wheels: - rm -rf \ - install/$(os)/*/openssl-$(OPENSSL_VERSION)/wheel \ - wheels/$(os)/openssl-$(OPENSSL_VERSION)-* - -########################################################################### -# Build: libFFI +# Build: Python ########################################################################### -# macOS uses the system-provided libFFI, so there's no need to package -# a libFFI framework for macOS. -ifneq ($(os),macOS) - -$$(LIBFFI_SRCDIR-$(os))/darwin_common/include/ffi.h: downloads/libffi-$(LIBFFI_VERSION).tar.gz $(HOST_PYTHON_EXE) - @echo ">>> Unpack and configure libFFI sources on $(os)" - mkdir -p $$(LIBFFI_SRCDIR-$(os)) - tar zxf $$< --strip-components 1 -C $$(LIBFFI_SRCDIR-$(os)) - # Patch the build to add support for new platforms - cd $$(LIBFFI_SRCDIR-$(os)) && patch -p1 < $(PROJECT_DIR)/patch/libffi-$(LIBFFI_VERSION).patch - # Configure the build - cd $$(LIBFFI_SRCDIR-$(os)) && \ - $(PROJECT_DIR)/$(HOST_PYTHON_EXE) generate-darwin-source-and-headers.py --only-$(shell echo $(os) | tr '[:upper:]' '[:lower:]') \ - 2>&1 | tee -a ../libffi-$(LIBFFI_VERSION).config.log -endif +PYTHON_XCFRAMEWORK-$(os)=support/$(PYTHON_VER)/$(os)/Python.xcframework -libFFI-$(os): $$(foreach sdk,$$(SDKS-$(os)),$$(LIBFFI_FATLIB-$$(sdk))) -libFFI-$(os)-wheels: $$(foreach target,$$(TARGETS-$(os)),$$(LIBFFI_WHEEL-$$(target))) +ifeq ($(os),macOS) -clean-libFFI-$(os): - @echo ">>> Clean libFFI build products on $(os)" - rm -rf \ - build/$(os)/libffi-$(LIBFFI_VERSION) \ - build/$(os)/libffi-$(LIBFFI_VERSION).*.log \ - merge/$(os)/libffi-$(LIBFFI_VERSION) \ - merge/$(os)/libffi-$(LIBFFI_VERSION).*.log \ - wheels/$(os)/libffi-$(LIBFFI_VERSION)-* +PYTHON_FRAMEWORK-$(os)=$$(PYTHON_INSTALL-$(sdk))/Python.framework -clean-libFFI-$(os)-wheels: - rm -rf \ - build/$(os)/libffi-$(LIBFFI_VERSION)/build_*/wheel \ - wheels/$(os)/libffi-$(LIBFFI_VERSION)-* +$$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ + downloads/python-$(PYTHON_PKG_VERSION)-macos11.pkg + @echo ">>> Repackage macOS package as XCFramework" + + # Unpack .pkg file. It turns out .pkg files are readable by tar... although + # 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_PKG_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 + + # Apply the App Store compliance patch + patch --strip 2 --directory $$(PYTHON_INSTALL_VERSION-macosx)/lib/python$(PYTHON_VER) --input $(PROJECT_DIR)/patch/Python/app-store-compliance.patch + + # Remove any .orig files produced by the patching process + find $$(PYTHON_INSTALL_VERSION-macosx) -name "*.orig" -exec rm {} \; + + # Rewrite the framework to make it standalone + patch/make-relocatable.sh $$(PYTHON_INSTALL_VERSION-macosx) 2>&1 > /dev/null + + # Create the modulemap file + cp -r patch/Python/module.modulemap.prefix $$(PYTHON_MODULEMAP-macosx) + echo "" >> $$(PYTHON_MODULEMAP-macosx) + cd $$(PYTHON_INCLUDE-macosx) && \ + find cpython -name "*.h" | sort | sed -e 's/^/ exclude header "/' | sed 's/$$$$/"/' >> $$(PYTHON_MODULEMAP-macosx) && \ + echo "" >> $$(PYTHON_MODULEMAP-macosx) && \ + find internal -name "*.h" | sort | sed -e 's/^/ exclude header "/' | sed 's/$$$$/"/' >> $$(PYTHON_MODULEMAP-macosx) + echo "\n}" >> $$(PYTHON_MODULEMAP-macosx) + + # Re-apply the signature on the binaries. + codesign -s - --preserve-metadata=identifier,entitlements,flags,runtime -f $$(PYTHON_LIB-macosx) \ + 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_PLATFORM_SITECUSTOMIZE-$$(target))) + + @echo ">>> Create final distribution artefact for macOS" + mkdir -p dist + # Strip xattrs from the support files + xattr -cr support/$(PYTHON_VER)/macOS + # Build a distributable tarball + tar zcvf $$@ -C support/$(PYTHON_VER)/macOS `ls -A support/$(PYTHON_VER)/macOS/` -########################################################################### -# Build: Python -########################################################################### +else $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ - $$(foreach sdk,$$(SDKS-$(os)),$$(PYTHON_FATLIB-$$(sdk)) $$(PYTHON_FATINCLUDE-$$(sdk))) + $$(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_FATLIB-$$(sdk)) -headers $$(PYTHON_FATINCLUDE-$$(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_FATSTDLIB-$$(sdk))) - @echo ">>> Create Python stdlib on $(os)" - # Copy stdlib from first SDK in $(os) - cp -r $$(PYTHON_FATSTDLIB-$$(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_FATSTDLIB-$$(sdk))/config-$(PYTHON_VER)-* $$(PYTHON_STDLIB-$(os)); ) - - # Copy the _sysconfigdata modules from every SDK in $(os) into the support folder. - $$(foreach sdk,$$(SDKS-$(os)),cp $$(PYTHON_FATSTDLIB-$$(sdk))/_sysconfigdata__*.py $$(PYTHON_STDLIB-$(os)); ) - - # Copy the lib-dynload contents from every SDK in $(os) into the support folder. - $$(foreach sdk,$$(SDKS-$(os)),cp $$(PYTHON_FATSTDLIB-$$(sdk))/lib-dynload/* $$(PYTHON_STDLIB-$(os))/lib-dynload; ) + @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)); ) + $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/platform-config $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) + # Disable dSYM production (for now) + # $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/Python.dSYM $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) + +ifeq ($(filter $(os),iOS visionOS),$(os)) + @echo ">>> Clone testbed project for $(os)" + $(HOST_PYTHON) $$(PYTHON_SRCDIR-$$(firstword $$(SDK_TARGETS-$$(firstword $$(SDKS-$(os))))))/$(os)/testbed clone --framework $$(PYTHON_XCFRAMEWORK-$(os)) support/$(PYTHON_VER)/$(os)/testbed +endif @echo ">>> Create VERSIONS file for $(os)" 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: macOS native" >> 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 "libFFI: $(LIBFFI_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS + echo "mpdecimal: $(MPDECIMAL_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS echo "OpenSSL: $(OPENSSL_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS echo "XZ: $(XZ_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz: \ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist \ - $$(PYTHON_STDLIB-$(os))/VERSIONS \ - $$(foreach target,$$(TARGETS-$(os)), $$(PYTHON_SITECUSTOMIZE-$$(target))) + $$(foreach target,$$(TARGETS-$(os)), $$(PYTHON_PLATFORM_SITECUSTOMIZE-$$(target))) @echo ">>> Create final distribution artefact for $(os)" mkdir -p dist - # 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)/` -Python-$(os): dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz +endif -clean-Python-$(os): +clean-$(os): @echo ">>> Clean Python build products on $(os)" rm -rf \ build/$(os)/*/python-$(PYTHON_VER)* \ build/$(os)/*/python-$(PYTHON_VER)*.*.log \ install/$(os)/*/python-$(PYTHON_VER)* \ install/$(os)/*/python-$(PYTHON_VER)*.*.log \ - merge/$(os)/*/python-$(PYTHON_VER)* \ - merge/$(os)/*/python-$(PYTHON_VER)*.*.log \ support/$(PYTHON_VER)/$(os) \ + support/$(PYTHON_VER)/python-$(os).*.log \ dist/Python-$(PYTHON_VER)-$(os)-* -dev-clean-Python-$(os): +dev-clean-$(os): @echo ">>> Partially clean Python build products on $(os) so that local code modifications can be made" rm -rf \ build/$(os)/*/Python-$(PYTHON_VERSION)/python.exe \ build/$(os)/*/python-$(PYTHON_VERSION).*.log \ install/$(os)/*/python-$(PYTHON_VERSION) \ install/$(os)/*/python-$(PYTHON_VERSION).*.log \ - merge/$(os)/*/python-$(PYTHON_VERSION) \ - merge/$(os)/*/python-$(PYTHON_VERSION).*.log \ - support/$(PYTHON_VER)/$(os) \ - dist/Python-$(PYTHON_VER)-$(os)-* - -merge-clean-Python-$(os): - @echo ">>> Partially clean Python build products on $(os) so that merge modifications can be made" - rm -rf \ - merge/$(os)/*/python-$(PYTHON_VERSION) \ - merge/$(os)/*/python-$(PYTHON_VERSION).*.log \ support/$(PYTHON_VER)/$(os) \ dist/Python-$(PYTHON_VER)-$(os)-* @@ -1191,21 +726,7 @@ merge-clean-Python-$(os): # Build ########################################################################### -$(os)-wheels: $(foreach dep,$(DEPENDENCIES),$(dep)-$(os)-wheels) -$(os): dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz $(os)-wheels - -clean-$(os)-wheels: $(foreach dep,$(DEPENDENCIES),clean-$(dep)-$(os)-wheels) - @echo ">>> Clean $(os) wheels" - rm -rf wheel/$(os) - -clean-$(os): clean-$(os)-wheels - @echo ">>> Clean $(os) build products" - rm -rf \ - build/$(os) \ - install/$(os) \ - merge/$(os) \ - dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz \ - dist/Python-$(PYTHON_VER)-$(os)-support.test-$(BUILD_NUMBER).tar.gz +$(os): dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz ########################################################################### # Build: Debug @@ -1214,49 +735,32 @@ clean-$(os): clean-$(os)-wheels vars-$(os): $$(foreach target,$$(TARGETS-$(os)),vars-$$(target)) $$(foreach sdk,$$(SDKS-$(os)),vars-$$(sdk)) @echo ">>> Environment variables for $(os)" @echo "SDKS-$(os): $$(SDKS-$(os))" - @echo "LIBFFI_SRCDIR-$(os): $$(LIBFFI_SRCDIR-$(os))" @echo "LIBPYTHON_XCFRAMEWORK-$(os): $$(LIBPYTHON_XCFRAMEWORK-$(os))" @echo "PYTHON_XCFRAMEWORK-$(os): $$(PYTHON_XCFRAMEWORK-$(os))" @echo endef # build -$(BDIST_WHEEL): - @echo ">>> Ensure the macOS python install has pip and wheel" - $(HOST_PYTHON_EXE) -m ensurepip - PIP_REQUIRE_VIRTUALENV=false $(HOST_PYTHON_EXE) -m pip install wheel - -# Binary support wheels -wheels: $(foreach dep,$(DEPENDENCIES),$(dep)-wheels) -clean-wheels: $(foreach dep,$(DEPENDENCIES),clean-$(dep)-wheels) - # Dump environment variables (for debugging purposes) vars: $(foreach os,$(OS_LIST),vars-$(os)) + @echo ">>> Environment variables for $(os)" + @echo "HOST_ARCH: $(HOST_ARCH)" + @echo "HOST_PYTHON: $(HOST_PYTHON)" + @echo + +config: + @echo "PYTHON_VERSION=$(PYTHON_VERSION)" + @echo "PYTHON_VER=$(PYTHON_VER)" + @echo "BUILD_NUMBER=$(BUILD_NUMBER)" + @echo "BZIP2_VERSION=$(BZIP2_VERSION)" + @echo "LIBFFI_VERSION=$(LIBFFI_VERSION)" + @echo "MPDECIMAL_VERSION=$(MPDECIMAL_VERSION)" + @echo "OPENSSL_VERSION=$(OPENSSL_VERSION)" + @echo "XZ_VERSION=$(XZ_VERSION)" # Expand cross-platform build and clean targets for each output product -BZip2: $(foreach os,$(OS_LIST),BZip2-$(os)) -BZip2-wheels: $(foreach os,$(OS_LIST),BZip2-$(os)-wheels) -clean-BZip2: $(foreach os,$(OS_LIST),clean-BZip2-$(os)) -clean-BZip2-wheels: $(foreach os,$(OS_LIST),clean-BZip2-$(os)-wheels) - -XZ: $(foreach os,$(OS_LIST),XZ-$(os)) -XZ-wheels: $(foreach os,$(OS_LIST),XZ-$(os)-wheels) -clean-XZ: $(foreach os,$(OS_LIST),clean-XZ-$(os)) -clean-XZ-wheels: $(foreach os,$(OS_LIST),clean-XZ-$(os)-wheels) - -OpenSSL: $(foreach os,$(OS_LIST),OpenSSL-$(os)) -OpenSSL-wheels: $(foreach os,$(OS_LIST),OpenSSL-$(os)-wheels) -clean-OpenSSL: $(foreach os,$(OS_LIST),clean-OpenSSL-$(os)) -clean-OpenSSL-wheels: $(foreach os,$(OS_LIST),clean-OpenSSL-$(os)-wheels) - -libFFI: $(foreach os,$(OS_LIST),libFFI-$(os)) -libFFI-wheels: $(foreach os,$(OS_LIST),libFFI-$(os)-wheels) -clean-libFFI: $(foreach os,$(OS_LIST),clean-libFFI-$(os)) -clean-libFFI-wheels: $(foreach os,$(OS_LIST),clean-libFFI-$(os)-wheels) - -Python: $(foreach os,$(OS_LIST),Python-$(os)) -clean-Python: $(foreach os,$(OS_LIST),clean-Python-$(os)) -dev-clean-Python: $(foreach os,$(OS_LIST),dev-clean-Python-$(os)) +clean: $(foreach os,$(OS_LIST),clean-$(os)) +dev-clean: $(foreach os,$(OS_LIST),dev-clean-$(os)) # Expand the build macro for every OS $(foreach os,$(OS_LIST),$(eval $(call build,$(os)))) diff --git a/README.rst b/README.rst index 7972ca55..06fde6c9 100644 --- a/README.rst +++ b/README.rst @@ -2,45 +2,38 @@ Python Apple Support ==================== This is a meta-package for building a version of Python that can be embedded -into a macOS, iOS, tvOS or watchOS project. +into a macOS, iOS, tvOS, watchOS, or visionOS project. -**This branch builds a packaged version of Python 3.13.0**. +**This branch builds a packaged version of Python 3.14**. Other Python versions are available by cloning other branches of the main repository: -* `Python 3.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, and the +App Store compliance patch applied. -* ``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, watchOS, and visionOS packages compiled by this project use the +official `PEP 730 `__ code that is part of +Python 3.13 to provide iOS support; the relevant patches have been backported +to 3.9-3.12. Additional patches have been applied to add tvOS, watchOS, and +visionOS support. The binaries support x86_64 and arm64 for macOS; arm64 for iOS and appleTV -devices; and arm64_32 for watchOS. It also supports device simulators on both -x86_64 and M1 hardware. This should enable the code to run on: +devices; arm64_32 for watchOS devices; and arm64 for visionOS devices. It also +supports device simulators on both x86_64 and M1 hardware, except for visionOS, +for which x86_64 simulators are officially unsupported. This should enable the +code to run on: * macOS 11 (Big Sur) or later, on: * MacBook (including MacBooks using Apple Silicon) @@ -48,17 +41,19 @@ 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) +* visionOS 2.0 or later, on: + * Apple Vision Pro Quickstart ---------- @@ -79,6 +74,7 @@ repository, and then in the root directory, and run: * ``make iOS`` to build everything for iOS. * ``make tvOS`` to build everything for tvOS. * ``make watchOS`` to build everything for watchOS. +* ``make visionOS`` to build everything for visionOS. This should: @@ -86,27 +82,51 @@ This should: 2. Patch them as required for compatibility with the selected OS 3. Build the packages as Xcode-compatible XCFrameworks. -The resulting support packages will be packaged as a ``.tar.gz`` file +The resulting support packages will be packaged as ``.tar.gz`` files in the ``dist`` folder. Each support package contains: * ``VERSIONS``, a text file describing the specific versions of code used to build the support package; -* ``platform-site``, a folder that contains site customization scripts that can be used - to make your local Python install look like it is an on-device install for each of the - underlying target architectures supported by the platform. This is needed because when - you run ``pip`` you'll be on a macOS machine with a specific architecture; if ``pip`` - tries to install a binary package, it will install a macOS binary wheel (which won't - work on iOS/tvOS/watchOS). However, if you add the ``platform-site`` folder to your - ``PYTHONPATH`` when invoking pip, the site customization will make your Python install - return ``platform`` and ``sysconfig`` responses consistent with on-device behavior, - which will cause ``pip`` to install platform-appropriate packages. -* ``Python.xcframework``, a multi-architecture build of the Python runtime library -* ``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. +* ``Python.xcframework``, a multi-architecture build of the Python runtime library. + +On iOS/tvOS/watchOS/visionOS, the ``Python.xcframework`` contains a +slice for each supported ABI (device and simulator). The folder containing the +slice can also be used as a ``PYTHONHOME``, as it contains a ``bin``, ``include`` +and ``lib`` directory. + +The ``bin`` folder does not contain Python executables (as they can't be +invoked). However, it *does* contain shell aliases for the compilers that are +needed to build packages. This is required because Xcode uses the ``xcrun`` +alias to dynamically generate the name of binaries, but a lot of C tooling +expects that ``CC`` will not contain spaces. + +Each slice of an iOS/tvOS/watchOS/visionOS XCframework also contains a +``platform-config`` folder with a subfolder for each supported architecture in +that slice. These subfolders can be used to make a macOS Python environment +behave as if it were on an iOS/tvOS/watchOS/visionOS device. This works in one +of two ways: + +1. **A sitecustomize.py script**. If the ``platform-config`` subfolder is on + your ``PYTHONPATH`` when a Python interpreter is started, a site + customization will be applied that patches methods in ``sys``, ``sysconfig`` + and ``platform`` that are used to identify the system. + +2. **A make_cross_venv.py script**. If you call ``make_cross_venv.py``, + providing the location of a virtual environment, the script will add some + files to the ``site-packages`` folder of that environment that will + automatically apply the same set of patches as the ``sitecustomize.py`` + script whenever the environment is activated, without any need to modify + ``PYTHONPATH``. If you use ``build`` to create an isolated PEP 517 + environment to build a wheel, these patches will also be applied to the + isolated build environment that is created. + +iOS and visionOS distributions also contain a copy of the iOS or visionOS +``testbed`` project - an Xcode project that can be used to run test suites of +Python code. See the `CPython documentation on testing packages +`__ for +details on how to use this testbed. For a detailed instructions on using the support package in your own project, see the `usage guide <./USAGE.md>`__ @@ -114,18 +134,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, watchOS, +and visionOS, although that hasn't been tested). Historical support ------------------ @@ -138,3 +151,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 706fe058..072ae1ec 100644 --- a/USAGE.md +++ b/USAGE.md @@ -2,223 +2,123 @@ ## The easy way -The easist way to use these packages is by creating a project with `Briefcase -`__. Briefcase will download pre-compiled -versions of these support packages, and add them to an Xcode project (or -pre-build stub application, in the case of macOS). +The easist way to use these packages is by creating a project with +(Briefcase)[https://github.com/beeware/briefcase]. Briefcase will download +pre-compiled versions of these support packages, and add them to an Xcode project +(or pre-build stub application, in the case of macOS). ## The manual way -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: +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: -1. [Download a release tarball for your desired Python version and Apple - platform](https://github.com/beeware/Python-Apple-support/releases) +* [macOS](https://docs.python.org/3/using/mac.html) +* [iOS](https://docs.python.org/3/using/ios.html#adding-python-to-an-ios-project) -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. +For tvOS, watchOS, and visionOS, you should be able to broadly follow the instructions +in the iOS guide, changing some platform names in the first script. The testbed projects +generated on iOS and visionOS may be used as rough references as well. -3. In Xcode, select the root node of the project tree, and select the target you - want to build. +### Using Objective C -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". +Once you've added the Python XCframework to your project, you'll need to +initialize the Python runtime in your Objective C code (This is step 10 of the +iOS guide linked above). This initialization should generally be done as early +as possible in the application's lifecycle, but definitely needs to be done +before you invoke Python code. -5. Select "General" -> "Build Phases", and ensure that the `python-stdlib` folder - is listed in the "Copy Bundle Resources" step. +As a *bare minimum*, you can do the following: -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: +1. Import the Python C API headers: + ```objc + #include + ``` -```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 -``` +2. Initialize the Python interpreter: + ```objc + NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; + NSString *pythonHome = [NSString stringWithFormat:@"%@/python", resourcePath, nil]; + NSString *appPath = [NSString stringWithFormat:@"%@/app", resourcePath, nil]; -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 "{}" \; -``` + setenv("PYTHONHOME", [pythonHome UTF8String], 1); + setenv("PYTHONPATH", [appPath UTF8String], 1); - 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 - - -``` + Py_Initialize(); - macOS projects don't require `.dylib` files be moved like this, so you can use a much - simpler signing script: + // we now have a Python interpreter ready to be used + ``` -```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 {} \; -``` +Again - this is the *bare minimum* initialization. In practice, you will likely +need to configure other aspects of the Python interpreter using the +`PyPreConfig` and `PyConfig` mechanisms. Consult the [Python documentation on +interpreter configuration](https://docs.python.org/3/c-api/init_config.html) for +more details on the configuration options that are available. You may find the +[bootstrap mainline code used by +Briefcase](https://github.com/beeware/briefcase-iOS-Xcode-template/blob/main/%7B%7B%20cookiecutter.format%20%7D%7D/%7B%7B%20cookiecutter.class_name%20%7D%7D/main.m) +a helpful point of comparison. + +### Using Swift -You will now be able to access the Python runtime in your Python code. +If you want to use Swift instead of Objective C, the bare minimum initialization +code will look something like this: -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). +1. Import the Python framework: + ```swift + import Python + ``` -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. +2. Initialize the Python interpreter: + ```swift + guard let pythonHome = Bundle.main.path(forResource: "python", ofType: nil) else { return } + let appPath = Bundle.main.path(forResource: "app", ofType: nil) -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. + setenv("PYTHONHOME", pythonHome, 1) + setenv("PYTHONPATH", appPath, 1) + Py_Initialize() + // we now have a Python interpreter ready to be used + ``` + + Again, references to a specific Python version should reflect the version of + Python you are using; and you will likely need to use `PyPreConfig` and + `PreConfig` APIs. ## Accessing the Python runtime There are 2 ways to access the Python runtime in your project code. -### Embedded C API. +### Embedded C API You can use the [Python Embedded C -API](https://docs.python.org/3/extending/embedding.html) to instantiate a Python -interpreter. This is the approach taken by Briefcase; you may find the bootstrap -mainline code generated by Briefcase a helpful guide to what is needed to start -an interpreter and run Python code. +API](https://docs.python.org/3/extending/embedding.html) to invoke Python code +and interact with Python objects. This is a raw C API that is accesible to both +Objective C and Swift. ### PythonKit -An alternate approach is to use +If you're using Swift, an alternate approach is to use [PythonKit](https://github.com/pvieito/PythonKit). PythonKit is a package that provides a Swift API to running Python code. -To use PythonKit in your project: - -1. Add PythonKit to your project using the Swift Package manager. See the - PythonKit documentation for details. +To use PythonKit in your project, add the Python Apple Support package to your +project and instantiate a Python interpreter as described above; then add +PythonKit to your project using the Swift Package manager (see the [PythonKit +documentation](https://github.com/pvieito/PythonKit) for details). -2. Create a file called `module.modulemap` inside - `Python.xcframework/macos-arm64_x86_64/Headers/`, containing the following - code: -``` -module Python { - umbrella header "Python.h" - export * - link "Python" -} -``` - -3. In your Swift code, initialize the Python runtime. This should generally be - done as early as possible in the application's lifecycle, but definitely - needs to be done before you invoke Python code: +Once you've done this, you can import PythonKit: ```swift -import Python - -guard let stdLibPath = Bundle.main.path(forResource: "python-stdlib", ofType: nil) else { return } -guard let libDynloadPath = Bundle.main.path(forResource: "python-stdlib/lib-dynload", ofType: nil) else { return } -setenv("PYTHONHOME", stdLibPath, 1) -setenv("PYTHONPATH", "\(stdLibPath):\(libDynloadPath)", 1) -Py_Initialize() -// we now have a Python interpreter ready to be used +import PythonKit ``` - -5. Invoke Python code in your app. For example: +and use the PythonKit Swift API to interact with Python code: ```swift -import PythonKit - let sys = Python.import("sys") print("Python Version: \(sys.version_info.major).\(sys.version_info.minor)") print("Python Encoding: \(sys.getdefaultencoding().upper())") print("Python Path: \(sys.path)") - -_ = Python.import("math") // verifies `lib-dynload` is found and signed successfully ``` - -To integrate 3rd party python code and dependencies, you will need to make sure -`PYTHONPATH` contains their paths; once this has been done, you can run -`Python.import("")`. to import that module from inside swift. diff --git a/patch/Python/OpenSSL.xcprivacy b/patch/Python/OpenSSL.xcprivacy new file mode 100644 index 00000000..95780a09 --- /dev/null +++ b/patch/Python/OpenSSL.xcprivacy @@ -0,0 +1,23 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + + + + NSPrivacyCollectedDataTypes + + NSPrivacyTrackingDomains + + NSPrivacyTracking + + + diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 72674183..73c5b5a6 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1,2222 +1,3988 @@ -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/__init__.py b/Lib/ctypes/__init__.py +index 823a3692fd1..00639dd8488 100644 +--- a/Lib/ctypes/__init__.py ++++ b/Lib/ctypes/__init__.py +@@ -419,7 +419,7 @@ + if name: + name = _os.fspath(name) + +- # If the filename that has been provided is an iOS/tvOS/watchOS ++ # If the filename that has been provided is an iOS/tvOS/watchOS/visionOS + # .fwork file, dereference the location to the true origin of the + # binary. + if name.endswith(".fwork"): diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py -index 0c2510e161..5567080ba5 100644 +index 99504911a3d..527c2f36dd0 100644 --- a/Lib/ctypes/util.py +++ b/Lib/ctypes/util.py -@@ -67,7 +67,7 @@ - return fname - return None +@@ -126,7 +126,7 @@ + if (name := _get_module_filename(h)) is not None] + return libraries --elif os.name == "posix" and sys.platform == "darwin": -+elif os.name == "posix" and sys.platform in {'darwin', 'ios', 'tvos', 'watchos'}: +-elif os.name == "posix" and sys.platform in {"darwin", "ios", "tvos", "watchos"}: ++elif os.name == "posix" and sys.platform in {"darwin", "ios", "tvos", "watchos", "visionos"}: from ctypes.macholib.dyld import dyld_find as _dyld_find def find_library(name): possible = ['lib%s.dylib' % name, +@@ -425,7 +425,7 @@ + # https://man.openbsd.org/dl_iterate_phdr + # https://docs.oracle.com/cd/E88353_01/html/E37843/dl-iterate-phdr-3c.html + if (os.name == "posix" and +- sys.platform not in {"darwin", "ios", "tvos", "watchos"}): ++ sys.platform not in {"darwin", "ios", "tvos", "watchos", "visionos"}): + import ctypes + if hasattr((_libc := ctypes.CDLL(None)), "dl_iterate_phdr"): + diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py -index 0019897c94..0356d2ab5d 100644 +index 8bcd741c446..d8a6f28edba 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -52,7 +52,7 @@ # Bootstrap-related code ###################################################### _CASE_INSENSITIVE_PLATFORMS_STR_KEY = 'win', --_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY = 'cygwin', 'darwin' -+_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY = 'cygwin', 'darwin', 'ios', 'tvos', 'watchos' +-_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY = 'cygwin', 'darwin', 'ios', 'tvos', 'watchos' ++_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY = 'cygwin', 'darwin', 'ios', 'tvos', 'watchos', 'visionos' _CASE_INSENSITIVE_PLATFORMS = (_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY + _CASE_INSENSITIVE_PLATFORMS_STR_KEY) -@@ -1704,6 +1704,59 @@ - return f'FileFinder({self.path!r})' +@@ -1535,7 +1535,7 @@ + """ + extension_loaders = [] + if hasattr(_imp, 'create_dynamic'): +- if sys.platform in {"ios", "tvos", "watchos"}: ++ if sys.platform in {"ios", "tvos", "watchos", "visionos"}: + extension_loaders = [(AppleFrameworkLoader, [ + suffix.replace(".so", ".fwork") + for suffix in _imp.extension_suffixes() +diff --git a/Lib/platform.py b/Lib/platform.py +index 55e211212d4..cad919bc0c4 100644 +--- a/Lib/platform.py ++++ b/Lib/platform.py +@@ -528,6 +528,78 @@ + return IOSVersionInfo(system, release, model, is_simulator) -+class AppleFrameworkLoader(ExtensionFileLoader): -+ """A loader for modules that have been packaged as Apple Frameworks for -+ compatibility with Apple's App Store policies. ++# A namedtuple for tvOS version information. ++TVOSVersionInfo = collections.namedtuple( ++ "TVOSVersionInfo", ++ ["system", "release", "model", "is_simulator"] ++) + -+ 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 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. + """ -+ def __init__(self, fullname, dylib_file, path): -+ super().__init__(fullname, dylib_file) -+ self.parent_paths = path ++ 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 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 ++ return TVOSVersionInfo(system, release, model, is_simulator) + + -+class AppleFrameworkFinder: -+ """A finder for modules that have been packaged as Apple Frameworks -+ for compatibility with Apple's App Store policies. ++# A namedtuple for watchOS version information. ++WatchOSVersionInfo = collections.namedtuple( ++ "WatchOSVersionInfo", ++ ["system", "release", "model", "is_simulator"] ++) + -+ 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)) -diff --git a/Lib/platform.py b/Lib/platform.py -index 7bb222088d..0a5ed0361e 100755 ---- a/Lib/platform.py -+++ b/Lib/platform.py -@@ -496,6 +496,26 @@ - # If that also doesn't work return the default values - return release, versioninfo, machine - -+def iOS_ver(): -+ """Get iOS/tvOS version information, and return it as a -+ tuple (system, release, model). All tuple entries are strings. ++ ++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. + """ -+ import _ios_support -+ return _ios_support.get_platform_ios() ++ 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 is_simulator(): -+ """Determine if the current platform is a device simulator. + -+ Only useful when working with iOS, tvOS or watchOS, because -+ Apple provides simulator platforms for those devices. ++# A namedtuple for visionOS version information. ++VisionOSVersionInfo = collections.namedtuple( ++ "VisionOSVersionInfo", ++ ["system", "release", "model", "is_simulator"] ++) ++ + -+ 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. ++def visionos_ver(system="", release="", model="", is_simulator=False): ++ """Get visionOS version information, and return it as a namedtuple: ++ (system, release, model, is_simulator). ++ ++ If values can't be determined, they are set to values provided as ++ parameters. + """ -+ return getattr(sys.implementation, "_simulator", False) ++ if sys.platform == "visionos": ++ # TODO: Can the iOS implementation be used here? ++ import _ios_support ++ result = _ios_support.get_platform_ios() ++ if result is not None: ++ return VisionOSVersionInfo(*result) ++ ++ return VisionOSVersionInfo(system, release, model, is_simulator) ++ + def _java_getprop(name, default): - + """This private helper is deprecated in 3.13 and will be removed in 3.15""" from java.lang import System -@@ -652,7 +672,7 @@ +@@ -727,7 +799,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'}: +- if sys.platform in {'dos', 'win32', 'win16', 'ios', 'tvos', 'watchos'}: ++ if sys.platform in {'dos', 'win32', 'win16', 'ios', 'tvos', 'watchos', 'visionos'}: # XXX Others too ? return default -@@ -814,6 +834,24 @@ +@@ -891,14 +963,30 @@ 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/visionOS simulator, os.uname returns the architecture as ++ # uname.machine. On device it returns the model name for some reason; but ++ # there's only one CPU architecture for devices, so we know the right ++ # answer. + def get_ios(): + if sys.implementation._multiarch.endswith("simulator"): + return os.uname().machine + return 'arm64' + + def get_tvos(): -+ if 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 get_visionos(): ++ if sys.implementation._multiarch.endswith("simulator"): ++ return os.uname().machine ++ return 'arm64' + def from_subprocess(): """ Fall back to `uname -p` -@@ -968,6 +1006,15 @@ - system = 'Windows' - release = 'Vista' +@@ -1058,9 +1146,15 @@ + 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() ++ if sys.platform == 'visionos': ++ system, release, _, _ = visionos_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 +@@ -1350,6 +1444,12 @@ + # macOS and iOS both report as a "Darwin" kernel + if sys.platform == "ios": + system, release, _, _ = ios_ver() ++ elif sys.platform == "tvos": ++ system, release, _, _ = tvos_ver() ++ elif sys.platform == "watchos": ++ system, release, _, _ = watchos_ver() ++ elif sys.platform == "visionos": ++ system, release, _, _ = visionos_ver() + else: + macos_release = mac_ver()[0] + if macos_release: diff --git a/Lib/site.py b/Lib/site.py -index 672fa7b000..9fd399e990 100644 +index f9327197159..74899abecb0 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' +@@ -298,8 +298,8 @@ + if env_base: + return env_base + +- # Emscripten, iOS, tvOS, VxWorks, WASI, and watchOS have no home directories +- if sys.platform in {"emscripten", "ios", "tvos", "vxworks", "wasi", "watchos"}: ++ # Emscripten, iOS, tvOS, visionOS, VxWorks, WASI, and watchOS have no home directories ++ if sys.platform in {"emscripten", "ios", "tvos", "vxworks", "visionos", "wasi", "watchos"}: + return None + def joinuser(*args): diff --git a/Lib/subprocess.py b/Lib/subprocess.py -index 6df5dd551e..597da09643 100644 +index 54c2eb515b6..03896a234bf 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py -@@ -74,8 +74,8 @@ - else: +@@ -75,7 +75,7 @@ _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"} + # some platforms do not support subprocesses +-_can_fork_exec = sys.platform not in {"emscripten", "wasi", "ios", "tvos", "watchos"} ++_can_fork_exec = sys.platform not in {"emscripten", "wasi", "ios", "tvos", "watchos", "visionos"} if _mswindows: import _winapi -@@ -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 diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py -index 68d30c0f9e..4a8a27b6d0 100644 +index f93b98dd681..0db3dbdce05 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: - 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() -+ 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) +@@ -23,6 +23,9 @@ + _ALWAYS_STR = { + 'IPHONEOS_DEPLOYMENT_TARGET', + 'MACOSX_DEPLOYMENT_TARGET', ++ 'TVOS_DEPLOYMENT_TARGET', ++ 'WATCHOS_DEPLOYMENT_TARGET', ++ 'XROS_DEPLOYMENT_TARGET', + } - return f"{osname}-{release}-{machine}" + _INSTALL_SCHEMES = { +@@ -119,7 +122,7 @@ + # Emscripten, iOS, tvOS, VxWorks, WASI, and watchOS have no home directories. + # Use _PYTHON_HOST_PLATFORM to get the correct platform when cross-compiling. + system_name = os.environ.get('_PYTHON_HOST_PLATFORM', sys.platform).split('-')[0] +- if system_name in {"emscripten", "ios", "tvos", "vxworks", "wasi", "watchos"}: ++ if system_name in {"emscripten", "ios", "tvos", "visionos", "vxworks", "wasi", "watchos"}: + return None + def joinuser(*args): +@@ -730,6 +733,18 @@ + release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0") + osname = sys.platform + machine = sys.implementation._multiarch ++ elif sys.platform == "tvos": ++ release = get_config_vars().get("TVOS_DEPLOYMENT_TARGET", "9.0") ++ osname = sys.platform ++ machine = sys.implementation._multiarch ++ elif sys.platform == "watchos": ++ release = get_config_vars().get("WATCHOS_DEPLOYMENT_TARGET", "4.0") ++ osname = sys.platform ++ machine = sys.implementation._multiarch ++ elif sys.platform == "visionos": ++ release = get_config_vars().get("XROS_DEPLOYMENT_TARGET", "2.0") ++ osname = sys.platform ++ machine = sys.implementation._multiarch + else: + import _osx_support + osname, release, machine = _osx_support.get_platform_osx( +diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py +index 1b551254f86..8594f92c097 100644 +--- a/Lib/test/datetimetester.py ++++ b/Lib/test/datetimetester.py +@@ -7159,9 +7159,9 @@ + self.assertEqual(dt_orig, dt_rt) + + def test_type_check_in_subinterp(self): +- # iOS requires the use of the custom framework loader, ++ # Apple mobile platforms require the use of the custom framework loader, + # not the ExtensionFileLoader. +- if sys.platform == "ios": ++ if support.is_apple_mobile: + extension_loader = "AppleFrameworkLoader" + else: + extension_loader = "ExtensionFileLoader" diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py -index 21e8770ab3..67958d247c 100644 +index b7cd7940eb1..32243a49e7a 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'): +@@ -558,7 +558,7 @@ + sys.platform == "android", f"Android blocks {name} with SELinux" + ) + +-if sys.platform not in {"win32", "vxworks", "ios", "tvos", "watchos"}: ++if sys.platform not in {"win32", "vxworks", "ios", "tvos", "watchos", "visionos"}: unix_shell = '/system/bin/sh' if is_android else '/bin/sh' else: unix_shell = None -@@ -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 -+) +@@ -574,7 +574,7 @@ + def skip_wasi_stack_overflow(): + return unittest.skipIf(is_wasi, "Exhausts stack on WASI") - def requires_fork(): - return unittest.skipUnless(has_fork_support, "requires working os.fork()") +-is_apple_mobile = sys.platform in {"ios", "tvos", "watchos"} ++is_apple_mobile = sys.platform in {"ios", "tvos", "watchos", "visionos"} + is_apple = is_apple_mobile or sys.platform == "darwin" --has_subprocess_support = not is_emscripten and not is_wasi -+has_subprocess_support = ( -+ not is_emscripten -+ and not is_wasi -+ and not is_apple_mobile -+) + has_fork_support = hasattr(os, "fork") and not ( +diff --git a/Lib/test/test_ctypes/test_dllist.py b/Lib/test/test_ctypes/test_dllist.py +index 15603dc3d77..bff6c0fb95f 100644 +--- a/Lib/test/test_ctypes/test_dllist.py ++++ b/Lib/test/test_ctypes/test_dllist.py +@@ -7,7 +7,7 @@ - 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 - 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') + + WINDOWS = os.name == "nt" +-APPLE = sys.platform in {"darwin", "ios", "tvos", "watchos"} ++APPLE = sys.platform in {"darwin", "ios", "tvos", "watchos", "visionos"} + + if WINDOWS: + KNOWN_LIBRARIES = ["KERNEL32.DLL"] diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py -index 2169733503..753a137d66 100644 +index 719c4feace6..92a831a9148 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): +@@ -271,13 +271,21 @@ + if sys.platform == "android": + self.assertEqual(res.system, "Android") + self.assertEqual(res.release, platform.android_ver().release) +- elif sys.platform == "ios": ++ elif support.is_apple_mobile: + # Platform module needs ctypes for full operation. If ctypes + # isn't available, there's no ObjC module, and dummy values are + # returned. + if _ctypes: +- self.assertIn(res.system, {"iOS", "iPadOS"}) +- self.assertEqual(res.release, platform.ios_ver().release) ++ if sys.platform == "ios": ++ # iPads also identify as iOS ++ self.assertIn(res.system, {"iOS", "iPadOS"}) ++ else: ++ # All other platforms - sys.platform is the lower case ++ # form of system (e.g., visionOS->visionos) ++ self.assertEqual(res.system.lower(), sys.platform) ++ # Use the platform-specific version method ++ platform_ver = getattr(platform, f"{sys.platform}_ver") ++ self.assertEqual(res.release, platform_ver().release) + else: + self.assertEqual(res.system, "") + self.assertEqual(res.release, "") +diff --git a/Lib/test/test_webbrowser.py b/Lib/test/test_webbrowser.py +index 4c3ea1cd8df..04a210e5c86 100644 +--- a/Lib/test/test_webbrowser.py ++++ b/Lib/test/test_webbrowser.py +@@ -236,7 +236,8 @@ + arguments=[f'openURL({URL},new-tab)']) + + +-@unittest.skipUnless(sys.platform == "ios", "Test only applicable to iOS") ++@unittest.skipUnless(sys.platform in {"ios", "visionOS"}, ++ "Test only applicable to iOS and visionOS") + class IOSBrowserTest(unittest.TestCase): + def _obj_ref(self, *args): + # Construct a string representation of the arguments that can be used diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py -index 8b0628745c..2d8de8aecb 100755 +index f2e2394089d..2efbbfb0014 100644 --- 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) +@@ -488,7 +488,8 @@ + # macOS can use below Unix support (but we prefer using the macOS + # specific stuff) + +- if sys.platform == "ios": ++ if sys.platform in {"ios", "visionos"}: ++ # iOS and visionOS provide a browser; tvOS and watchOS don't. + register("iosbrowser", None, IOSBrowser(), preferred=True) + if sys.platform == "serenityos": +@@ -653,9 +654,10 @@ + return not rc + + # +-# Platform support for iOS ++# Platform support for Apple Mobile platforms that provide a browser ++# (i.e., iOS and visionOS) # - # Platform support for Windows +-if sys.platform == "ios": ++if sys.platform in {"ios", "visionos"}: + from _ios_support import objc + if objc: + # If objc exists, we know ctypes is also importable. +diff --git a/Makefile.pre.in b/Makefile.pre.in +index b5703fbe6ae..e436b2efb8e 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -209,6 +209,12 @@ + # the build, and is only listed here so it will be included in sysconfigdata. + IPHONEOS_DEPLOYMENT_TARGET=@IPHONEOS_DEPLOYMENT_TARGET@ + ++# visionOS Deployment target is *actually* used during the build, by the ++# compiler shims; export. ++XROS_DEPLOYMENT_TARGET=@XROS_DEPLOYMENT_TARGET@ ++@EXPORT_XROS_DEPLOYMENT_TARGET@export XROS_DEPLOYMENT_TARGET ++ ++ + # Option to install to strip binaries + STRIPFLAG=-s + +@@ -2264,7 +2270,7 @@ + # a full Xcode install that has an iPhone SE (3rd edition) simulator available. + # This must be run *after* a `make install` has completed the build. The + # `--with-framework-name` argument *cannot* be used when configuring the build. +-XCFOLDER:=iOSTestbed.$(MULTIARCH).$(shell date +%s).$$PPID ++XCFOLDER-iOS:=iOSTestbed.$(MULTIARCH).$(shell date +%s).$$PPID + .PHONY: testios + testios: + @if test "$(MACHDEP)" != "ios"; then \ +@@ -2284,11 +2290,41 @@ + exit 1;\ + fi + +- # Clone the testbed project into the XCFOLDER +- $(PYTHON_FOR_BUILD) $(srcdir)/iOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER)" ++ # Clone the testbed project into the XCFOLDER-iOS ++ $(PYTHON_FOR_BUILD) $(srcdir)/iOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER-iOS)" ++ ++ # Run the testbed project ++ $(PYTHON_FOR_BUILD) "$(XCFOLDER-iOS)" run --verbose -- test -uall --single-process --rerun -W ++ ++# Run the test suite on the visionOS simulator. Must be run on a macOS machine with ++# a full Xcode install that has an Apple Vision Pro simulator available. ++# This must be run *after* a `make install` has completed the build. The ++# `--with-framework-name` argument *cannot* be used when configuring the build. ++XCFOLDER-visionOS:=visionOSTestbed.$(MULTIARCH).$(shell date +%s).$$PPID ++.PHONY: testvisionos ++testvisionos: ++ @if test "$(MACHDEP)" != "visionos"; then \ ++ echo "Cannot run the visionOS testbed for a non-visionOS build."; \ ++ exit 1;\ ++ fi ++ @if test "$(findstring -xrsimulator,$(MULTIARCH))" != "-xrsimulator"; then \ ++ echo "Cannot run the visionOS testbed for non-simulator builds."; \ ++ exit 1;\ ++ fi ++ @if test $(PYTHONFRAMEWORK) != "Python"; then \ ++ echo "Cannot run the visionOS testbed with a non-default framework name."; \ ++ exit 1;\ ++ fi ++ @if ! test -d $(PYTHONFRAMEWORKPREFIX); then \ ++ echo "Cannot find a finalized visionOS Python.framework. Have you run 'make install' to finalize the framework build?"; \ ++ exit 1;\ ++ fi ++ ++ # Clone the testbed project into the XCFOLDER-visionOS ++ $(PYTHON_FOR_BUILD) $(srcdir)/visionOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER-visionOS)" + + # Run the testbed project +- $(PYTHON_FOR_BUILD) "$(XCFOLDER)" run --verbose -- test -uall --single-process --rerun -W ++ $(PYTHON_FOR_BUILD) "$(XCFOLDER-visionOS)" run --verbose -- test -uall --single-process --rerun -W + + # Like test, but using --slow-ci which enables all test resources and use + # longer timeout. Run an optional pybuildbot.identify script to include diff --git a/Misc/platform_triplet.c b/Misc/platform_triplet.c -index 3307260544..b5db9e8a80 100644 +index f5cd73bdea8..6c1863c943b 100644 --- a/Misc/platform_triplet.c +++ b/Misc/platform_triplet.c -@@ -233,7 +233,42 @@ - # error unknown platform triplet - # endif - #elif defined(__APPLE__) -+# include "TargetConditionals.h" -+# if TARGET_OS_IOS -+# if TARGET_OS_SIMULATOR +@@ -257,6 +257,32 @@ + # else + PLATFORM_TRIPLET=arm64-iphoneos + # endif ++# elif defined(TARGET_OS_TV) && TARGET_OS_TV ++# if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR +# if __x86_64__ -+PLATFORM_TRIPLET=iphonesimulator-x86_64 ++PLATFORM_TRIPLET=x86_64-appletvsimulator +# else -+PLATFORM_TRIPLET=iphonesimulator-arm64 ++PLATFORM_TRIPLET=arm64-appletvsimulator +# endif +# else -+PLATFORM_TRIPLET=iphoneos-arm64 ++PLATFORM_TRIPLET=arm64-appletvos +# endif -+# elif TARGET_OS_TV -+# if TARGET_OS_SIMULATOR ++# elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH ++# if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR +# if __x86_64__ -+PLATFORM_TRIPLET=appletvsimulator-x86_64 ++PLATFORM_TRIPLET=x86_64-watchsimulator +# else -+PLATFORM_TRIPLET=appletvsimulator-arm64 ++PLATFORM_TRIPLET=arm64-watchsimulator +# endif +# else -+PLATFORM_TRIPLET=appletvos-arm64 ++PLATFORM_TRIPLET=arm64_32-watchos +# endif -+# elif TARGET_OS_WATCH -+# if TARGET_OS_SIMULATOR -+# if __x86_64__ -+PLATFORM_TRIPLET=watchsimulator-x86_64 -+# else -+PLATFORM_TRIPLET=watchsimulator-arm64 -+# endif ++# elif defined(TARGET_OS_VISION) && TARGET_OS_VISION ++# if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR ++PLATFORM_TRIPLET=arm64-xrsimulator +# else -+PLATFORM_TRIPLET=watchos-arm64_32 ++PLATFORM_TRIPLET=arm64-xros +# 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 +index 1bb6a05dc11..49febd56a37 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 @@ +@@ -1743,7 +1743,7 @@ | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ | hiux* | abug | nacl* | netware* | windows* \ -- | os9* | macos* | osx* | ios* \ -+ | os9* | macos* | osx* | ios* | tvos* | watchos* \ +- | os9* | macos* | osx* | ios* | tvos* | watchos* \ ++ | os9* | macos* | osx* | ios* | tvos* | watchos* | xros* \ | mpw* | magic* | mmixware* | mon960* | lnews* \ | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ | aos* | aros* | cloudabi* | sortix* | twizzler* \ -@@ -1786,6 +1786,8 @@ +@@ -1867,7 +1867,7 @@ ;; - *-eabi* | *-gnueabi*) + *-eabi*- | *-gnueabi*-) ;; -+ ios*-simulator | tvos*-simulator | watchos*-simulator) -+ ;; - -*) - # Blank kernel with real OS is always fine. +- ios*-simulator- | tvos*-simulator- | watchos*-simulator- ) ++ ios*-simulator- | tvos*-simulator- | watchos*-simulator- | xros*-simulator-) ;; + none--*) + # None (no kernel, i.e. freestanding / bare metal), diff --git a/configure b/configure -index c87f518382..69685cd25a 100755 +index 884f8a4b068..7c93d36e717 100755 --- a/configure +++ b/configure -@@ -4247,6 +4247,15 @@ - *-*-cygwin*) - ac_sys_system=Cygwin +@@ -982,6 +982,10 @@ + CFLAGS + CC + HAS_XCRUN ++EXPORT_XROS_DEPLOYMENT_TARGET ++XROS_DEPLOYMENT_TARGET ++WATCHOS_DEPLOYMENT_TARGET ++TVOS_DEPLOYMENT_TARGET + IPHONEOS_DEPLOYMENT_TARGET + EXPORT_MACOSX_DEPLOYMENT_TARGET + CONFIGURE_MACOSX_DEPLOYMENT_TARGET +@@ -4116,6 +4120,15 @@ + *-apple-ios*) + ac_sys_system=iOS ;; -+ *-apple-ios*) -+ ac_sys_system=iOS -+ ;; + *-apple-tvos*) + ac_sys_system=tvOS + ;; + *-apple-watchos*) + ac_sys_system=watchOS + ;; - *-*-vxworks*) - ac_sys_system=VxWorks - ;; -@@ -4303,27 +4312,96 @@ - *-*-linux*) - case "$host_cpu" in - arm*) -- _host_cpu=arm -+ _host_ident=arm - ;; ++ *-apple-xros*) ++ ac_sys_system=visionOS ++ ;; + *-*-darwin*) + ac_sys_system=Darwin + ;; +@@ -4197,7 +4210,7 @@ + # On cross-compile builds, configure will look for a host-specific compiler by + # prepending the user-provided host triple to the required binary name. + # +-# On iOS, this results in binaries like "arm64-apple-ios13.0-simulator-gcc", ++# On iOS/tvOS/watchOS/visionOS, this results in binaries like "arm64-apple-ios13.0-simulator-gcc", + # which isn't a binary that exists, and isn't very convenient, as it contains the + # iOS version. As the default cross-compiler name won't exist, configure falls + # back to gcc, which *definitely* won't work. We're providing wrapper scripts for +@@ -4212,6 +4225,17 @@ + aarch64-apple-ios*-simulator) AR=arm64-apple-ios-simulator-ar ;; + aarch64-apple-ios*) AR=arm64-apple-ios-ar ;; + x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;; ++ ++ aarch64-apple-tvos*-simulator) AR=arm64-apple-tvos-simulator-ar ;; ++ aarch64-apple-tvos*) AR=arm64-apple-tvos-ar ;; ++ x86_64-apple-tvos*-simulator) AR=x86_64-apple-tvos-simulator-ar ;; ++ ++ aarch64-apple-watchos*-simulator) AR=arm64-apple-watchos-simulator-ar ;; ++ aarch64-apple-watchos*) AR=arm64_32-apple-watchos-ar ;; ++ x86_64-apple-watchos*-simulator) AR=x86_64-apple-watchos-simulator-ar ;; ++ ++ aarch64-apple-xros*-simulator) AR=arm64-apple-xros-simulator-ar ;; ++ aarch64-apple-xros*) AR=arm64-apple-xros-ar ;; + *) + esac + fi +@@ -4220,6 +4244,17 @@ + aarch64-apple-ios*-simulator) CC=arm64-apple-ios-simulator-clang ;; + aarch64-apple-ios*) CC=arm64-apple-ios-clang ;; + x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;; ++ ++ aarch64-apple-tvos*-simulator) CC=arm64-apple-tvos-simulator-clang ;; ++ aarch64-apple-tvos*) CC=arm64-apple-tvos-clang ;; ++ x86_64-apple-tvos*-simulator) CC=x86_64-apple-tvos-simulator-clang ;; ++ ++ aarch64-apple-watchos*-simulator) CC=arm64-apple-watchos-simulator-clang ;; ++ aarch64-apple-watchos*) CC=arm64_32-apple-watchos-clang ;; ++ x86_64-apple-watchos*-simulator) CC=x86_64-apple-watchos-simulator-clang ;; ++ ++ aarch64-apple-xros*-simulator) CC=arm64-apple-xros-simulator-clang ;; ++ aarch64-apple-xros*) CC=arm64-apple-xros-clang ;; + *) + esac + fi +@@ -4228,6 +4263,17 @@ + aarch64-apple-ios*-simulator) CPP=arm64-apple-ios-simulator-cpp ;; + aarch64-apple-ios*) CPP=arm64-apple-ios-cpp ;; + x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;; ++ ++ aarch64-apple-tvos*-simulator) CPP=arm64-apple-tvos-simulator-cpp ;; ++ aarch64-apple-tvos*) CPP=arm64-apple-tvos-cpp ;; ++ x86_64-apple-tvos*-simulator) CPP=x86_64-apple-tvos-simulator-cpp ;; ++ ++ aarch64-apple-watchos*-simulator) CPP=arm64-apple-watchos-simulator-cpp ;; ++ aarch64-apple-watchos*) CPP=arm64_32-apple-watchos-cpp ;; ++ x86_64-apple-watchos*-simulator) CPP=x86_64-apple-watchos-simulator-cpp ;; ++ ++ aarch64-apple-xros*-simulator) CPP=arm64-apple-xros-simulator-cpp ;; ++ aarch64-apple-xros*) CPP=arm64-apple-xros-cpp ;; + *) + esac + fi +@@ -4236,6 +4282,17 @@ + aarch64-apple-ios*-simulator) CXX=arm64-apple-ios-simulator-clang++ ;; + aarch64-apple-ios*) CXX=arm64-apple-ios-clang++ ;; + x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;; ++ ++ aarch64-apple-tvos*-simulator) CXX=arm64-apple-tvos-simulator-clang++ ;; ++ aarch64-apple-tvos*) CXX=arm64-apple-tvos-clang++ ;; ++ x86_64-apple-tvos*-simulator) CXX=x86_64-apple-tvos-simulator-clang++ ;; ++ ++ aarch64-apple-watchos*-simulator) CXX=arm64-apple-watchos-simulator-clang++ ;; ++ aarch64-apple-watchos*) CXX=arm64_32-apple-watchos-clang++ ;; ++ x86_64-apple-watchos*-simulator) CXX=x86_64-apple-watchos-simulator-clang++ ;; ++ ++ aarch64-apple-xros*-simulator) CXX=arm64-apple-xros-simulator-clang++ ;; ++ aarch64-apple-xros*) CXX=arm64-apple-xros-clang++ ;; + *) + esac + fi +@@ -4358,8 +4415,11 @@ + case $enableval in + yes) + case $ac_sys_system in +- Darwin) enableval=/Library/Frameworks ;; +- iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;; ++ Darwin) enableval=/Library/Frameworks ;; ++ iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;; ++ tvOS) enableval=tvOS/Frameworks/\$\(MULTIARCH\) ;; ++ watchOS) enableval=watchOS/Frameworks/\$\(MULTIARCH\) ;; ++ visionOS) enableval=visionOS/Frameworks/\$\(MULTIARCH\) ;; + *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 + esac + esac +@@ -4368,6 +4428,9 @@ + no) + case $ac_sys_system in + iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; ++ tvOS) as_fn_error $? "tvOS builds must use --enable-framework" "$LINENO" 5 ;; ++ watchOS) as_fn_error $? "watchOS builds must use --enable-framework" "$LINENO" 5 ;; ++ visionOS) as_fn_error $? "visionOS builds must use --enable-framework" "$LINENO" 5 ;; + *) + PYTHONFRAMEWORK= + PYTHONFRAMEWORKDIR=no-framework +@@ -4474,6 +4537,51 @@ + + ac_config_files="$ac_config_files iOS/Resources/Info.plist" + ++ ;; ++ tvOS) : ++ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=tvOS/Resources ++ ++ ac_config_files="$ac_config_files tvOS/Resources/Info.plist" ++ ++ ;; ++ watchOS) : ++ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=watchOS/Resources ++ ++ ac_config_files="$ac_config_files watchOS/Resources/Info.plist" ++ ++ ;; ++ visionOS) : ++ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=visionOS/Resources ++ ++ ac_config_files="$ac_config_files visionOS/Resources/Info.plist" ++ + ;; + *) + as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 +@@ -4485,6 +4593,9 @@ + e) + case $ac_sys_system in + iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; ++ tvOS) as_fn_error $? "tvOS builds must use --enable-framework" "$LINENO" 5 ;; ++ watchOS) as_fn_error $? "watchOS builds must use --enable-framework" "$LINENO" 5 ;; ++ visionOS) as_fn_error $? "visionOS builds must use --enable-framework" "$LINENO" 5 ;; *) -- _host_cpu=$host_cpu -+ _host_ident=$host_cpu + PYTHONFRAMEWORK= + PYTHONFRAMEWORKDIR=no-framework +@@ -4539,8 +4650,8 @@ + case "$withval" in + yes) + case $ac_sys_system in +- Darwin|iOS) +- # iOS is able to share the macOS patch ++ Darwin|iOS|tvOS|watchOS|visionOS) ++ # iOS/tvOS/watchOS/visionOS is able to share the macOS patch + APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" + ;; + *) as_fn_error $? "no default app store compliance patch available for $ac_sys_system" "$LINENO" 5 ;; +@@ -4558,8 +4669,8 @@ + else case e in #( + e) + case $ac_sys_system in +- iOS) +- # Always apply the compliance patch on iOS; we can use the macOS patch ++ iOS|tvOS|watchOS|visionOS) ++ # Always apply the compliance patch on iOS/tvOS/watchOS/visionOS; we can use the macOS patch + APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: applying default app store compliance patch" >&5 + printf "%s\n" "applying default app store compliance patch" >&6; } +@@ -4577,6 +4688,8 @@ + + + ++EXPORT_XROS_DEPLOYMENT_TARGET='#' ++ + + if test "$cross_compiling" = yes; then + case "$host" in +@@ -4614,6 +4727,78 @@ + ;; 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 -+ ;; -+ *-apple-watchos*-simulator) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` ++ *-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}-watchsimualtor-arm64 -+ ;; -+ *) -+ _host_ident=${_host_os_min_version:3}-watchsimualtor-$host_cpu -+ esac -+ ;; -+ *-apple-watchos*) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` ++ aarch64) ++ _host_ident=${TVOS_DEPLOYMENT_TARGET}-arm64-appletv${_host_device} ++ ;; ++ *) ++ _host_ident=${TVOS_DEPLOYMENT_TARGET}-$host_cpu-appletv${_host_device} ++ ;; ++ esac ++ ;; ++ *-apple-watchos*) ++ _host_os=`echo $host | cut -d '-' -f3` ++ _host_device=`echo $host | cut -d '-' -f4` ++ _host_device=${_host_device:=os} ++ ++ # WATCHOS_DEPLOYMENT_TARGET is the minimum supported watchOS version ++ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking watchOS deployment target" >&5 ++printf %s "checking watchOS deployment target... " >&6; } ++ WATCHOS_DEPLOYMENT_TARGET=${_host_os:7} ++ WATCHOS_DEPLOYMENT_TARGET=${WATCHOS_DEPLOYMENT_TARGET:=4.0} ++ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $WATCHOS_DEPLOYMENT_TARGET" >&5 ++printf "%s\n" "$WATCHOS_DEPLOYMENT_TARGET" >&6; } ++ + case "$host_cpu" in -+ aarch64) -+ _host_ident=${_host_os_min_version:3}-watchosos-arm64_32 -+ ;; -+ *) -+ _host_ident=${_host_os_min_version:3}-watchosos-$host_cpu -+ esac -+ ;; -+ *-apple-*) ++ aarch64) ++ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-arm64-watch${_host_device} ++ ;; ++ *) ++ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-$host_cpu-watch${_host_device} ++ ;; ++ esac ++ ;; ++ *-apple-xros*) ++ _host_os=`echo $host | cut -d '-' -f3` ++ _host_device=`echo $host | cut -d '-' -f4` ++ _host_device=${_host_device:=os} ++ ++ # XROS_DEPLOYMENT_TARGET is the minimum supported visionOS version ++ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking visionOS deployment target" >&5 ++printf %s "checking visionOS deployment target... " >&6; } ++ XROS_DEPLOYMENT_TARGET=${_host_os:8} ++ XROS_DEPLOYMENT_TARGET=${XROS_DEPLOYMENT_TARGET:=2.0} ++ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $XROS_DEPLOYMENT_TARGET" >&5 ++printf "%s\n" "$XROS_DEPLOYMENT_TARGET" >&6; } ++ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking exporting flag of visionOS deployment target" >&5 ++printf %s "checking exporting flag of visionOS deployment target... " >&6; } ++ export XROS_DEPLOYMENT_TARGET ++ EXPORT_XROS_DEPLOYMENT_TARGET='' ++ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $EXPORT_XROS_DEPLOYMENT_TARGET" >&5 ++printf "%s\n" "$EXPORT_XROS_DEPLOYMENT_TARGET" >&6; } ++ + case "$host_cpu" in -+ arm*) -+ _host_ident=arm -+ ;; -+ *) -+ _host_ident=$host_cpu ++ aarch64) ++ _host_ident=${XROS_DEPLOYMENT_TARGET}-arm64-xr${_host_device} ++ ;; ++ *) ++ _host_ident=${XROS_DEPLOYMENT_TARGET}-$host_cpu-xr${_host_device} ++ ;; + esac - ;; - *-*-vxworks*) -- _host_cpu=$host_cpu -+ _host_ident=$host_cpu - ;; - wasm32-*-* | wasm64-*-*) -- _host_cpu=$host_cpu -+ _host_ident=$host_cpu - ;; - *) - # for now, limit cross builds to known configurations - MACHDEP="unknown" - as_fn_error $? "cross build not supported for $host" "$LINENO" 5 - esac -- _PYTHON_HOST_PLATFORM="$MACHDEP${_host_cpu:+-$_host_cpu}" -+ _PYTHON_HOST_PLATFORM="$MACHDEP${_host_ident:+-$_host_ident}" - fi - - # Some systems cannot stand _XOPEN_SOURCE being defined at all; they -@@ -4390,6 +4468,13 @@ ++ ;; + *-*-darwin*) + case "$host_cpu" in + arm*) +@@ -4704,9 +4889,15 @@ define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; -+ # On iOS, defining _POSIX_C_SOURCE also disables platform specific features. -+ iOS/*) -+ define_xopen_source=no;; +- # On iOS, defining _POSIX_C_SOURCE also disables platform specific features. ++ # On iOS/tvOS/watchOS/visionOS, defining _POSIX_C_SOURCE also disables platform specific features. + iOS/*) + define_xopen_source=no;; + tvOS/*) + define_xopen_source=no;; + watchOS/*) ++ define_xopen_source=no;; ++ visionOS/*) + define_xopen_source=no;; # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -6746,6 +6831,12 @@ - case $ac_sys_system in #( - Darwin*) : +@@ -4769,7 +4960,13 @@ + CONFIGURE_MACOSX_DEPLOYMENT_TARGET= + EXPORT_MACOSX_DEPLOYMENT_TARGET='#' + +-# Record the value of IPHONEOS_DEPLOYMENT_TARGET enforced by the selected host triple. ++# Record the value of IPHONEOS_DEPLOYMENT_TARGET / TVOS_DEPLOYMENT_TARGET / ++# WATCHOS_DEPLOYMENT_TARGET / XROS_DEPLOYMENT_TARGET enforced by the selected host triple. ++ ++ ++ ++ ++# XROS_DEPLOYMENT_TARGET should get exported + + + # checks for alternative programs +@@ -4810,6 +5007,16 @@ + as_fn_append CFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" + as_fn_append LDFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" + ;; #( ++ tvOS) : ++ ++ as_fn_append CFLAGS " -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}" ++ as_fn_append LDFLAGS " -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}" ++ ;; #( ++ watchOS) : ++ ++ as_fn_append CFLAGS " -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}" ++ as_fn_append LDFLAGS " -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}" ++ ;; #( + *) : + ;; + esac +@@ -7179,6 +7386,12 @@ + MULTIARCH="" ;; #( + iOS) : MULTIARCH="" ;; #( -+ iOS) : -+ MULTIARCH="" ;; #( + tvOS) : + MULTIARCH="" ;; #( + watchOS) : ++ MULTIARCH="" ;; #( ++ visionOS) : + MULTIARCH="" ;; #( FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -6753,9 +6844,6 @@ - ;; - esac +@@ -7199,7 +7412,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 #( -+ 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 @@ + case $ac_sys_system in #( +- iOS) : ++ iOS|tvOS|watchOS|visionOS) : + SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2` ;; #( + *) : + SOABI_PLATFORM=$PLATFORM_TRIPLET +@@ -7250,6 +7463,18 @@ PY_SUPPORT_TIER=3 ;; #( - 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 @@ - BLDSHARED="$LDSHARED" - fi ++ arm64_32-apple-watchos*/clang) : ++ PY_SUPPORT_TIER=3 ;; #( ++ aarch64-apple-xros*-simulator/clang) : ++ PY_SUPPORT_TIER=3 ;; #( ++ aarch64-apple-xros*/clang) : ++ PY_SUPPORT_TIER=3 ;; #( + aarch64-*-linux-android/clang) : + PY_SUPPORT_TIER=3 ;; #( + x86_64-*-linux-android/clang) : +@@ -7686,7 +7911,7 @@ + case $ac_sys_system in + Darwin) + LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; +- iOS) ++ iOS|tvOS|watchOS|visionOS) + LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; + *) + as_fn_error $? "Unknown platform for framework build" "$LINENO" 5;; +@@ -7752,7 +7977,7 @@ + BLDLIBRARY='-L. -lpython$(LDVERSION)' + RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} + ;; +- iOS) ++ iOS|tvOS|watchOS|visionOS) + LDLIBRARY='libpython$(LDVERSION).dylib' + ;; + AIX*) +@@ -13574,7 +13799,7 @@ + BLDSHARED="$LDSHARED" + fi ;; -+ 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 @@ +- iOS/*) ++ iOS/*|tvOS/*|watchOS/*|visionOS/*) + LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' + LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' + BLDSHARED="$LDSHARED" +@@ -13707,7 +13932,7 @@ + Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; + Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; + # -u libsys_s pulls in all symbols in libsys +- Darwin/*|iOS/*) ++ Darwin/*|iOS/*|tvOS/*|watchOS/*|visionOS/*) + LINKFORSHARED="$extra_undefs -framework CoreFoundation" + + # Issue #18075: the default maximum stack size (8MBytes) is too +@@ -13731,7 +13956,7 @@ + LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' + fi + LINKFORSHARED="$LINKFORSHARED" +- elif test $ac_sys_system = "iOS"; then ++ elif test "$ac_sys_system" = "iOS" -o "$ac_sys_system" = "tvOS" -o "$ac_sys_system" = "watchOS" -o "$ac_sys_system" = "visionOS"; then + LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' + fi + ;; +@@ -15508,7 +15733,7 @@ ctypes_malloc_closure=yes ;; #( -+ iOS|tvOS|watchOS) : +- iOS) : ++ iOS|tvOS|watchOS|visionOS) : + + ctypes_malloc_closure=yes + ;; #( +@@ -19260,12 +19485,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 +@@ -19326,18 +19545,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 +@@ -19764,24 +19971,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 +@@ -20100,12 +20289,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 +@@ -20374,11 +20557,11 @@ + + fi + +-# iOS defines some system methods that can be linked (so they are ++# iOS/tvOS/watchOS/visionOS define some system methods that can be linked (so they are + # found by configure), but either raise a compilation error (because the + # header definition prevents usage - autoconf doesn't use the headers), or + # raise an error if used at runtime. Force these symbols off. +-if test "$ac_sys_system" != "iOS" ; then ++if test "$ac_sys_system" != "iOS" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" -a "$ac_sys_system" != "visionOS" ; then + ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" + if test "x$ac_cv_func_getentropy" = xyes + then : +@@ -20400,6 +20583,53 @@ + + fi + ++# tvOS/watchOS have some additional methods that can be found, but not used. ++if test "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" ; then ++ ac_fn_c_check_func "$LINENO" "execv" "ac_cv_func_execv" ++if test "x$ac_cv_func_execv" = xyes ++then : ++ printf "%s\n" "#define HAVE_EXECV 1" >>confdefs.h + -+ 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; } ++fi ++ac_fn_c_check_func "$LINENO" "fork" "ac_cv_func_fork" ++if test "x$ac_cv_func_fork" = xyes ++then : ++ printf "%s\n" "#define HAVE_FORK 1" >>confdefs.h ++ ++fi ++ac_fn_c_check_func "$LINENO" "fork1" "ac_cv_func_fork1" ++if test "x$ac_cv_func_fork1" = xyes ++then : ++ printf "%s\n" "#define HAVE_FORK1 1" >>confdefs.h ++ ++fi ++ac_fn_c_check_func "$LINENO" "posix_spawn" "ac_cv_func_posix_spawn" ++if test "x$ac_cv_func_posix_spawn" = xyes ++then : ++ printf "%s\n" "#define HAVE_POSIX_SPAWN 1" >>confdefs.h ++ ++fi ++ac_fn_c_check_func "$LINENO" "posix_spawnp" "ac_cv_func_posix_spawnp" ++if test "x$ac_cv_func_posix_spawnp" = xyes ++then : ++ printf "%s\n" "#define HAVE_POSIX_SPAWNP 1" >>confdefs.h ++ ++fi ++ac_fn_c_check_func "$LINENO" "posix_spawn_file_actions_addclosefrom_np" "ac_cv_func_posix_spawn_file_actions_addclosefrom_np" ++if test "x$ac_cv_func_posix_spawn_file_actions_addclosefrom_np" = xyes ++then : ++ printf "%s\n" "#define HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP 1" >>confdefs.h ++ ++fi ++ac_fn_c_check_func "$LINENO" "sigaltstack" "ac_cv_func_sigaltstack" ++if test "x$ac_cv_func_sigaltstack" = xyes ++then : ++ printf "%s\n" "#define HAVE_SIGALTSTACK 1" >>confdefs.h ++ ++fi ++ ++fi ++ + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5 + printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; } + if test ${ac_cv_c_undeclared_builtin_options+y} +@@ -23844,7 +24074,8 @@ + + + # 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 -@@ -23660,7 +23772,7 @@ - if test "$Py_DEBUG" = 'true'; then - # Similar to SOABI but remove "d" flag from ABIFLAGS + for ac_func in openpty + do : +@@ -23958,7 +24189,7 @@ + fi -- 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} + done +-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing login_tty" >&5 ++ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing login_tty" >&5 + printf %s "checking for library containing login_tty... " >&6; } + if test ${ac_cv_search_login_tty+y} + then : +@@ -24141,6 +24372,7 @@ + fi - printf "%s\n" "#define ALT_SOABI \"${ALT_SOABI}\"" >>confdefs.h + done ++fi + + # check for long file support functions + ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" +@@ -24406,10 +24638,10 @@ + + done + +-# On Android and iOS, clock_settime can be linked (so it is found by ++# On Android, iOS, tvOS, watchOS, and visionOS, clock_settime can be linked (so it is found by + # configure), but when used in an unprivileged process, it crashes rather than + # returning an error. Force the symbol off. +-if test "$ac_sys_system" != "Linux-android" && test "$ac_sys_system" != "iOS" ++if test "$ac_sys_system" != "Linux-android" -a "$ac_sys_system" != "iOS" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" -a "$ac_sys_system" != "visionOS" + then + + for ac_func in clock_settime +@@ -24726,7 +24958,7 @@ + e) if test "$cross_compiling" = yes + then : + +-if test "$ac_sys_system" = "Linux-android" || test "$ac_sys_system" = "iOS"; then ++if test "$ac_sys_system" = "Linux-android" || test "$ac_sys_system" = "iOS" || test "$ac_sys_system" = "tvOS" || test "$ac_sys_system" = "watchOS" || test "$ac_sys_system" = "visionOS"; then + ac_cv_buggy_getaddrinfo="no" + elif test "${enable_ipv6+set}" = set; then + ac_cv_buggy_getaddrinfo="no -- configured with --(en|dis)able-ipv6" +@@ -26748,8 +26980,8 @@ + LIBPYTHON="\$(BLDLIBRARY)" + fi -@@ -27949,6 +28061,28 @@ +-# On iOS the shared libraries must be linked with the Python framework +-if test "$ac_sys_system" = "iOS"; then ++# On iOS/tvOS/watchOS the shared libraries must be linked with the Python framework ++if test "$ac_sys_system" = "iOS" -o $ac_sys_system = "tvOS" -o $ac_sys_system = "watchOS" -o $ac_sys_system = "visionOS"; then + MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" + fi + +@@ -29619,7 +29851,7 @@ + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for device files" >&5 + printf "%s\n" "$as_me: checking for device files" >&6;} + +-if test "$ac_sys_system" = "Linux-android" || test "$ac_sys_system" = "iOS"; then ++if test "$ac_sys_system" = "Linux-android" -o "$ac_sys_system" = "iOS" -o "$ac_sys_system" = "tvOS" -o "$ac_sys_system" = "watchOS" -o "$ac_sys_system" = "visionOS" ; then + ac_cv_file__dev_ptmx=no + ac_cv_file__dev_ptc=no + else +@@ -30129,7 +30361,7 @@ + with_ensurepip=no ;; #( + WASI) : + with_ensurepip=no ;; #( +- iOS) : ++ iOS|tvOS|watchOS|visionOS) : + with_ensurepip=no ;; #( + *) : + with_ensurepip=upgrade +@@ -31078,7 +31310,7 @@ + SunOS*) _PYTHREAD_NAME_MAXLEN=31;; + NetBSD*) _PYTHREAD_NAME_MAXLEN=15;; # gh-131268 + Darwin) _PYTHREAD_NAME_MAXLEN=63;; +- iOS) _PYTHREAD_NAME_MAXLEN=63;; ++ iOS|tvOS|watchOS|visionOS) _PYTHREAD_NAME_MAXLEN=63;; + FreeBSD*) _PYTHREAD_NAME_MAXLEN=19;; # gh-131268 + OpenBSD*) _PYTHREAD_NAME_MAXLEN=23;; # gh-131268 + *) _PYTHREAD_NAME_MAXLEN=;; +@@ -31110,7 +31342,7 @@ ;; #( Darwin) : ;; #( -+ iOS|tvOS|watchOS) : -+ -+ -+ -+ py_cv_module__curses=n/a -+ py_cv_module__curses_panel=n/a -+ py_cv_module__gdbm=n/a -+ py_cv_module__multiprocessing=n/a -+ py_cv_module__posixshmem=n/a -+ py_cv_module__posixsubprocess=n/a -+ py_cv_module__scproxy=n/a -+ py_cv_module__tkinter=n/a -+ py_cv_module__xxsubinterpreters=n/a -+ py_cv_module_grp=n/a -+ py_cv_module_nis=n/a -+ py_cv_module_readline=n/a -+ py_cv_module_pwd=n/a -+ py_cv_module_spwd=n/a -+ py_cv_module_syslog=n/a -+ py_cv_module_=n/a -+ -+ ;; #( - CYGWIN*) : +- iOS) : ++ iOS|tvOS|watchOS|visionOS) : -@@ -32186,4 +32320,3 @@ - CPython core team, see https://peps.python.org/pep-0011/ for more information. - " >&2;} - fi -- + +@@ -35272,6 +35504,9 @@ + "Mac/Resources/framework/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/framework/Info.plist" ;; + "Mac/Resources/app/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/app/Info.plist" ;; + "iOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES iOS/Resources/Info.plist" ;; ++ "tvOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES tvOS/Resources/Info.plist" ;; ++ "watchOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES watchOS/Resources/Info.plist" ;; ++ "visionOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES visionOS/Resources/Info.plist" ;; + "Makefile.pre") CONFIG_FILES="$CONFIG_FILES Makefile.pre" ;; + "Misc/python.pc") CONFIG_FILES="$CONFIG_FILES Misc/python.pc" ;; + "Misc/python-embed.pc") CONFIG_FILES="$CONFIG_FILES Misc/python-embed.pc" ;; diff --git a/configure.ac b/configure.ac -index cd69f0ede5..135036bf67 100644 +index cf25148bad2..7ab0609bf8a 100644 --- a/configure.ac +++ b/configure.ac -@@ -553,6 +553,15 @@ - *-*-cygwin*) - ac_sys_system=Cygwin +@@ -330,6 +330,15 @@ + *-apple-ios*) + ac_sys_system=iOS ;; -+ *-apple-ios*) -+ ac_sys_system=iOS -+ ;; + *-apple-tvos*) + ac_sys_system=tvOS + ;; + *-apple-watchos*) + ac_sys_system=watchOS + ;; - *-*-vxworks*) - ac_sys_system=VxWorks - ;; -@@ -607,27 +616,96 @@ - *-*-linux*) - case "$host_cpu" in - arm*) -- _host_cpu=arm -+ _host_ident=arm - ;; ++ *-apple-xros*) ++ ac_sys_system=visionOS ++ ;; + *-*-darwin*) + ac_sys_system=Darwin + ;; +@@ -405,7 +414,7 @@ + # On cross-compile builds, configure will look for a host-specific compiler by + # prepending the user-provided host triple to the required binary name. + # +-# On iOS, this results in binaries like "arm64-apple-ios13.0-simulator-gcc", ++# On iOS/tvOS/watchOS/visionOS, this results in binaries like "arm64-apple-ios13.0-simulator-gcc", + # which isn't a binary that exists, and isn't very convenient, as it contains the + # iOS version. As the default cross-compiler name won't exist, configure falls + # back to gcc, which *definitely* won't work. We're providing wrapper scripts for +@@ -420,6 +429,17 @@ + aarch64-apple-ios*-simulator) AR=arm64-apple-ios-simulator-ar ;; + aarch64-apple-ios*) AR=arm64-apple-ios-ar ;; + x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;; ++ ++ aarch64-apple-tvos*-simulator) AR=arm64-apple-tvos-simulator-ar ;; ++ aarch64-apple-tvos*) AR=arm64-apple-tvos-ar ;; ++ x86_64-apple-tvos*-simulator) AR=x86_64-apple-tvos-simulator-ar ;; ++ ++ aarch64-apple-watchos*-simulator) AR=arm64-apple-watchos-simulator-ar ;; ++ aarch64-apple-watchos*) AR=arm64_32-apple-watchos-ar ;; ++ x86_64-apple-watchos*-simulator) AR=x86_64-apple-watchos-simulator-ar ;; ++ ++ aarch64-apple-xros*-simulator) AR=arm64-apple-xros-simulator-ar ;; ++ aarch64-apple-xros*) AR=arm64-apple-xros-ar ;; + *) + esac + fi +@@ -428,6 +448,17 @@ + aarch64-apple-ios*-simulator) CC=arm64-apple-ios-simulator-clang ;; + aarch64-apple-ios*) CC=arm64-apple-ios-clang ;; + x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;; ++ ++ aarch64-apple-tvos*-simulator) CC=arm64-apple-tvos-simulator-clang ;; ++ aarch64-apple-tvos*) CC=arm64-apple-tvos-clang ;; ++ x86_64-apple-tvos*-simulator) CC=x86_64-apple-tvos-simulator-clang ;; ++ ++ aarch64-apple-watchos*-simulator) CC=arm64-apple-watchos-simulator-clang ;; ++ aarch64-apple-watchos*) CC=arm64_32-apple-watchos-clang ;; ++ x86_64-apple-watchos*-simulator) CC=x86_64-apple-watchos-simulator-clang ;; ++ ++ aarch64-apple-xros*-simulator) CC=arm64-apple-xros-simulator-clang ;; ++ aarch64-apple-xros*) CC=arm64-apple-xros-clang ;; + *) + esac + fi +@@ -436,6 +467,17 @@ + aarch64-apple-ios*-simulator) CPP=arm64-apple-ios-simulator-cpp ;; + aarch64-apple-ios*) CPP=arm64-apple-ios-cpp ;; + x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;; ++ ++ aarch64-apple-tvos*-simulator) CPP=arm64-apple-tvos-simulator-cpp ;; ++ aarch64-apple-tvos*) CPP=arm64-apple-tvos-cpp ;; ++ x86_64-apple-tvos*-simulator) CPP=x86_64-apple-tvos-simulator-cpp ;; ++ ++ aarch64-apple-watchos*-simulator) CPP=arm64-apple-watchos-simulator-cpp ;; ++ aarch64-apple-watchos*) CPP=arm64_32-apple-watchos-cpp ;; ++ x86_64-apple-watchos*-simulator) CPP=x86_64-apple-watchos-simulator-cpp ;; ++ ++ aarch64-apple-xros*-simulator) CPP=arm64-apple-xros-simulator-cpp ;; ++ aarch64-apple-xros*) CPP=arm64-apple-xros-cpp ;; + *) + esac + fi +@@ -444,6 +486,17 @@ + aarch64-apple-ios*-simulator) CXX=arm64-apple-ios-simulator-clang++ ;; + aarch64-apple-ios*) CXX=arm64-apple-ios-clang++ ;; + x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;; ++ ++ aarch64-apple-tvos*-simulator) CXX=arm64-apple-tvos-simulator-clang++ ;; ++ aarch64-apple-tvos*) CXX=arm64-apple-tvos-clang++ ;; ++ x86_64-apple-tvos*-simulator) CXX=x86_64-apple-tvos-simulator-clang++ ;; ++ ++ aarch64-apple-watchos*-simulator) CXX=arm64-apple-watchos-simulator-clang++ ;; ++ aarch64-apple-watchos*) CXX=arm64_32-apple-watchos-clang++ ;; ++ x86_64-apple-watchos*-simulator) CXX=x86_64-apple-watchos-simulator-clang++ ;; ++ ++ aarch64-apple-xros*-simulator) CXX=arm64-apple-xros-simulator-clang++ ;; ++ aarch64-apple-xros*) CXX=arm64-apple-xros-clang++ ;; *) -- _host_cpu=$host_cpu -+ _host_ident=$host_cpu + esac + fi +@@ -558,8 +611,11 @@ + case $enableval in + yes) + case $ac_sys_system in +- Darwin) enableval=/Library/Frameworks ;; +- iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;; ++ Darwin) enableval=/Library/Frameworks ;; ++ iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;; ++ tvOS) enableval=tvOS/Frameworks/\$\(MULTIARCH\) ;; ++ watchOS) enableval=watchOS/Frameworks/\$\(MULTIARCH\) ;; ++ visionOS) enableval=visionOS/Frameworks/\$\(MULTIARCH\) ;; + *) AC_MSG_ERROR([Unknown platform for framework build]) + esac + esac +@@ -568,6 +624,9 @@ + no) + case $ac_sys_system in + iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;; ++ tvOS) AC_MSG_ERROR([tvOS builds must use --enable-framework]) ;; ++ watchOS) AC_MSG_ERROR([watchOS builds must use --enable-framework]) ;; ++ visionOS) AC_MSG_ERROR([visionOS builds must use --enable-framework]) ;; + *) + PYTHONFRAMEWORK= + PYTHONFRAMEWORKDIR=no-framework +@@ -670,6 +729,48 @@ + + AC_CONFIG_FILES([iOS/Resources/Info.plist]) + ;; ++ tvOS) : ++ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=tvOS/Resources ++ ++ AC_CONFIG_FILES([tvOS/Resources/Info.plist]) ++ ;; ++ watchOS) : ++ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=watchOS/Resources ++ ++ AC_CONFIG_FILES([watchOS/Resources/Info.plist]) ++ ;; ++ visionOS) : ++ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=visionOS/Resources ++ ++ AC_CONFIG_FILES([visionOS/Resources/Info.plist]) ++ ;; + *) + AC_MSG_ERROR([Unknown platform for framework build]) + ;; +@@ -678,6 +779,9 @@ + ],[ + case $ac_sys_system in + iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;; ++ tvOS) AC_MSG_ERROR([tvOS builds must use --enable-framework]) ;; ++ watchOS) AC_MSG_ERROR([watchOS builds must use --enable-framework]) ;; ++ visionOS) AC_MSG_ERROR([visionOS builds must use --enable-framework]) ;; + *) + PYTHONFRAMEWORK= + PYTHONFRAMEWORKDIR=no-framework +@@ -730,8 +834,8 @@ + case "$withval" in + yes) + case $ac_sys_system in +- Darwin|iOS) +- # iOS is able to share the macOS patch ++ Darwin|iOS|tvOS|watchOS|visionOS) ++ # iOS/tvOS/watchOS/visionOS is able to share the macOS patch + APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" + ;; + *) AC_MSG_ERROR([no default app store compliance patch available for $ac_sys_system]) ;; +@@ -745,8 +849,8 @@ + esac + ],[ + case $ac_sys_system in +- iOS) +- # Always apply the compliance patch on iOS; we can use the macOS patch ++ iOS|tvOS|watchOS|visionOS) ++ # Always apply the compliance patch on iOS/tvOS/watchOS/visionOS; we can use the macOS patch + APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" + AC_MSG_RESULT([applying default app store compliance patch]) + ;; +@@ -759,6 +863,8 @@ + ]) + AC_SUBST([APP_STORE_COMPLIANCE_PATCH]) + ++EXPORT_XROS_DEPLOYMENT_TARGET='#' ++ + AC_SUBST([_PYTHON_HOST_PLATFORM]) + if test "$cross_compiling" = yes; then + case "$host" in +@@ -794,6 +900,70 @@ + ;; esac ;; - *-*-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 -+ ;; -+ *-apple-watchos*-simulator) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` ++ *-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}-watchsimualtor-arm64 -+ ;; -+ *) -+ _host_ident=${_host_os_min_version:3}-watchsimualtor-$host_cpu -+ esac -+ ;; -+ *-apple-watchos*) -+ _host_os_min_version=`echo $host | cut -d '-' -f3` ++ aarch64) ++ _host_ident=${TVOS_DEPLOYMENT_TARGET}-arm64-appletv${_host_device} ++ ;; ++ *) ++ _host_ident=${TVOS_DEPLOYMENT_TARGET}-$host_cpu-appletv${_host_device} ++ ;; ++ esac ++ ;; ++ *-apple-watchos*) ++ _host_os=`echo $host | cut -d '-' -f3` ++ _host_device=`echo $host | cut -d '-' -f4` ++ _host_device=${_host_device:=os} ++ ++ # WATCHOS_DEPLOYMENT_TARGET is the minimum supported watchOS version ++ AC_MSG_CHECKING([watchOS deployment target]) ++ WATCHOS_DEPLOYMENT_TARGET=${_host_os:7} ++ WATCHOS_DEPLOYMENT_TARGET=${WATCHOS_DEPLOYMENT_TARGET:=4.0} ++ AC_MSG_RESULT([$WATCHOS_DEPLOYMENT_TARGET]) ++ + case "$host_cpu" in -+ aarch64) -+ _host_ident=${_host_os_min_version:3}-watchosos-arm64_32 -+ ;; -+ *) -+ _host_ident=${_host_os_min_version:3}-watchosos-$host_cpu -+ esac -+ ;; -+ *-apple-*) ++ aarch64) ++ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-arm64-watch${_host_device} ++ ;; ++ *) ++ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-$host_cpu-watch${_host_device} ++ ;; ++ esac ++ ;; ++ *-apple-xros*) ++ _host_os=`echo $host | cut -d '-' -f3` ++ _host_device=`echo $host | cut -d '-' -f4` ++ _host_device=${_host_device:=os} ++ ++ # XROS_DEPLOYMENT_TARGET is the minimum supported visionOS version ++ AC_MSG_CHECKING([visionOS deployment target]) ++ XROS_DEPLOYMENT_TARGET=${_host_os:8} ++ XROS_DEPLOYMENT_TARGET=${XROS_DEPLOYMENT_TARGET:=2.0} ++ AC_MSG_RESULT([$XROS_DEPLOYMENT_TARGET]) ++ AC_MSG_CHECKING([exporting flag of visionOS deployment target]) ++ export XROS_DEPLOYMENT_TARGET ++ EXPORT_XROS_DEPLOYMENT_TARGET='' ++ AC_MSG_RESULT([$EXPORT_XROS_DEPLOYMENT_TARGET]) ++ + case "$host_cpu" in -+ arm*) -+ _host_ident=arm -+ ;; -+ *) -+ _host_ident=$host_cpu ++ aarch64) ++ _host_ident=${XROS_DEPLOYMENT_TARGET}-arm64-xr${_host_device} ++ ;; ++ *) ++ _host_ident=${XROS_DEPLOYMENT_TARGET}-$host_cpu-xr${_host_device} ++ ;; + esac - ;; - *-*-vxworks*) -- _host_cpu=$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 @@ ++ ;; + *-*-darwin*) + case "$host_cpu" in + arm*) +@@ -883,9 +1053,15 @@ define_xopen_source=no;; Darwin/@<:@[12]@:>@@<:@0-9@:>@.*) define_xopen_source=no;; -+ # On iOS, defining _POSIX_C_SOURCE also disables platform specific features. -+ iOS/*) -+ define_xopen_source=no;; +- # On iOS, defining _POSIX_C_SOURCE also disables platform specific features. ++ # On iOS/tvOS/watchOS/visionOS, defining _POSIX_C_SOURCE also disables platform specific features. + iOS/*) + define_xopen_source=no;; + tvOS/*) + define_xopen_source=no;; + watchOS/*) ++ define_xopen_source=no;; ++ visionOS/*) + define_xopen_source=no;; # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -941,11 +1026,13 @@ - AC_MSG_CHECKING([for multiarch]) +@@ -944,8 +1120,14 @@ + CONFIGURE_MACOSX_DEPLOYMENT_TARGET= + EXPORT_MACOSX_DEPLOYMENT_TARGET='#' + +-# Record the value of IPHONEOS_DEPLOYMENT_TARGET enforced by the selected host triple. ++# Record the value of IPHONEOS_DEPLOYMENT_TARGET / TVOS_DEPLOYMENT_TARGET / ++# WATCHOS_DEPLOYMENT_TARGET / XROS_DEPLOYMENT_TARGET enforced by the selected host triple. + AC_SUBST([IPHONEOS_DEPLOYMENT_TARGET]) ++AC_SUBST([TVOS_DEPLOYMENT_TARGET]) ++AC_SUBST([WATCHOS_DEPLOYMENT_TARGET]) ++AC_SUBST([XROS_DEPLOYMENT_TARGET]) ++# XROS_DEPLOYMENT_TARGET should get exported ++AC_SUBST([EXPORT_XROS_DEPLOYMENT_TARGET]) + + # checks for alternative programs + +@@ -979,11 +1161,19 @@ + ], + ) + +-dnl Add the compiler flag for the iOS minimum supported OS version. ++dnl Add the compiler flag for the iOS/tvOS/watchOS minimum supported OS ++dnl version. visionOS doesn't use an explicit -mxros-version-min option - ++dnl it encodes the min version into the target triple. + AS_CASE([$ac_sys_system], + [iOS], [ + AS_VAR_APPEND([CFLAGS], [" -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"]) + AS_VAR_APPEND([LDFLAGS], [" -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"]) ++ ],[tvOS], [ ++ AS_VAR_APPEND([CFLAGS], [" -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}"]) ++ AS_VAR_APPEND([LDFLAGS], [" -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}"]) ++ ],[watchOS], [ ++ AS_VAR_APPEND([CFLAGS], [" -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}"]) ++ AS_VAR_APPEND([LDFLAGS], [" -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}"]) + ], + ) + +@@ -1172,6 +1362,9 @@ AS_CASE([$ac_sys_system], [Darwin*], [MULTIARCH=""], -+ [iOS], [MULTIARCH=""], + [iOS], [MULTIARCH=""], + [tvOS], [MULTIARCH=""], + [watchOS], [MULTIARCH=""], ++ [visionOS], [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] +@@ -1193,7 +1386,7 @@ + dnl use a single "fat" binary at runtime. SOABI_PLATFORM is the component of + dnl the PLATFORM_TRIPLET that will be used in binary module extensions. + AS_CASE([$ac_sys_system], +- [iOS], [SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2`], ++ [iOS|tvOS|watchOS|visionOS], [SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2`], + [SOABI_PLATFORM=$PLATFORM_TRIPLET] ) -@@ -3085,6 +3181,7 @@ - esac - ;; - CYGWIN*) SHLIB_SUFFIX=.dll;; -+ iOS|tvOS|watchOS) SHLIB_SUFFIX=.dylib;; - *) SHLIB_SUFFIX=.so;; - esac - fi -@@ -3165,6 +3262,9 @@ +@@ -1227,6 +1420,12 @@ + [x86_64-*-freebsd*/clang], [PY_SUPPORT_TIER=3], dnl FreeBSD on AMD64 + [aarch64-apple-ios*-simulator/clang], [PY_SUPPORT_TIER=3], dnl iOS Simulator on arm64 + [aarch64-apple-ios*/clang], [PY_SUPPORT_TIER=3], dnl iOS on ARM64 ++ [aarch64-apple-tvos*-simulator/clang], [PY_SUPPORT_TIER=3], dnl tvOS Simulator on arm64 ++ [aarch64-apple-tvos*/clang], [PY_SUPPORT_TIER=3], dnl tvOS on ARM64 ++ [aarch64-apple-watchos*-simulator/clang], [PY_SUPPORT_TIER=3], dnl watchOS Simulator on arm64 ++ [arm64_32-apple-watchos*/clang], [PY_SUPPORT_TIER=3], dnl watchOS on ARM64 ++ [aarch64-apple-xros*-simulator/clang], [PY_SUPPORT_TIER=3], dnl visionOS Simulator on arm64 ++ [aarch64-apple-xros*/clang], [PY_SUPPORT_TIER=3], dnl visionOS on ARM64 + [aarch64-*-linux-android/clang], [PY_SUPPORT_TIER=3], dnl Android on ARM64 + [x86_64-*-linux-android/clang], [PY_SUPPORT_TIER=3], dnl Android on AMD64 + +@@ -1536,7 +1735,7 @@ + case $ac_sys_system in + Darwin) + LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; +- iOS) ++ iOS|tvOS|watchOS|visionOS) + LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; + *) + AC_MSG_ERROR([Unknown platform for framework build]);; +@@ -1601,7 +1800,7 @@ + BLDLIBRARY='-L. -lpython$(LDVERSION)' + RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} + ;; +- iOS) ++ iOS|tvOS|watchOS|visionOS) + LDLIBRARY='libpython$(LDVERSION).dylib' + ;; + AIX*) +@@ -3470,7 +3669,7 @@ BLDSHARED="$LDSHARED" fi ;; -+ 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 @@ +- iOS/*) ++ iOS/*|tvOS/*|watchOS/*|visionOS/*) + LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' + LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' + BLDSHARED="$LDSHARED" +@@ -3594,7 +3793,7 @@ + Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; + Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; + # -u libsys_s pulls in all symbols in libsys +- Darwin/*|iOS/*) ++ Darwin/*|iOS/*|tvOS/*|watchOS/*|visionOS/*) + LINKFORSHARED="$extra_undefs -framework CoreFoundation" + + # Issue #18075: the default maximum stack size (8MBytes) is too +@@ -3618,7 +3817,7 @@ + LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' + fi + LINKFORSHARED="$LINKFORSHARED" +- elif test $ac_sys_system = "iOS"; then ++ elif test "$ac_sys_system" = "iOS" -o "$ac_sys_system" = "tvOS" -o "$ac_sys_system" = "watchOS" -o "$ac_sys_system" = "visionOS"; then + LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' + fi + ;; +@@ -4106,7 +4305,7 @@ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], -+ [iOS|tvOS|watchOS], [ -+ ctypes_malloc_closure=yes -+ ], +- [iOS], [ ++ [iOS|tvOS|watchOS|visionOS], [ + 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]) +@@ -5215,9 +5414,9 @@ + # checks for library functions + AC_CHECK_FUNCS([ \ + accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ +- copy_file_range ctermid dladdr dup dup3 execv explicit_bzero explicit_memset \ ++ copy_file_range ctermid dladdr dup dup3 explicit_bzero explicit_memset \ + faccessat fchmod fchmodat fchown fchownat fdopendir fdwalk fexecve \ +- fork fork1 fpathconf fstatat ftime ftruncate futimens futimes futimesat \ ++ fpathconf fstatat ftime ftruncate futimens futimes futimesat \ + gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \ + getgrnam_r getgrouplist gethostname getitimer getloadavg getlogin \ + getpeername getpgid getpid getppid getpriority _getpty \ +@@ -5225,8 +5424,7 @@ + getspnam getuid getwd grantpt if_nameindex initgroups kill killpg lchown linkat \ + lockf lstat lutimes madvise mbrtowc memrchr mkdirat mkfifo mkfifoat \ + mknod mknodat mktime mmap mremap nice openat opendir pathconf pause pipe \ +- pipe2 plock poll posix_fadvise posix_fallocate posix_openpt posix_spawn posix_spawnp \ +- posix_spawn_file_actions_addclosefrom_np \ ++ pipe2 plock poll posix_fadvise posix_fallocate posix_openpt \ + pread preadv preadv2 process_vm_readv \ + pthread_cond_timedwait_relative_np pthread_condattr_setclock pthread_init \ + pthread_kill pthread_get_name_np pthread_getname_np pthread_set_name_np +@@ -5236,7 +5434,7 @@ + sched_setparam sched_setscheduler sem_clockwait sem_getvalue sem_open \ + sem_timedwait sem_unlink sendfile setegid seteuid setgid sethostname \ + setitimer setlocale setpgid setpgrp setpriority setregid setresgid \ +- setresuid setreuid setsid setuid setvbuf shutdown sigaction sigaltstack \ ++ setresuid setreuid setsid setuid setvbuf shutdown sigaction \ + sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ + sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ + sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ +@@ -5251,12 +5449,20 @@ + AC_CHECK_FUNCS([lchmod]) + fi + +-# iOS defines some system methods that can be linked (so they are ++# iOS/tvOS/watchOS/visionOS define some system methods that can be linked (so they are + # found by configure), but either raise a compilation error (because the + # header definition prevents usage - autoconf doesn't use the headers), or + # raise an error if used at runtime. Force these symbols off. +-if test "$ac_sys_system" != "iOS" ; then +- AC_CHECK_FUNCS([getentropy getgroups system]) ++if test "$ac_sys_system" != "iOS" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" -a "$ac_sys_system" != "visionOS" ; then ++ AC_CHECK_FUNCS([ getentropy getgroups system ]) ++fi ++ ++# tvOS/watchOS have some additional methods that can be found, but not used. ++if test "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" ; then ++ AC_CHECK_FUNCS([ \ ++ execv fork fork1 posix_spawn posix_spawnp posix_spawn_file_actions_addclosefrom_np \ ++ sigaltstack \ ++ ]) fi -@@ -7068,6 +7171,29 @@ + + AC_CHECK_DECL([dirfd], +@@ -5539,20 +5745,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]) +@@ -5591,10 +5799,10 @@ + ]) + ]) + +-# On Android and iOS, clock_settime can be linked (so it is found by ++# On Android, iOS, tvOS, watchOS, and visionOS, clock_settime can be linked (so it is found by + # configure), but when used in an unprivileged process, it crashes rather than + # returning an error. Force the symbol off. +-if test "$ac_sys_system" != "Linux-android" && test "$ac_sys_system" != "iOS" ++if test "$ac_sys_system" != "Linux-android" -a "$ac_sys_system" != "iOS" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" -a "$ac_sys_system" != "visionOS" + then + AC_CHECK_FUNCS([clock_settime], [], [ + AC_CHECK_LIB([rt], [clock_settime], [ +@@ -5752,7 +5960,7 @@ + [ac_cv_buggy_getaddrinfo=no], + [ac_cv_buggy_getaddrinfo=yes], + [ +-if test "$ac_sys_system" = "Linux-android" || test "$ac_sys_system" = "iOS"; then ++if test "$ac_sys_system" = "Linux-android" || test "$ac_sys_system" = "iOS" || test "$ac_sys_system" = "tvOS" || test "$ac_sys_system" = "watchOS" || test "$ac_sys_system" = "visionOS"; then + ac_cv_buggy_getaddrinfo="no" + elif test "${enable_ipv6+set}" = set; then + ac_cv_buggy_getaddrinfo="no -- configured with --(en|dis)able-ipv6" +@@ -6345,8 +6553,8 @@ + LIBPYTHON="\$(BLDLIBRARY)" + fi + +-# On iOS the shared libraries must be linked with the Python framework +-if test "$ac_sys_system" = "iOS"; then ++# On iOS/tvOS/watchOS the shared libraries must be linked with the Python framework ++if test "$ac_sys_system" = "iOS" -o $ac_sys_system = "tvOS" -o $ac_sys_system = "watchOS" -o $ac_sys_system = "visionOS"; then + MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" + fi + +@@ -7005,7 +7213,7 @@ + dnl NOTE: Inform user how to proceed with files when cross compiling. + dnl Some cross-compile builds are predictable; they won't ever + dnl have /dev/ptmx or /dev/ptc, so we can set them explicitly. +-if test "$ac_sys_system" = "Linux-android" || test "$ac_sys_system" = "iOS"; then ++if test "$ac_sys_system" = "Linux-android" -o "$ac_sys_system" = "iOS" -o "$ac_sys_system" = "tvOS" -o "$ac_sys_system" = "watchOS" -o "$ac_sys_system" = "visionOS" ; then + ac_cv_file__dev_ptmx=no + ac_cv_file__dev_ptc=no + else +@@ -7307,7 +7515,7 @@ + AS_CASE([$ac_sys_system], + [Emscripten], [with_ensurepip=no], + [WASI], [with_ensurepip=no], +- [iOS], [with_ensurepip=no], ++ [iOS|tvOS|watchOS|visionOS], [with_ensurepip=no], + [with_ensurepip=upgrade] + ) + ]) +@@ -7694,7 +7902,7 @@ + SunOS*) _PYTHREAD_NAME_MAXLEN=31;; + NetBSD*) _PYTHREAD_NAME_MAXLEN=15;; # gh-131268 + Darwin) _PYTHREAD_NAME_MAXLEN=63;; +- iOS) _PYTHREAD_NAME_MAXLEN=63;; ++ iOS|tvOS|watchOS|visionOS) _PYTHREAD_NAME_MAXLEN=63;; + FreeBSD*) _PYTHREAD_NAME_MAXLEN=19;; # gh-131268 + OpenBSD*) _PYTHREAD_NAME_MAXLEN=23;; # gh-131268 + *) _PYTHREAD_NAME_MAXLEN=;; +@@ -7719,7 +7927,7 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], -+ [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], +- [iOS], [ ++ [iOS|tvOS|watchOS|visionOS], [ + dnl subprocess and multiprocessing are not supported (no fork syscall). + dnl curses and tkinter user interface are not available. + dnl gdbm and nis aren't available +diff --git a/iOS/Resources/Info.plist.in b/iOS/Resources/Info.plist.in +index c3e261ecd9e..26ef7a95de4 100644 +--- a/iOS/Resources/Info.plist.in ++++ b/iOS/Resources/Info.plist.in +@@ -17,13 +17,13 @@ + CFBundlePackageType + FMWK + CFBundleShortVersionString +- @VERSION@ ++ %VERSION% + CFBundleLongVersionString + %VERSION%, (c) 2001-2024 Python Software Foundation. + CFBundleSignature + ???? + CFBundleVersion +- 1 ++ %VERSION% + CFBundleSupportedPlatforms + + iPhoneOS +diff --git a/iOS/testbed/__main__.py b/iOS/testbed/__main__.py +index c05497ede3a..1146bf3b988 100644 +--- a/iOS/testbed/__main__.py ++++ b/iOS/testbed/__main__.py +@@ -127,7 +127,7 @@ + async def select_simulator_device(): + # List the testing simulators, in JSON format + raw_json = await async_check_output( +- "xcrun", "simctl", "--set", "testing", "list", "-j" ++ "xcrun", "simctl", "list", "-j" + ) + json_data = json.loads(raw_json) + +--- /dev/null ++++ b/tvOS/README.rst +@@ -0,0 +1,108 @@ ++===================== ++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-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-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-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/visionOS/Resources/Info.plist.in +@@ -0,0 +1,34 @@ ++ ++ ++ ++ ++ CFBundleDevelopmentRegion ++ en ++ CFBundleExecutable ++ Python ++ CFBundleGetInfoString ++ Python Runtime and Library ++ CFBundleIdentifier ++ @PYTHONFRAMEWORKIDENTIFIER@ ++ CFBundleInfoDictionaryVersion ++ 6.0 ++ CFBundleName ++ Python ++ CFBundlePackageType ++ FMWK ++ CFBundleShortVersionString ++ %VERSION% ++ CFBundleLongVersionString ++ %VERSION%, (c) 2001-2023 Python Software Foundation. ++ CFBundleSignature ++ ???? ++ CFBundleVersion ++ %VERSION% ++ CFBundleSupportedPlatforms ++ ++ XROS ++ ++ MinimumOSVersion ++ @XROS_DEPLOYMENT_TARGET@ ++ ++ +--- /dev/null ++++ b/visionOS/Resources/bin/arm64-apple-xros-ar +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk xros${XROS_SDK_VERSION} ar "$@" +--- /dev/null ++++ b/visionOS/Resources/bin/arm64-apple-xros-clang +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk xros${XROS_SDK_VERSION} clang -target arm64-apple-xros${XROS_DEPLOYMENT_TARGET} "$@" +--- /dev/null ++++ b/visionOS/Resources/bin/arm64-apple-xros-clang++ +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk xros${XROS_SDK_VERSION} clang++ -target arm64-apple-xros${XROS_DEPLOYMENT_TARGET} "$@" +--- /dev/null ++++ b/visionOS/Resources/bin/arm64-apple-xros-cpp +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk xros${XROS_SDK_VERSION} clang -target arm64-apple-xros${XROS_DEPLOYMENT_TARGET} -E "$@" +--- /dev/null ++++ b/visionOS/Resources/bin/arm64-apple-xros-simulator-ar +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk xrsimulator${XROS_SDK_VERSION} ar "$@" +--- /dev/null ++++ b/visionOS/Resources/bin/arm64-apple-xros-simulator-clang +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk xrsimulator${XROS_SDK_VERSION} clang -target arm64-apple-xros${XROS_DEPLOYMENT_TARGET}-simulator "$@" +--- /dev/null ++++ b/visionOS/Resources/bin/arm64-apple-xros-simulator-clang++ +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk xrsimulator${XROS_SDK_VERSION} clang++ -target arm64-apple-xros${XROS_DEPLOYMENT_TARGET}-simulator "$@" +--- /dev/null ++++ b/visionOS/Resources/bin/arm64-apple-xros-simulator-cpp +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk xrsimulator clang -target arm64-apple-xros${XROS_DEPLOYMENT_TARGET}-simulator -E "$@" +--- /dev/null ++++ b/visionOS/Resources/dylib-Info-template.plist +@@ -0,0 +1,30 @@ ++ ++ ++ ++ ++ CFBundleDevelopmentRegion ++ en ++ CFBundleInfoDictionaryVersion ++ 6.0 ++ CFBundleExecutable ++ ++ CFBundleIdentifier ++ ++ CFBundlePackageType ++ FMWK ++ CFBundleShortVersionString ++ 1.0 ++ CFBundleSupportedPlatforms ++ ++ XROS ++ ++ CFBundleVersion ++ 1 ++ MinimumOSVersion ++ 2.0 ++ UIDeviceFamily ++ ++ 7 ++ ++ ++ +--- /dev/null ++++ b/visionOS/testbed/Python.xcframework/Info.plist +@@ -0,0 +1,43 @@ ++ ++ ++ ++ ++ AvailableLibraries ++ ++ ++ BinaryPath ++ Python.framework/Python ++ LibraryIdentifier ++ xros-arm64-simulator ++ LibraryPath ++ Python.framework ++ SupportedArchitectures ++ ++ arm64 ++ ++ SupportedPlatform ++ xros ++ SupportedPlatformVariant ++ simulator ++ ++ ++ BinaryPath ++ Python.framework/Python ++ LibraryIdentifier ++ xros-arm64 ++ LibraryPath ++ Python.framework ++ SupportedArchitectures ++ ++ arm64 ++ ++ SupportedPlatform ++ xros ++ ++ ++ CFBundlePackageType ++ XFWK ++ XCFrameworkFormatVersion ++ 1.0 ++ ++ +--- /dev/null ++++ b/visionOS/testbed/Python.xcframework/xros-arm64-simulator/README +@@ -0,0 +1,4 @@ ++This directory is intentionally empty. ++ ++It should be used as a target for `--enable-framework` when compiling an visionOS simulator ++build for testing purposes (either x86_64 or ARM64). +--- /dev/null ++++ b/visionOS/testbed/Python.xcframework/xros-arm64/README +@@ -0,0 +1,4 @@ ++This directory is intentionally empty. ++ ++It should be used as a target for `--enable-framework` when compiling an visionOS on-device ++build for testing purposes. +--- /dev/null ++++ b/visionOS/testbed/__main__.py +@@ -0,0 +1,512 @@ ++import argparse ++import asyncio ++import fcntl ++import json ++import os ++import plistlib ++import re ++import shutil ++import subprocess ++import sys ++import tempfile ++from contextlib import asynccontextmanager ++from datetime import datetime ++from pathlib import Path ++ ++ ++DECODE_ARGS = ("UTF-8", "backslashreplace") ++ ++# The system log prefixes each line: ++# 2025-01-17 16:14:29.090 Df visionOSTestbed[23987:1fd393b4] (Python) ... ++# 2025-01-17 16:14:29.090 E visionOSTestbed[23987:1fd393b4] (Python) ... ++ ++LOG_PREFIX_REGEX = re.compile( ++ r"^\d{4}-\d{2}-\d{2}" # YYYY-MM-DD ++ r"\s+\d+:\d{2}:\d{2}\.\d+" # HH:MM:SS.sss ++ r"\s+\w+" # Df/E ++ r"\s+visionOSTestbed\[\d+:\w+\]" # Process/thread ID ++ r"\s+\(Python\)\s" # Logger name ++) ++ ++ ++# Work around a bug involving sys.exit and TaskGroups ++# (https://github.com/python/cpython/issues/101515). ++def exit(*args): ++ raise MySystemExit(*args) ++ ++ ++class MySystemExit(Exception): ++ pass ++ ++ ++class SimulatorLock: ++ # An fcntl-based filesystem lock that can be used to ensure that ++ def __init__(self, timeout): ++ self.filename = Path(tempfile.gettempdir()) / "python-visionos-testbed" ++ self.timeout = timeout ++ ++ self.fd = None ++ ++ async def acquire(self): ++ # Ensure the lockfile exists ++ self.filename.touch(exist_ok=True) ++ ++ # Try `timeout` times to acquire the lock file, with a 1 second pause ++ # between each attempt. Report status every 10 seconds. ++ for i in range(0, self.timeout): ++ try: ++ fd = os.open(self.filename, os.O_RDWR | os.O_TRUNC, 0o644) ++ fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) ++ except OSError: ++ os.close(fd) ++ if i % 10 == 0: ++ print("... waiting", flush=True) ++ await asyncio.sleep(1) ++ else: ++ self.fd = fd ++ return ++ ++ # If we reach the end of the loop, we've exceeded the allowed number of ++ # attempts. ++ raise ValueError("Unable to obtain lock on visionOS simulator creation") ++ ++ def release(self): ++ # If a lock is held, release it. ++ if self.fd is not None: ++ # Release the lock. ++ fcntl.flock(self.fd, fcntl.LOCK_UN) ++ os.close(self.fd) ++ self.fd = None ++ ++ ++# All subprocesses are executed through this context manager so that no matter ++# what happens, they can always be cancelled from another task, and they will ++# always be cleaned up on exit. ++@asynccontextmanager ++async def async_process(*args, **kwargs): ++ process = await asyncio.create_subprocess_exec(*args, **kwargs) ++ try: ++ yield process ++ finally: ++ if process.returncode is None: ++ # Allow a reasonably long time for Xcode to clean itself up, ++ # because we don't want stale emulators left behind. ++ timeout = 10 ++ process.terminate() ++ try: ++ await asyncio.wait_for(process.wait(), timeout) ++ except TimeoutError: ++ print( ++ f"Command {args} did not terminate after {timeout} seconds " ++ f" - sending SIGKILL" ++ ) ++ process.kill() ++ ++ # Even after killing the process we must still wait for it, ++ # otherwise we'll get the warning "Exception ignored in __del__". ++ await asyncio.wait_for(process.wait(), timeout=1) ++ ++ ++async def async_check_output(*args, **kwargs): ++ async with async_process( ++ *args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs ++ ) as process: ++ stdout, stderr = await process.communicate() ++ if process.returncode == 0: ++ return stdout.decode(*DECODE_ARGS) ++ else: ++ raise subprocess.CalledProcessError( ++ process.returncode, ++ args, ++ stdout.decode(*DECODE_ARGS), ++ stderr.decode(*DECODE_ARGS), ++ ) ++ ++ ++# Return a list of UDIDs associated with booted simulators ++async def list_devices(): ++ try: ++ # List the testing simulators, in JSON format ++ raw_json = await async_check_output( ++ "xcrun", "simctl", "--set", "testing", "list", "-j" ++ ) ++ json_data = json.loads(raw_json) ++ ++ # Filter out the booted visionOS simulators ++ return [ ++ simulator["udid"] ++ for runtime, simulators in json_data["devices"].items() ++ for simulator in simulators ++ if runtime.split(".")[-1].startswith("xrOS") and simulator["state"] == "Booted" ++ ] ++ except subprocess.CalledProcessError as e: ++ # If there's no ~/Library/Developer/XCTestDevices folder (which is the ++ # case on fresh installs, and in some CI environments), `simctl list` ++ # returns error code 1, rather than an empty list. Handle that case, ++ # but raise all other errors. ++ if e.returncode == 1: ++ return [] ++ else: ++ raise ++ ++ ++async def find_device(initial_devices, lock): ++ while True: ++ new_devices = set(await list_devices()).difference(initial_devices) ++ if len(new_devices) == 0: ++ await asyncio.sleep(1) ++ elif len(new_devices) == 1: ++ udid = new_devices.pop() ++ print(f"{datetime.now():%Y-%m-%d %H:%M:%S}: New test simulator detected") ++ print(f"UDID: {udid}", flush=True) ++ lock.release() ++ return udid ++ else: ++ exit(f"Found more than one new device: {new_devices}") ++ ++ ++async def log_stream_task(initial_devices, lock): ++ # Wait up to 5 minutes for the build to complete and the simulator to boot. ++ udid = await asyncio.wait_for(find_device(initial_devices, lock), 5 * 60) ++ ++ # Stream the visionOS device's logs, filtering out messages that come from the ++ # XCTest test suite (catching NSLog messages from the test method), or ++ # Python itself (catching stdout/stderr content routed to the system log ++ # with config->use_system_logger). ++ args = [ ++ "xcrun", ++ "simctl", ++ "--set", ++ "testing", ++ "spawn", ++ udid, ++ "log", ++ "stream", ++ "--style", ++ "compact", ++ "--predicate", ++ ( ++ 'senderImagePath ENDSWITH "/visionOSTestbedTests.xctest/visionOSTestbedTests"' ++ ' OR senderImagePath ENDSWITH "/Python.framework/Python"' ++ ), ++ ] ++ ++ async with async_process( ++ *args, ++ stdout=subprocess.PIPE, ++ stderr=subprocess.STDOUT, ++ ) as process: ++ suppress_dupes = False ++ while line := (await process.stdout.readline()).decode(*DECODE_ARGS): ++ # Strip the prefix from each log line ++ line = LOG_PREFIX_REGEX.sub("", line) ++ # The visionOS log streamer can sometimes lag; when it does, it outputs ++ # a warning about messages being dropped... often multiple times. ++ # Only print the first of these duplicated warnings. ++ if line.startswith("=== Messages dropped "): ++ if not suppress_dupes: ++ suppress_dupes = True ++ sys.stdout.write(line) ++ else: ++ suppress_dupes = False ++ sys.stdout.write(line) ++ sys.stdout.flush() ++ ++ ++async def xcode_test(location, simulator, verbose): ++ # Run the test suite on the named simulator ++ print("Starting xcodebuild...", flush=True) ++ args = [ ++ "xcodebuild", ++ "test", ++ "-project", ++ str(location / "visionOSTestbed.xcodeproj"), ++ "-scheme", ++ "visionOSTestbed", ++ "-destination", ++ f"platform=visionOS Simulator,name={simulator}", ++ "-resultBundlePath", ++ str(location / f"{datetime.now():%Y%m%d-%H%M%S}.xcresult"), ++ "-derivedDataPath", ++ str(location / "DerivedData"), ++ ] ++ if not verbose: ++ args += ["-quiet"] ++ ++ async with async_process( ++ *args, ++ stdout=subprocess.PIPE, ++ stderr=subprocess.STDOUT, ++ ) as process: ++ while line := (await process.stdout.readline()).decode(*DECODE_ARGS): ++ sys.stdout.write(line) ++ sys.stdout.flush() ++ ++ status = await asyncio.wait_for(process.wait(), timeout=1) ++ exit(status) ++ ++ ++def clone_testbed( ++ source: Path, ++ target: Path, ++ framework: Path, ++ apps: list[Path], ++) -> None: ++ if target.exists(): ++ print(f"{target} already exists; aborting without creating project.") ++ sys.exit(10) ++ ++ if framework is None: ++ if not ( ++ source / "Python.xcframework/xros-arm64-simulator/bin" ++ ).is_dir(): ++ print( ++ f"The testbed being cloned ({source}) does not contain " ++ f"a simulator framework. Re-run with --framework" ++ ) ++ sys.exit(11) ++ else: ++ if not framework.is_dir(): ++ print(f"{framework} does not exist.") ++ sys.exit(12) ++ elif not ( ++ framework.suffix == ".xcframework" ++ or (framework / "Python.framework").is_dir() ++ ): ++ print( ++ f"{framework} is not an XCframework, " ++ f"or a simulator slice of a framework build." ++ ) ++ sys.exit(13) ++ ++ print("Cloning testbed project:") ++ print(f" Cloning {source}...", end="", flush=True) ++ shutil.copytree(source, target, symlinks=True) ++ print(" done") ++ ++ xc_framework_path = target / "Python.xcframework" ++ sim_framework_path = xc_framework_path / "xros-arm64-simulator" ++ if framework is not None: ++ if framework.suffix == ".xcframework": ++ print(" Installing XCFramework...", end="", flush=True) ++ if xc_framework_path.is_dir(): ++ shutil.rmtree(xc_framework_path) ++ else: ++ xc_framework_path.unlink(missing_ok=True) ++ xc_framework_path.symlink_to( ++ framework.relative_to(xc_framework_path.parent, walk_up=True) ++ ) ++ print(" done") ++ else: ++ print(" Installing simulator framework...", end="", flush=True) ++ if sim_framework_path.is_dir(): ++ shutil.rmtree(sim_framework_path) ++ else: ++ sim_framework_path.unlink(missing_ok=True) ++ sim_framework_path.symlink_to( ++ framework.relative_to(sim_framework_path.parent, walk_up=True) ++ ) ++ print(" done") ++ else: ++ if ( ++ xc_framework_path.is_symlink() ++ and not xc_framework_path.readlink().is_absolute() ++ ): ++ # XCFramework is a relative symlink. Rewrite the symlink relative ++ # to the new location. ++ print(" Rewriting symlink to XCframework...", end="", flush=True) ++ orig_xc_framework_path = ( ++ source ++ / xc_framework_path.readlink() ++ ).resolve() ++ xc_framework_path.unlink() ++ xc_framework_path.symlink_to( ++ orig_xc_framework_path.relative_to( ++ xc_framework_path.parent, walk_up=True ++ ) ++ ) ++ print(" done") ++ elif ( ++ sim_framework_path.is_symlink() ++ and not sim_framework_path.readlink().is_absolute() ++ ): ++ print(" Rewriting symlink to simulator framework...", end="", flush=True) ++ # Simulator framework is a relative symlink. Rewrite the symlink ++ # relative to the new location. ++ orig_sim_framework_path = ( ++ source ++ / "Python.XCframework" ++ / sim_framework_path.readlink() ++ ).resolve() ++ sim_framework_path.unlink() ++ sim_framework_path.symlink_to( ++ orig_sim_framework_path.relative_to( ++ sim_framework_path.parent, walk_up=True ++ ) ++ ) ++ print(" done") ++ else: ++ print(" Using pre-existing visionOS framework.") ++ ++ for app_src in apps: ++ print(f" Installing app {app_src.name!r}...", end="", flush=True) ++ app_target = target / f"visionOSTestbed/app/{app_src.name}" ++ if app_target.is_dir(): ++ shutil.rmtree(app_target) ++ shutil.copytree(app_src, app_target) ++ print(" done") ++ ++ print(f"Successfully cloned testbed: {target.resolve()}") ++ ++ ++def update_plist(testbed_path, args): ++ # Add the test runner arguments to the testbed's Info.plist file. ++ info_plist = testbed_path / "visionOSTestbed" / "visionOSTestbed-Info.plist" ++ with info_plist.open("rb") as f: ++ info = plistlib.load(f) ++ ++ info["TestArgs"] = args ++ ++ with info_plist.open("wb") as f: ++ plistlib.dump(info, f) ++ ++ ++async def run_testbed(simulator: str, args: list[str], verbose: bool=False): ++ location = Path(__file__).parent ++ print("Updating plist...", end="", flush=True) ++ update_plist(location, args) ++ print(" done.", flush=True) ++ ++ # We need to get an exclusive lock on simulator creation, to avoid issues ++ # with multiple simulators starting and being unable to tell which ++ # simulator is due to which testbed instance. See ++ # https://github.com/python/cpython/issues/130294 for details. Wait up to ++ # 10 minutes for a simulator to boot. ++ print("Obtaining lock on simulator creation...", flush=True) ++ simulator_lock = SimulatorLock(timeout=10*60) ++ await simulator_lock.acquire() ++ print("Simulator lock acquired.", flush=True) ++ ++ # Get the list of devices that are booted at the start of the test run. ++ # The simulator started by the test suite will be detected as the new ++ # entry that appears on the device list. ++ initial_devices = await list_devices() ++ ++ try: ++ async with asyncio.TaskGroup() as tg: ++ tg.create_task(log_stream_task(initial_devices, simulator_lock)) ++ tg.create_task(xcode_test(location, simulator=simulator, verbose=verbose)) ++ except* MySystemExit as e: ++ raise SystemExit(*e.exceptions[0].args) from None ++ except* subprocess.CalledProcessError as e: ++ # Extract it from the ExceptionGroup so it can be handled by `main`. ++ raise e.exceptions[0] ++ finally: ++ simulator_lock.release() ++ ++ ++def main(): ++ parser = argparse.ArgumentParser( ++ description=( ++ "Manages the process of testing a Python project in the visionOS simulator." ++ ), ++ ) ++ ++ subcommands = parser.add_subparsers(dest="subcommand") ++ ++ clone = subcommands.add_parser( ++ "clone", ++ description=( ++ "Clone the testbed project, copying in an visionOS Python framework and" ++ "any specified application code." ++ ), ++ help="Clone a testbed project to a new location.", ++ ) ++ clone.add_argument( ++ "--framework", ++ help=( ++ "The location of the XCFramework (or simulator-only slice of an " ++ "XCFramework) to use when running the testbed" ++ ), ++ ) ++ clone.add_argument( ++ "--app", ++ dest="apps", ++ action="https://github.com/append", ++ default=[], ++ help="The location of any code to include in the testbed project", ++ ) ++ clone.add_argument( ++ "location", ++ help="The path where the testbed will be cloned.", ++ ) ++ ++ run = subcommands.add_parser( ++ "run", ++ usage="%(prog)s [-h] [--simulator SIMULATOR] -- [ ...]", ++ description=( ++ "Run a testbed project. The arguments provided after `--` will be " ++ "passed to the running visionOS process as if they were arguments to " ++ "`python -m`." ++ ), ++ help="Run a testbed project", ++ ) ++ run.add_argument( ++ "--simulator", ++ default="Apple Vision Pro", ++ help="The name of the simulator to use (default: 'Apple Vision Pro')", + ) ++ run.add_argument( ++ "-v", "--verbose", ++ action="https://github.com/store_true", ++ help="Enable verbose output", ++ ) ++ ++ try: ++ pos = sys.argv.index("--") ++ testbed_args = sys.argv[1:pos] ++ test_args = sys.argv[pos + 1 :] ++ except ValueError: ++ testbed_args = sys.argv[1:] ++ test_args = [] ++ ++ context = parser.parse_args(testbed_args) ++ ++ if context.subcommand == "clone": ++ clone_testbed( ++ source=Path(__file__).parent.resolve(), ++ target=Path(context.location).resolve(), ++ framework=Path(context.framework).resolve() if context.framework else None, ++ apps=[Path(app) for app in context.apps], ++ ) ++ elif context.subcommand == "run": ++ if test_args: ++ if not ( ++ Path(__file__).parent / "Python.xcframework/xros-arm64-simulator/bin" ++ ).is_dir(): ++ print( ++ f"Testbed does not contain a compiled visionOS framework. Use " ++ f"`python {sys.argv[0]} clone ...` to create a runnable " ++ f"clone of this testbed." ++ ) ++ sys.exit(20) ++ ++ asyncio.run( ++ run_testbed( ++ simulator=context.simulator, ++ verbose=context.verbose, ++ args=test_args, ++ ) ++ ) ++ else: ++ print(f"Must specify test arguments (e.g., {sys.argv[0]} run -- test)") ++ print() ++ parser.print_help(sys.stderr) ++ sys.exit(21) ++ else: ++ parser.print_help(sys.stderr) ++ sys.exit(1) ++ ++ ++if __name__ == "__main__": ++ main() +--- /dev/null ++++ b/visionOS/testbed/visionOSTestbed.xcodeproj/project.pbxproj +@@ -0,0 +1,581 @@ ++// !$*UTF8*$! ++{ ++ archiveVersion = 1; ++ classes = { ++ }; ++ objectVersion = 56; ++ objects = { ++ ++/* Begin PBXBuildFile section */ ++ 607A66172B0EFA380010BFC8 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66162B0EFA380010BFC8 /* AppDelegate.m */; }; ++ 607A66222B0EFA390010BFC8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607A66212B0EFA390010BFC8 /* Assets.xcassets */; }; ++ 607A66282B0EFA390010BFC8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66272B0EFA390010BFC8 /* main.m */; }; ++ 607A66322B0EFA3A0010BFC8 /* visionOSTestbedTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66312B0EFA3A0010BFC8 /* visionOSTestbedTests.m */; }; ++ 607A66582B0F079F0010BFC8 /* dylib-Info-template.plist in Resources */ = {isa = PBXBuildFile; fileRef = 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */; }; ++ 608619542CB77BA900F46182 /* app_packages in Resources */ = {isa = PBXBuildFile; fileRef = 608619532CB77BA900F46182 /* app_packages */; }; ++ 608619562CB7819B00F46182 /* app in Resources */ = {isa = PBXBuildFile; fileRef = 608619552CB7819B00F46182 /* app */; }; ++ EEB367CE2DADF5C900B9A1D7 /* Python.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EEE9C80C2DAB5ECA0056F8C6 /* Python.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; ++ EEB367CF2DADF5D300B9A1D7 /* Python.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EEE9C80C2DAB5ECA0056F8C6 /* Python.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; ++ EEE9C80D2DAB5ECA0056F8C6 /* Python.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EEE9C80C2DAB5ECA0056F8C6 /* Python.xcframework */; }; ++ EEE9C80E2DAB5ECA0056F8C6 /* Python.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EEE9C80C2DAB5ECA0056F8C6 /* Python.xcframework */; }; ++/* End PBXBuildFile section */ ++ ++/* Begin PBXContainerItemProxy section */ ++ 607A662E2B0EFA3A0010BFC8 /* PBXContainerItemProxy */ = { ++ isa = PBXContainerItemProxy; ++ containerPortal = 607A660A2B0EFA380010BFC8 /* Project object */; ++ proxyType = 1; ++ remoteGlobalIDString = 607A66112B0EFA380010BFC8; ++ remoteInfo = iOSTestbed; ++ }; ++/* End PBXContainerItemProxy section */ ++ ++/* Begin PBXCopyFilesBuildPhase section */ ++ 607A664E2B0EFC080010BFC8 /* Embed Frameworks */ = { ++ isa = PBXCopyFilesBuildPhase; ++ buildActionMask = 2147483647; ++ dstPath = ""; ++ dstSubfolderSpec = 10; ++ files = ( ++ EEB367CF2DADF5D300B9A1D7 /* Python.xcframework in Embed Frameworks */, ++ ); ++ name = "Embed Frameworks"; ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++ 607A66522B0EFFE00010BFC8 /* Embed Frameworks */ = { ++ isa = PBXCopyFilesBuildPhase; ++ buildActionMask = 2147483647; ++ dstPath = ""; ++ dstSubfolderSpec = 10; ++ files = ( ++ EEB367CE2DADF5C900B9A1D7 /* Python.xcframework in Embed Frameworks */, ++ ); ++ name = "Embed Frameworks"; ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++/* End PBXCopyFilesBuildPhase section */ ++ ++/* Begin PBXFileReference section */ ++ 607A66122B0EFA380010BFC8 /* visionOSTestbed.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = visionOSTestbed.app; sourceTree = BUILT_PRODUCTS_DIR; }; ++ 607A66152B0EFA380010BFC8 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; ++ 607A66162B0EFA380010BFC8 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; ++ 607A66212B0EFA390010BFC8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; ++ 607A66272B0EFA390010BFC8 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; ++ 607A662D2B0EFA3A0010BFC8 /* visionOSTestbedTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = visionOSTestbedTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; ++ 607A66312B0EFA3A0010BFC8 /* visionOSTestbedTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = visionOSTestbedTests.m; sourceTree = ""; }; ++ 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "dylib-Info-template.plist"; sourceTree = ""; }; ++ 607A66592B0F08600010BFC8 /* visionOSTestbed-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "visionOSTestbed-Info.plist"; sourceTree = ""; }; ++ 608619532CB77BA900F46182 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = ""; }; ++ 608619552CB7819B00F46182 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = ""; }; ++ EEE9C80C2DAB5ECA0056F8C6 /* Python.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Python.xcframework; sourceTree = ""; }; ++/* End PBXFileReference section */ ++ ++/* Begin PBXFrameworksBuildPhase section */ ++ 607A660F2B0EFA380010BFC8 /* Frameworks */ = { ++ isa = PBXFrameworksBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ EEE9C80D2DAB5ECA0056F8C6 /* Python.xcframework in Frameworks */, ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++ 607A662A2B0EFA3A0010BFC8 /* Frameworks */ = { ++ isa = PBXFrameworksBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ EEE9C80E2DAB5ECA0056F8C6 /* Python.xcframework in Frameworks */, ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++/* End PBXFrameworksBuildPhase section */ ++ ++/* Begin PBXGroup section */ ++ 607A66092B0EFA380010BFC8 = { ++ isa = PBXGroup; ++ children = ( ++ EEE9C80C2DAB5ECA0056F8C6 /* Python.xcframework */, ++ 607A66142B0EFA380010BFC8 /* visionOSTestbed */, ++ 607A66302B0EFA3A0010BFC8 /* visionOSTestbedTests */, ++ 607A66132B0EFA380010BFC8 /* Products */, ++ 607A664F2B0EFFE00010BFC8 /* Frameworks */, ++ ); ++ sourceTree = ""; ++ }; ++ 607A66132B0EFA380010BFC8 /* Products */ = { ++ isa = PBXGroup; ++ children = ( ++ 607A66122B0EFA380010BFC8 /* visionOSTestbed.app */, ++ 607A662D2B0EFA3A0010BFC8 /* visionOSTestbedTests.xctest */, ++ ); ++ name = Products; ++ sourceTree = ""; ++ }; ++ 607A66142B0EFA380010BFC8 /* visionOSTestbed */ = { ++ isa = PBXGroup; ++ children = ( ++ 608619552CB7819B00F46182 /* app */, ++ 608619532CB77BA900F46182 /* app_packages */, ++ 607A66592B0F08600010BFC8 /* visionOSTestbed-Info.plist */, ++ 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */, ++ 607A66152B0EFA380010BFC8 /* AppDelegate.h */, ++ 607A66162B0EFA380010BFC8 /* AppDelegate.m */, ++ 607A66212B0EFA390010BFC8 /* Assets.xcassets */, ++ 607A66272B0EFA390010BFC8 /* main.m */, ++ ); ++ path = visionOSTestbed; ++ sourceTree = ""; ++ }; ++ 607A66302B0EFA3A0010BFC8 /* visionOSTestbedTests */ = { ++ isa = PBXGroup; ++ children = ( ++ 607A66312B0EFA3A0010BFC8 /* visionOSTestbedTests.m */, ++ ); ++ path = visionOSTestbedTests; ++ sourceTree = ""; ++ }; ++ 607A664F2B0EFFE00010BFC8 /* Frameworks */ = { ++ isa = PBXGroup; ++ children = ( ++ ); ++ name = Frameworks; ++ sourceTree = ""; ++ }; ++/* End PBXGroup section */ ++ ++/* Begin PBXNativeTarget section */ ++ 607A66112B0EFA380010BFC8 /* visionOSTestbed */ = { ++ isa = PBXNativeTarget; ++ buildConfigurationList = 607A66412B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "visionOSTestbed" */; ++ buildPhases = ( ++ 607A660E2B0EFA380010BFC8 /* Sources */, ++ 607A660F2B0EFA380010BFC8 /* Frameworks */, ++ 607A66102B0EFA380010BFC8 /* Resources */, ++ 607A66552B0F061D0010BFC8 /* Install Target Specific Python Standard Library */, ++ 607A66562B0F06200010BFC8 /* Prepare Python Binary Modules */, ++ 607A664E2B0EFC080010BFC8 /* Embed Frameworks */, ++ ); ++ buildRules = ( ++ ); ++ dependencies = ( ++ ); ++ name = visionOSTestbed; ++ productName = iOSTestbed; ++ productReference = 607A66122B0EFA380010BFC8 /* visionOSTestbed.app */; ++ productType = "com.apple.product-type.application"; ++ }; ++ 607A662C2B0EFA3A0010BFC8 /* visionOSTestbedTests */ = { ++ isa = PBXNativeTarget; ++ buildConfigurationList = 607A66442B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "visionOSTestbedTests" */; ++ buildPhases = ( ++ 607A66292B0EFA3A0010BFC8 /* Sources */, ++ 607A662A2B0EFA3A0010BFC8 /* Frameworks */, ++ 607A662B2B0EFA3A0010BFC8 /* Resources */, ++ 607A66522B0EFFE00010BFC8 /* Embed Frameworks */, ++ ); ++ buildRules = ( ++ ); ++ dependencies = ( ++ 607A662F2B0EFA3A0010BFC8 /* PBXTargetDependency */, ++ ); ++ name = visionOSTestbedTests; ++ productName = iOSTestbedTests; ++ productReference = 607A662D2B0EFA3A0010BFC8 /* visionOSTestbedTests.xctest */; ++ productType = "com.apple.product-type.bundle.unit-test"; ++ }; ++/* End PBXNativeTarget section */ ++ ++/* Begin PBXProject section */ ++ 607A660A2B0EFA380010BFC8 /* Project object */ = { ++ isa = PBXProject; ++ attributes = { ++ BuildIndependentTargetsInParallel = 1; ++ LastUpgradeCheck = 1500; ++ TargetAttributes = { ++ 607A66112B0EFA380010BFC8 = { ++ CreatedOnToolsVersion = 15.0.1; ++ }; ++ 607A662C2B0EFA3A0010BFC8 = { ++ CreatedOnToolsVersion = 15.0.1; ++ TestTargetID = 607A66112B0EFA380010BFC8; ++ }; ++ }; ++ }; ++ buildConfigurationList = 607A660D2B0EFA380010BFC8 /* Build configuration list for PBXProject "visionOSTestbed" */; ++ compatibilityVersion = "Xcode 14.0"; ++ developmentRegion = en; ++ hasScannedForEncodings = 0; ++ knownRegions = ( ++ en, ++ Base, ++ ); ++ mainGroup = 607A66092B0EFA380010BFC8; ++ productRefGroup = 607A66132B0EFA380010BFC8 /* Products */; ++ projectDirPath = ""; ++ projectRoot = ""; ++ targets = ( ++ 607A66112B0EFA380010BFC8 /* visionOSTestbed */, ++ 607A662C2B0EFA3A0010BFC8 /* visionOSTestbedTests */, ++ ); ++ }; ++/* End PBXProject section */ ++ ++/* Begin PBXResourcesBuildPhase section */ ++ 607A66102B0EFA380010BFC8 /* Resources */ = { ++ isa = PBXResourcesBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ 607A66582B0F079F0010BFC8 /* dylib-Info-template.plist in Resources */, ++ 608619562CB7819B00F46182 /* app in Resources */, ++ 607A66222B0EFA390010BFC8 /* Assets.xcassets in Resources */, ++ 608619542CB77BA900F46182 /* app_packages in Resources */, ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++ 607A662B2B0EFA3A0010BFC8 /* Resources */ = { ++ isa = PBXResourcesBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++/* End PBXResourcesBuildPhase section */ ++ ++/* Begin PBXShellScriptBuildPhase section */ ++ 607A66552B0F061D0010BFC8 /* Install Target Specific Python Standard Library */ = { ++ isa = PBXShellScriptBuildPhase; ++ alwaysOutOfDate = 1; ++ buildActionMask = 2147483647; ++ files = ( ++ ); ++ inputFileListPaths = ( ++ ); ++ inputPaths = ( ++ ); ++ name = "Install Target Specific Python Standard Library"; ++ outputFileListPaths = ( ++ ); ++ outputPaths = ( ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ shellPath = /bin/sh; ++ shellScript = "set -e\n\nmkdir -p \"$CODESIGNING_FOLDER_PATH/python/lib\"\nif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-xrsimulator\" ]; then\n echo \"Installing Python modules for xrOS Simulator\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/xros-arm64-simulator/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nelse\n echo \"Installing Python modules for xrOS Device\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/xros-arm64/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nfi\n"; ++ showEnvVarsInLog = 0; ++ }; ++ 607A66562B0F06200010BFC8 /* Prepare Python Binary Modules */ = { ++ isa = PBXShellScriptBuildPhase; ++ alwaysOutOfDate = 1; ++ buildActionMask = 2147483647; ++ files = ( ++ ); ++ inputFileListPaths = ( ++ ); ++ inputPaths = ( ++ ); ++ name = "Prepare Python Binary Modules"; ++ outputFileListPaths = ( ++ ); ++ outputPaths = ( ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ shellPath = /bin/sh; ++ shellScript = "set -e\n\ninstall_dylib () {\n INSTALL_BASE=$1\n FULL_EXT=$2\n\n # The name of the extension file\n EXT=$(basename \"$FULL_EXT\")\n # The location of the extension file, relative to the bundle\n RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/} \n # The path to the extension file, relative to the install base\n PYTHON_EXT=${RELATIVE_EXT/$INSTALL_BASE/}\n # The full dotted name of the extension module, constructed from the file path.\n FULL_MODULE_NAME=$(echo $PYTHON_EXT | cut -d \".\" -f 1 | tr \"/\" \".\"); \n # A bundle identifier; not actually used, but required by Xcode framework packaging\n FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr \"_\" \"-\")\n # The name of the framework folder.\n FRAMEWORK_FOLDER=\"Frameworks/$FULL_MODULE_NAME.framework\"\n\n # If the framework folder doesn't exist, create it.\n if [ ! -d \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\" ]; then\n echo \"Creating framework for $RELATIVE_EXT\" \n mkdir -p \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\"\n cp \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleExecutable -string \"$FULL_MODULE_NAME\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleIdentifier -string \"$FRAMEWORK_BUNDLE_ID\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n fi\n \n echo \"Installing binary for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" \n mv \"$FULL_EXT\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\"\n # Create a placeholder .fwork file where the .so was\n echo \"$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" > ${FULL_EXT%.so}.fwork\n # Create a back reference to the .so file location in the framework\n echo \"${RELATIVE_EXT%.so}.fwork\" > \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin\" \n}\n\nPYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/python/lib\")\necho \"Install Python $PYTHON_VER standard library extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib python/lib/$PYTHON_VER/lib-dynload/ \"$FULL_EXT\"\ndone\necho \"Install app package extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app_packages\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app_packages/ \"$FULL_EXT\"\ndone\necho \"Install app extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app/ \"$FULL_EXT\"\ndone\n\n# Clean up dylib template \nrm -f \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\"\necho \"Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)...\"\nfind \"$CODESIGNING_FOLDER_PATH/Frameworks\" -name \"*.framework\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der \"{}\" \\; \n"; ++ showEnvVarsInLog = 0; ++ }; ++/* End PBXShellScriptBuildPhase section */ ++ ++/* Begin PBXSourcesBuildPhase section */ ++ 607A660E2B0EFA380010BFC8 /* Sources */ = { ++ isa = PBXSourcesBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ 607A66172B0EFA380010BFC8 /* AppDelegate.m in Sources */, ++ 607A66282B0EFA390010BFC8 /* main.m in Sources */, ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++ 607A66292B0EFA3A0010BFC8 /* Sources */ = { ++ isa = PBXSourcesBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ 607A66322B0EFA3A0010BFC8 /* visionOSTestbedTests.m in Sources */, ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++/* End PBXSourcesBuildPhase section */ ++ ++/* Begin PBXTargetDependency section */ ++ 607A662F2B0EFA3A0010BFC8 /* PBXTargetDependency */ = { ++ isa = PBXTargetDependency; ++ target = 607A66112B0EFA380010BFC8 /* visionOSTestbed */; ++ targetProxy = 607A662E2B0EFA3A0010BFC8 /* PBXContainerItemProxy */; ++ }; ++/* End PBXTargetDependency section */ ++ ++/* Begin XCBuildConfiguration section */ ++ 607A663F2B0EFA3A0010BFC8 /* Debug */ = { ++ isa = XCBuildConfiguration; ++ buildSettings = { ++ ALWAYS_SEARCH_USER_PATHS = NO; ++ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ++ CLANG_ANALYZER_NONNULL = YES; ++ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; ++ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; ++ CLANG_ENABLE_MODULES = YES; ++ CLANG_ENABLE_OBJC_ARC = YES; ++ CLANG_ENABLE_OBJC_WEAK = YES; ++ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; ++ CLANG_WARN_BOOL_CONVERSION = YES; ++ CLANG_WARN_COMMA = YES; ++ CLANG_WARN_CONSTANT_CONVERSION = YES; ++ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; ++ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; ++ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; ++ CLANG_WARN_EMPTY_BODY = YES; ++ CLANG_WARN_ENUM_CONVERSION = YES; ++ CLANG_WARN_INFINITE_RECURSION = YES; ++ CLANG_WARN_INT_CONVERSION = YES; ++ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; ++ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; ++ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; ++ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; ++ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; ++ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; ++ CLANG_WARN_STRICT_PROTOTYPES = YES; ++ CLANG_WARN_SUSPICIOUS_MOVE = YES; ++ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; ++ CLANG_WARN_UNREACHABLE_CODE = YES; ++ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; ++ COPY_PHASE_STRIP = NO; ++ DEBUG_INFORMATION_FORMAT = dwarf; ++ ENABLE_STRICT_OBJC_MSGSEND = YES; ++ ENABLE_TESTABILITY = YES; ++ ENABLE_USER_SCRIPT_SANDBOXING = YES; ++ GCC_C_LANGUAGE_STANDARD = gnu17; ++ GCC_DYNAMIC_NO_PIC = NO; ++ GCC_NO_COMMON_BLOCKS = YES; ++ GCC_OPTIMIZATION_LEVEL = 0; ++ GCC_PREPROCESSOR_DEFINITIONS = ( ++ "DEBUG=1", ++ "$(inherited)", ++ ); ++ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; ++ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; ++ GCC_WARN_UNDECLARED_SELECTOR = YES; ++ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; ++ GCC_WARN_UNUSED_FUNCTION = YES; ++ GCC_WARN_UNUSED_VARIABLE = YES; ++ IPHONEOS_DEPLOYMENT_TARGET = 12.0; ++ LOCALIZATION_PREFERS_STRING_CATALOGS = YES; ++ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; ++ MTL_FAST_MATH = YES; ++ ONLY_ACTIVE_ARCH = YES; ++ SDKROOT = xros; ++ }; ++ name = Debug; ++ }; ++ 607A66402B0EFA3A0010BFC8 /* Release */ = { ++ isa = XCBuildConfiguration; ++ buildSettings = { ++ ALWAYS_SEARCH_USER_PATHS = NO; ++ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ++ CLANG_ANALYZER_NONNULL = YES; ++ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; ++ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; ++ CLANG_ENABLE_MODULES = YES; ++ CLANG_ENABLE_OBJC_ARC = YES; ++ CLANG_ENABLE_OBJC_WEAK = YES; ++ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; ++ CLANG_WARN_BOOL_CONVERSION = YES; ++ CLANG_WARN_COMMA = YES; ++ CLANG_WARN_CONSTANT_CONVERSION = YES; ++ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; ++ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; ++ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; ++ CLANG_WARN_EMPTY_BODY = YES; ++ CLANG_WARN_ENUM_CONVERSION = YES; ++ CLANG_WARN_INFINITE_RECURSION = YES; ++ CLANG_WARN_INT_CONVERSION = YES; ++ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; ++ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; ++ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; ++ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; ++ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; ++ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; ++ CLANG_WARN_STRICT_PROTOTYPES = YES; ++ CLANG_WARN_SUSPICIOUS_MOVE = YES; ++ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; ++ CLANG_WARN_UNREACHABLE_CODE = YES; ++ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; ++ COPY_PHASE_STRIP = NO; ++ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ++ ENABLE_NS_ASSERTIONS = NO; ++ ENABLE_STRICT_OBJC_MSGSEND = YES; ++ ENABLE_USER_SCRIPT_SANDBOXING = YES; ++ GCC_C_LANGUAGE_STANDARD = gnu17; ++ GCC_NO_COMMON_BLOCKS = YES; ++ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; ++ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; ++ GCC_WARN_UNDECLARED_SELECTOR = YES; ++ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; ++ GCC_WARN_UNUSED_FUNCTION = YES; ++ GCC_WARN_UNUSED_VARIABLE = YES; ++ IPHONEOS_DEPLOYMENT_TARGET = 12.0; ++ LOCALIZATION_PREFERS_STRING_CATALOGS = YES; ++ MTL_ENABLE_DEBUG_INFO = NO; ++ MTL_FAST_MATH = YES; ++ SDKROOT = xros; ++ VALIDATE_PRODUCT = YES; ++ }; ++ name = Release; ++ }; ++ 607A66422B0EFA3A0010BFC8 /* Debug */ = { ++ isa = XCBuildConfiguration; ++ buildSettings = { ++ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ++ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ++ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; ++ CODE_SIGN_STYLE = Automatic; ++ CURRENT_PROJECT_VERSION = 1; ++ DEVELOPMENT_TEAM = ""; ++ ENABLE_USER_SCRIPT_SANDBOXING = NO; ++ HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; ++ INFOPLIST_FILE = "visionOSTestbed/visionOSTestbed-Info.plist"; ++ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; ++ INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; ++ INFOPLIST_KEY_UIMainStoryboardFile = Main; ++ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; ++ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; ++ LD_RUNPATH_SEARCH_PATHS = ( ++ "$(inherited)", ++ "@executable_path/Frameworks", ++ ); ++ MARKETING_VERSION = 3.13.0a1; ++ PRODUCT_BUNDLE_IDENTIFIER = org.python.visionOSTestbed; ++ PRODUCT_NAME = "$(TARGET_NAME)"; ++ SUPPORTED_PLATFORMS = "xros xrsimulator"; ++ SUPPORTS_MACCATALYST = NO; ++ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; ++ SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; ++ SWIFT_EMIT_LOC_STRINGS = YES; ++ TARGETED_DEVICE_FAMILY = 7; ++ XROS_DEPLOYMENT_TARGET = 2.0; ++ }; ++ name = Debug; ++ }; ++ 607A66432B0EFA3A0010BFC8 /* Release */ = { ++ isa = XCBuildConfiguration; ++ buildSettings = { ++ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ++ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ++ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; ++ CODE_SIGN_STYLE = Automatic; ++ CURRENT_PROJECT_VERSION = 1; ++ DEVELOPMENT_TEAM = ""; ++ ENABLE_TESTABILITY = YES; ++ ENABLE_USER_SCRIPT_SANDBOXING = NO; ++ HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; ++ INFOPLIST_FILE = "visionOSTestbed/visionOSTestbed-Info.plist"; ++ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; ++ INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; ++ INFOPLIST_KEY_UIMainStoryboardFile = Main; ++ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; ++ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; ++ LD_RUNPATH_SEARCH_PATHS = ( ++ "$(inherited)", ++ "@executable_path/Frameworks", ++ ); ++ MARKETING_VERSION = 3.13.0a1; ++ PRODUCT_BUNDLE_IDENTIFIER = org.python.visionOSTestbed; ++ PRODUCT_NAME = "$(TARGET_NAME)"; ++ SUPPORTED_PLATFORMS = "xros xrsimulator"; ++ SUPPORTS_MACCATALYST = NO; ++ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; ++ SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; ++ SWIFT_EMIT_LOC_STRINGS = YES; ++ TARGETED_DEVICE_FAMILY = 7; ++ XROS_DEPLOYMENT_TARGET = 2.0; ++ }; ++ name = Release; ++ }; ++ 607A66452B0EFA3A0010BFC8 /* Debug */ = { ++ isa = XCBuildConfiguration; ++ buildSettings = { ++ BUNDLE_LOADER = "$(TEST_HOST)"; ++ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; ++ CODE_SIGN_STYLE = Automatic; ++ CURRENT_PROJECT_VERSION = 1; ++ DEVELOPMENT_TEAM = 3HEZE76D99; ++ GENERATE_INFOPLIST_FILE = YES; ++ HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; ++ IPHONEOS_DEPLOYMENT_TARGET = 12.0; ++ MARKETING_VERSION = 1.0; ++ PRODUCT_BUNDLE_IDENTIFIER = org.python.visionOSTestbedTests; ++ PRODUCT_NAME = "$(TARGET_NAME)"; ++ SUPPORTED_PLATFORMS = "xros xrsimulator"; ++ SUPPORTS_MACCATALYST = NO; ++ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; ++ SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; ++ SWIFT_EMIT_LOC_STRINGS = NO; ++ TARGETED_DEVICE_FAMILY = 7; ++ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/visionOSTestbed.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/visionOSTestbed"; ++ }; ++ name = Debug; ++ }; ++ 607A66462B0EFA3A0010BFC8 /* Release */ = { ++ isa = XCBuildConfiguration; ++ buildSettings = { ++ BUNDLE_LOADER = "$(TEST_HOST)"; ++ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; ++ CODE_SIGN_STYLE = Automatic; ++ CURRENT_PROJECT_VERSION = 1; ++ DEVELOPMENT_TEAM = 3HEZE76D99; ++ GENERATE_INFOPLIST_FILE = YES; ++ HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; ++ IPHONEOS_DEPLOYMENT_TARGET = 12.0; ++ MARKETING_VERSION = 1.0; ++ PRODUCT_BUNDLE_IDENTIFIER = org.python.visionOSTestbedTests; ++ PRODUCT_NAME = "$(TARGET_NAME)"; ++ SUPPORTED_PLATFORMS = "xros xrsimulator"; ++ SUPPORTS_MACCATALYST = NO; ++ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; ++ SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; ++ SWIFT_EMIT_LOC_STRINGS = NO; ++ TARGETED_DEVICE_FAMILY = 7; ++ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/visionOSTestbed.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/visionOSTestbed"; ++ }; ++ name = Release; ++ }; ++/* End XCBuildConfiguration section */ ++ ++/* Begin XCConfigurationList section */ ++ 607A660D2B0EFA380010BFC8 /* Build configuration list for PBXProject "visionOSTestbed" */ = { ++ isa = XCConfigurationList; ++ buildConfigurations = ( ++ 607A663F2B0EFA3A0010BFC8 /* Debug */, ++ 607A66402B0EFA3A0010BFC8 /* Release */, ++ ); ++ defaultConfigurationIsVisible = 0; ++ defaultConfigurationName = Release; ++ }; ++ 607A66412B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "visionOSTestbed" */ = { ++ isa = XCConfigurationList; ++ buildConfigurations = ( ++ 607A66422B0EFA3A0010BFC8 /* Debug */, ++ 607A66432B0EFA3A0010BFC8 /* Release */, ++ ); ++ defaultConfigurationIsVisible = 0; ++ defaultConfigurationName = Release; ++ }; ++ 607A66442B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "visionOSTestbedTests" */ = { ++ isa = XCConfigurationList; ++ buildConfigurations = ( ++ 607A66452B0EFA3A0010BFC8 /* Debug */, ++ 607A66462B0EFA3A0010BFC8 /* Release */, ++ ); ++ defaultConfigurationIsVisible = 0; ++ defaultConfigurationName = Release; ++ }; ++/* End XCConfigurationList section */ ++ }; ++ rootObject = 607A660A2B0EFA380010BFC8 /* Project object */; ++} +--- /dev/null ++++ b/visionOS/testbed/visionOSTestbed/AppDelegate.h +@@ -0,0 +1,11 @@ ++// ++// AppDelegate.h ++// visionOSTestbed ++// ++ ++#import ++ ++@interface AppDelegate : UIResponder ++ ++ ++@end +--- /dev/null ++++ b/visionOS/testbed/visionOSTestbed/AppDelegate.m +@@ -0,0 +1,19 @@ ++// ++// AppDelegate.m ++// visionOSTestbed ++// ++ ++#import "AppDelegate.h" ++ ++@interface AppDelegate () ++ ++@end ++ ++@implementation AppDelegate ++ ++ ++- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ++ return YES; ++} ++ ++@end +--- /dev/null ++++ b/visionOS/testbed/visionOSTestbed/Assets.xcassets/AccentColor.colorset/Contents.json +@@ -0,0 +1,11 @@ ++{ ++ "colors" : [ ++ { ++ "idiom" : "universal" ++ } ++ ], ++ "info" : { ++ "author" : "xcode", ++ "version" : 1 ++ } ++} +--- /dev/null ++++ b/visionOS/testbed/visionOSTestbed/Assets.xcassets/AppIcon.appiconset/Contents.json +@@ -0,0 +1,13 @@ ++{ ++ "images" : [ ++ { ++ "idiom" : "universal", ++ "platform" : "ios", ++ "size" : "1024x1024" ++ } + ], - [CYGWIN*], [PY_STDLIB_MOD_SET_NA([_scproxy])], - [QNX*], [PY_STDLIB_MOD_SET_NA([_scproxy])], - [FreeBSD*], [PY_STDLIB_MOD_SET_NA([_scproxy])], ++ "info" : { ++ "author" : "xcode", ++ "version" : 1 ++ } ++} +--- /dev/null ++++ b/visionOS/testbed/visionOSTestbed/Assets.xcassets/Contents.json +@@ -0,0 +1,6 @@ ++{ ++ "info" : { ++ "author" : "xcode", ++ "version" : 1 ++ } ++} +--- /dev/null ++++ b/visionOS/testbed/visionOSTestbed/app/README +@@ -0,0 +1,7 @@ ++This folder can contain any Python application code. ++ ++During the build, any binary modules found in this folder will be processed into ++iOS Framework form. ++ ++When the test suite runs, this folder will be on the PYTHONPATH, and will be the ++working directory for the test suite. +--- /dev/null ++++ b/visionOS/testbed/visionOSTestbed/app_packages/README +@@ -0,0 +1,7 @@ ++This folder can be a target for installing any Python dependencies needed by the ++test suite. ++ ++During the build, any binary modules found in this folder will be processed into ++iOS Framework form. ++ ++When the test suite runs, this folder will be on the PYTHONPATH. +--- /dev/null ++++ b/visionOS/testbed/visionOSTestbed/dylib-Info-template.plist +@@ -0,0 +1,30 @@ ++ ++ ++ ++ ++ CFBundleDevelopmentRegion ++ en ++ CFBundleInfoDictionaryVersion ++ 6.0 ++ CFBundleExecutable ++ ++ CFBundleIdentifier ++ ++ CFBundlePackageType ++ FMWK ++ CFBundleShortVersionString ++ 1.0 ++ CFBundleSupportedPlatforms ++ ++ XROS ++ ++ CFBundleVersion ++ 1 ++ MinimumOSVersion ++ 2.0 ++ UIDeviceFamily ++ ++ 7 ++ ++ ++ +--- /dev/null ++++ b/visionOS/testbed/visionOSTestbed/main.m +@@ -0,0 +1,16 @@ ++// ++// main.m ++// visionOSTestbed ++// ++ ++#import ++#import "AppDelegate.h" ++ ++int main(int argc, char * argv[]) { ++ NSString * appDelegateClassName; ++ @autoreleasepool { ++ appDelegateClassName = NSStringFromClass([AppDelegate class]); ++ ++ return UIApplicationMain(argc, argv, nil, appDelegateClassName); ++ } ++} +--- /dev/null ++++ b/visionOS/testbed/visionOSTestbed/visionOSTestbed-Info.plist +@@ -0,0 +1,56 @@ ++ ++ ++ ++ ++ CFBundleDevelopmentRegion ++ en ++ CFBundleDisplayName ++ ${PRODUCT_NAME} ++ CFBundleExecutable ++ ${EXECUTABLE_NAME} ++ CFBundleIdentifier ++ org.python.visionOSTestbed ++ CFBundleInfoDictionaryVersion ++ 6.0 ++ CFBundleName ++ ${PRODUCT_NAME} ++ CFBundlePackageType ++ APPL ++ CFBundleShortVersionString ++ 1.0 ++ CFBundleSignature ++ ???? ++ CFBundleVersion ++ 1 ++ TestArgs ++ ++ test ++ -uall ++ --single-process ++ --rerun ++ -W ++ ++ UIApplicationSceneManifest ++ ++ UIApplicationSupportsMultipleScenes ++ ++ UISceneConfigurations ++ ++ ++ UIRequiresFullScreen ++ ++ UISupportedInterfaceOrientations ++ ++ UIInterfaceOrientationPortrait ++ UIInterfaceOrientationLandscapeLeft ++ UIInterfaceOrientationLandscapeRight ++ ++ UISupportedInterfaceOrientations~ipad ++ ++ UIInterfaceOrientationPortrait ++ UIInterfaceOrientationPortraitUpsideDown ++ UIInterfaceOrientationLandscapeLeft ++ UIInterfaceOrientationLandscapeRight ++ ++ ++ +--- /dev/null ++++ b/visionOS/testbed/visionOSTestbedTests/visionOSTestbedTests.m +@@ -0,0 +1,162 @@ ++#import ++#import ++ ++@interface visionOSTestbedTests : XCTestCase ++ ++@end ++ ++@implementation visionOSTestbedTests ++ ++ ++- (void)testPython { ++ const char **argv; ++ int exit_code; ++ int failed; ++ PyStatus status; ++ PyPreConfig preconfig; ++ PyConfig config; ++ PyObject *sys_module; ++ PyObject *sys_path_attr; ++ NSArray *test_args; ++ NSString *python_home; ++ NSString *path; ++ wchar_t *wtmp_str; ++ ++ NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; ++ ++ // Set some other common environment indicators to disable color, as the ++ // Xcode log can't display color. Stdout will report that it is *not* a ++ // TTY. ++ setenv("NO_COLOR", "1", true); ++ setenv("PYTHON_COLORS", "0", true); ++ ++ // Arguments to pass into the test suite runner. ++ // argv[0] must identify the process; any subsequent arg ++ // will be handled as if it were an argument to `python -m test` ++ test_args = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"TestArgs"]; ++ if (test_args == NULL) { ++ NSLog(@"Unable to identify test arguments."); ++ } ++ argv = malloc(sizeof(char *) * ([test_args count] + 1)); ++ argv[0] = "visionOSTestbed"; ++ for (int i = 1; i < [test_args count]; i++) { ++ argv[i] = [[test_args objectAtIndex:i] UTF8String]; ++ } ++ NSLog(@"Test command: %@", test_args); ++ ++ // Generate an isolated Python configuration. ++ NSLog(@"Configuring isolated Python..."); ++ PyPreConfig_InitIsolatedConfig(&preconfig); ++ PyConfig_InitIsolatedConfig(&config); ++ ++ // Configure the Python interpreter: ++ // Enforce UTF-8 encoding for stderr, stdout, file-system encoding and locale. ++ // See https://docs.python.org/3/library/os.html#python-utf-8-mode. ++ preconfig.utf8_mode = 1; ++ // Use the system logger for stdout/err ++ config.use_system_logger = 1; ++ // Don't buffer stdio. We want output to appears in the log immediately ++ config.buffered_stdio = 0; ++ // Don't write bytecode; we can't modify the app bundle ++ // after it has been signed. ++ config.write_bytecode = 0; ++ // Ensure that signal handlers are installed ++ config.install_signal_handlers = 1; ++ // Run the test module. ++ config.run_module = Py_DecodeLocale([[test_args objectAtIndex:0] UTF8String], NULL); ++ // For debugging - enable verbose mode. ++ // config.verbose = 1; ++ ++ NSLog(@"Pre-initializing Python runtime..."); ++ status = Py_PreInitialize(&preconfig); ++ if (PyStatus_Exception(status)) { ++ XCTFail(@"Unable to pre-initialize Python interpreter: %s", status.err_msg); ++ PyConfig_Clear(&config); ++ return; ++ } ++ ++ // Set the home for the Python interpreter ++ python_home = [NSString stringWithFormat:@"%@/python", resourcePath, nil]; ++ NSLog(@"PythonHome: %@", python_home); ++ wtmp_str = Py_DecodeLocale([python_home UTF8String], NULL); ++ status = PyConfig_SetString(&config, &config.home, wtmp_str); ++ if (PyStatus_Exception(status)) { ++ XCTFail(@"Unable to set PYTHONHOME: %s", status.err_msg); ++ PyConfig_Clear(&config); ++ return; ++ } ++ PyMem_RawFree(wtmp_str); ++ ++ // Read the site config ++ status = PyConfig_Read(&config); ++ if (PyStatus_Exception(status)) { ++ XCTFail(@"Unable to read site config: %s", status.err_msg); ++ PyConfig_Clear(&config); ++ return; ++ } ++ ++ NSLog(@"Configure argc/argv..."); ++ status = PyConfig_SetBytesArgv(&config, [test_args count], (char**) argv); ++ if (PyStatus_Exception(status)) { ++ XCTFail(@"Unable to configure argc/argv: %s", status.err_msg); ++ PyConfig_Clear(&config); ++ return; ++ } ++ ++ NSLog(@"Initializing Python runtime..."); ++ status = Py_InitializeFromConfig(&config); ++ if (PyStatus_Exception(status)) { ++ XCTFail(@"Unable to initialize Python interpreter: %s", status.err_msg); ++ PyConfig_Clear(&config); ++ return; ++ } ++ ++ sys_module = PyImport_ImportModule("sys"); ++ if (sys_module == NULL) { ++ XCTFail(@"Could not import sys module"); ++ return; ++ } ++ ++ sys_path_attr = PyObject_GetAttrString(sys_module, "path"); ++ if (sys_path_attr == NULL) { ++ XCTFail(@"Could not access sys.path"); ++ return; ++ } ++ ++ // Add the app packages path ++ path = [NSString stringWithFormat:@"%@/app_packages", resourcePath, nil]; ++ NSLog(@"App packages path: %@", path); ++ wtmp_str = Py_DecodeLocale([path UTF8String], NULL); ++ failed = PyList_Insert(sys_path_attr, 0, PyUnicode_FromString([path UTF8String])); ++ if (failed) { ++ XCTFail(@"Unable to add app packages to sys.path"); ++ return; ++ } ++ PyMem_RawFree(wtmp_str); ++ ++ path = [NSString stringWithFormat:@"%@/app", resourcePath, nil]; ++ NSLog(@"App path: %@", path); ++ wtmp_str = Py_DecodeLocale([path UTF8String], NULL); ++ failed = PyList_Insert(sys_path_attr, 0, PyUnicode_FromString([path UTF8String])); ++ if (failed) { ++ XCTFail(@"Unable to add app to sys.path"); ++ return; ++ } ++ PyMem_RawFree(wtmp_str); ++ ++ // Ensure the working directory is the app folder. ++ chdir([path UTF8String]); ++ ++ // Start the test suite. Print a separator to differentiate Python startup logs from app logs ++ NSLog(@"---------------------------------------------------------------------------"); ++ ++ exit_code = Py_RunMain(); ++ XCTAssertEqual(exit_code, 0, @"Test suite did not pass"); ++ ++ NSLog(@"---------------------------------------------------------------------------"); ++ ++ Py_Finalize(); ++} ++ ++ ++@end +--- /dev/null ++++ b/watchOS/README.rst +@@ -0,0 +1,108 @@ ++======================== ++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-clang++ +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk watchsimulator${WATCHOS_SDK_VERSION} clang++ -target arm64-apple-watchos-simulator "$@" +--- /dev/null ++++ b/watchOS/Resources/bin/arm64-apple-watchos-simulator-cpp +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk watchsimulator clang -target arm64-apple-watchos-simulator -E "$@" +--- /dev/null ++++ b/watchOS/Resources/bin/arm64_32-apple-watchos-ar +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk watchos${WATCHOS_SDK_VERSION} ar "$@" +--- /dev/null ++++ b/watchOS/Resources/bin/arm64_32-apple-watchos-clang +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk watchos${WATCHOS_SDK_VERSION} clang -target arm64_32-apple-watchos "$@" +--- /dev/null ++++ b/watchOS/Resources/bin/arm64_32-apple-watchos-clang++ +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk watchos${WATCHOS_SDK_VERSION} clang++ -target arm64_32-apple-watchos "$@" +--- /dev/null ++++ b/watchOS/Resources/bin/arm64_32-apple-watchos-cpp +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk watchos${WATCHOS_SDK_VERSION} clang -target arm64_32-apple-watchos -E "$@" +--- /dev/null ++++ b/watchOS/Resources/bin/x86_64-apple-watchos-simulator-ar +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk watchsimulator${WATCHOS_SDK_VERSION} ar "$@" +--- /dev/null ++++ b/watchOS/Resources/bin/x86_64-apple-watchos-simulator-clang +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk watchsimulator${WATCHOS_SDK_VERSION} clang -target x86_64-apple-watchos-simulator "$@" +--- /dev/null ++++ b/watchOS/Resources/bin/x86_64-apple-watchos-simulator-clang++ +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk watchsimulator${WATCHOS_SDK_VERSION} clang++ -target x86_64-apple-watchos-simulator "$@" +--- /dev/null ++++ b/watchOS/Resources/bin/x86_64-apple-watchos-simulator-cpp +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk watchsimulator${WATCHOS_SDK_VERSION} clang -target x86_64-apple-watchos-simulator -E "$@" +--- /dev/null ++++ b/watchOS/Resources/dylib-Info-template.plist +@@ -0,0 +1,26 @@ ++ ++ ++ ++ ++ CFBundleDevelopmentRegion ++ en ++ CFBundleExecutable ++ ++ CFBundleIdentifier ++ ++ CFBundleInfoDictionaryVersion ++ 6.0 ++ CFBundlePackageType ++ APPL ++ CFBundleShortVersionString ++ 1.0 ++ CFBundleSupportedPlatforms ++ ++ 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/_cross_target.py.tmpl b/patch/Python/_cross_target.py.tmpl new file mode 100644 index 00000000..458ec074 --- /dev/null +++ b/patch/Python/_cross_target.py.tmpl @@ -0,0 +1,77 @@ +# A site package that turns a macOS virtual environment +# into an {{arch}} {{sdk}} cross-platform virtual environment +import platform +import subprocess +import sys +import sysconfig + +########################################################################### +# sys module patches +########################################################################### +sys.cross_compiling = True +sys.platform = "{{platform}}" +sys.implementation._multiarch = "{{arch}}-{{sdk}}" +sys.base_prefix = sysconfig.get_config_var("prefix") +sys.base_exec_prefix = sysconfig.get_config_var("prefix") + +########################################################################### +# subprocess module patches +########################################################################### +subprocess._can_fork_exec = True + + +########################################################################### +# platform module patches +########################################################################### + +def cross_system(): + return "{{os}}" + + +def cross_uname(): + return platform.uname_result( + system="{{os}}", + node="build", + release="{{version_min}}", + version="", + machine="{{arch}}", + ) + + +def cross_ios_ver(system="", release="", model="", is_simulator=False): + if system == "": + system = "{{os}}" + if release == "": + release = "{{version_min}}" + if model == "": + model = "{{sdk}}" + + return platform.IOSVersionInfo(system, release, model, {{is_simulator}}) + + +platform.system = cross_system +platform.uname = cross_uname +platform.ios_ver = cross_ios_ver + + +########################################################################### +# sysconfig module patches +########################################################################### + +def cross_get_platform(): + return "{{platform}}-{{version_min}}-{{arch}}-{{sdk}}" + + +def cross_get_sysconfigdata_name(): + return "_sysconfigdata__{{platform}}_{{arch}}-{{sdk}}" + + +sysconfig.get_platform = cross_get_platform +sysconfig._get_sysconfigdata_name = cross_get_sysconfigdata_name + +# Ensure module-level values cached at time of import are updated. +sysconfig._BASE_PREFIX = sys.base_prefix +sysconfig._BASE_EXEC_PREFIX = sys.base_exec_prefix + +# Force sysconfig data to be loaded (and cached). +sysconfig._init_config_vars() diff --git a/patch/Python/_cross_venv.py b/patch/Python/_cross_venv.py new file mode 100644 index 00000000..9caddf60 --- /dev/null +++ b/patch/Python/_cross_venv.py @@ -0,0 +1,105 @@ +import shutil +import sys +import sysconfig +from pathlib import Path + +SITE_PACKAGE_PATH = Path(__file__).parent + +########################################################################### +# importlib module patches +########################################################################### + + +def patch_env_create(env): + """ + Patch the process of creating virtual environments to ensure that the cross + environment modification files are also copied as part of environment + creation. + """ + old_pip_env_create = env._PipBackend.create + + def pip_env_create(self, path, *args, **kwargs): + result = old_pip_env_create(self, path, *args, **kwargs) + # Copy any _cross_*.pth or _cross_*.py file, plus the cross-platform + # sysconfigdata module and sysconfig_vars JSON to the new environment. + data_name = sysconfig._get_sysconfigdata_name() + json_name = data_name.replace("_sysconfigdata", "_sysconfig_vars") + for filename in [ + "_cross_venv.pth", + "_cross_venv.py", + f"_cross_{sys.implementation._multiarch.replace('-', '_')}.py", + f"{data_name}.py", + f"{json_name}.json", + ]: + src = SITE_PACKAGE_PATH / filename + target = Path(path) / src.relative_to( + SITE_PACKAGE_PATH.parent.parent.parent + ) + if not target.exists(): + shutil.copy(src, target) + return result + + env._PipBackend.create = pip_env_create + + +# Import hook that patches the creation of virtual environments by `build` +# +# The approach used here is the same as the one used by virtualenv to patch +# distutils (but without support for the older load_module API). +# https://docs.python.org/3/library/importlib.html#setting-up-an-importer +_BUILD_PATCH = ("build.env",) + + +class _Finder: + """A meta path finder that allows patching the imported build modules.""" + + fullname = None + + # lock[0] is threading.Lock(), but initialized lazily to avoid importing + # threading very early at startup, because there are gevent-based + # applications that need to be first to import threading by themselves. + # See https://github.com/pypa/virtualenv/issues/1895 for details. + lock = [] # noqa: RUF012 + + def find_spec(self, fullname, path, target=None): + if fullname in _BUILD_PATCH and self.fullname is None: + # initialize lock[0] lazily + if len(self.lock) == 0: + import threading + + lock = threading.Lock() + # there is possibility that two threads T1 and T2 are + # simultaneously running into find_spec, observing .lock as + # empty, and further going into hereby initialization. However + # due to the GIL, list.append() operation is atomic and this + # way only one of the threads will "win" to put the lock + # - that every thread will use - into .lock[0]. + # https://docs.python.org/3/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe + self.lock.append(lock) + + from functools import partial + from importlib.util import find_spec + + with self.lock[0]: + self.fullname = fullname + try: + spec = find_spec(fullname, path) + if spec is not None: + # https://www.python.org/dev/peps/pep-0451/#how-loading-will-work + old = spec.loader.exec_module + func = self.exec_module + if old is not func: + spec.loader.exec_module = partial(func, old) + return spec + finally: + self.fullname = None + return None + + @staticmethod + def exec_module(old, module): + old(module) + if module.__name__ in _BUILD_PATCH: + patch_env_create(module) + + +sys.meta_path.insert(0, _Finder()) diff --git a/patch/Python/app-store-compliance.patch b/patch/Python/app-store-compliance.patch new file mode 100644 index 00000000..f4b7decc --- /dev/null +++ b/patch/Python/app-store-compliance.patch @@ -0,0 +1,29 @@ +diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py +index d6c83a75c1c..19ed4e01091 100644 +--- a/Lib/test/test_urlparse.py ++++ b/Lib/test/test_urlparse.py +@@ -237,11 +237,6 @@ def test_roundtrips(self): + '','',''), + ('git+ssh', 'git@github.com','/user/project.git', + '', '')), +- ('itms-services://?action=download-manifest&url=https://example.com/app', +- ('itms-services', '', '', '', +- 'action=download-manifest&url=https://example.com/app', ''), +- ('itms-services', '', '', +- 'action=download-manifest&url=https://example.com/app', '')), + ('+scheme:path/to/file', + ('', '', '+scheme:path/to/file', '', '', ''), + ('', '', '+scheme:path/to/file', '', '')), +diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py +index 8f724f907d4..148caf742c9 100644 +--- a/Lib/urllib/parse.py ++++ b/Lib/urllib/parse.py +@@ -59,7 +59,7 @@ + 'imap', 'wais', 'file', 'mms', 'https', 'shttp', + 'snews', 'prospero', 'rtsp', 'rtsps', 'rtspu', 'rsync', + 'svn', 'svn+ssh', 'sftp', 'nfs', 'git', 'git+ssh', +- 'ws', 'wss', 'itms-services'] ++ 'ws', 'wss'] + + uses_params = ['', 'ftp', 'hdl', 'prospero', 'http', 'imap', + 'https', 'shttp', 'rtsp', 'rtsps', 'rtspu', 'sip', diff --git a/patch/Python/make_cross_venv.py b/patch/Python/make_cross_venv.py new file mode 100644 index 00000000..f8dad270 --- /dev/null +++ b/patch/Python/make_cross_venv.py @@ -0,0 +1,144 @@ +import json +import pprint +import shutil +import sys +from pathlib import Path +from importlib import util as importlib_util + + +def localized_vars(orig_vars, slice_path): + """Update (where possible) any references to build-time variables with the + best guess of the installed location. + """ + # The host's sysconfigdata will include references to build-time variables. + # Update these to refer to the current known install location. + orig_prefix = orig_vars["prefix"] + localized_vars = {} + for key, value in orig_vars.items(): + final = value + if isinstance(value, str): + # Replace any reference to the build installation prefix + final = final.replace(orig_prefix, str(slice_path)) + # Replace any reference to the build-time Framework location + final = final.replace("-F .", f"-F {slice_path}") + localized_vars[key] = final + + return localized_vars + + +def localize_sysconfigdata(platform_config_path, venv_site_packages): + """Localize a sysconfigdata python module. + + :param platform_config_path: The platform config that contains the + sysconfigdata module to localize. + :param venv_site_packages: The site packages folder where the localized + sysconfigdata module should be output. + """ + # Find the "_sysconfigdata_*.py" file in the platform config + sysconfigdata_path = next(platform_config_path.glob("_sysconfigdata_*.py")) + + # Import the sysconfigdata module + spec = importlib_util.spec_from_file_location( + sysconfigdata_path.stem, + sysconfigdata_path + ) + if spec is None: + msg = f"Unable to load spec for {sysconfigdata_path}" + raise ValueError(msg) + if spec.loader is None: + msg = f"Spec for {sysconfigdata_path} does not define a loader" + raise ValueError(msg) + sysconfigdata = importlib_util.module_from_spec(spec) + spec.loader.exec_module(sysconfigdata) + + # Write the updated sysconfigdata module into the cross-platform site. + slice_path = sysconfigdata_path.parent.parent.parent + with (venv_site_packages / sysconfigdata_path.name).open("w") as f: + f.write(f"# Generated from {sysconfigdata_path}\n") + f.write("build_time_vars = ") + pprint.pprint( + localized_vars(sysconfigdata.build_time_vars, slice_path), + stream=f, + compact=True + ) + + +def localize_sysconfig_vars(platform_config_path, venv_site_packages): + """Localize a sysconfig_vars.json file. + + :param platform_config_path: The platform config that contains the + sysconfigdata module to localize. + :param venv_site_packages: The site-packages folder where the localized + sysconfig_vars.json file should be output. + """ + # Find the "_sysconfig_vars_*.json" file in the platform config + sysconfig_vars_path = next(platform_config_path.glob("_sysconfig_vars_*.json")) + + with sysconfig_vars_path.open("rb") as f: + build_time_vars = json.load(f) + + slice_path = sysconfig_vars_path.parent.parent.parent + with (venv_site_packages / sysconfig_vars_path.name).open("w") as f: + json.dump(localized_vars(build_time_vars, slice_path), f, indent=2) + + +def make_cross_venv(venv_path: Path, platform_config_path: Path): + """Convert a virtual environment into a cross-platform environment. + + :param venv_path: The path to the root of the venv. + :param platform_config_path: The path containing the platform config. + """ + if not venv_path.exists(): + raise ValueError(f"Virtual environment {venv_path} does not exist.") + if not (venv_path / "bin/python3").exists(): + raise ValueError(f"{venv_path} does not appear to be a virtual environment.") + + print( + f"Converting {venv_path} into a {platform_config_path.name} environment... ", + end="", + ) + + LIB_PATH = f"lib/python{sys.version_info[0]}.{sys.version_info[1]}" + + # Update path references in the sysconfigdata to reflect local conditions. + venv_site_packages = venv_path / LIB_PATH / "site-packages" + localize_sysconfigdata(platform_config_path, venv_site_packages) + localize_sysconfig_vars(platform_config_path, venv_site_packages) + + # Copy in the site-package environment modifications. + cross_multiarch = f"_cross_{platform_config_path.name.replace('-', '_')}" + shutil.copy( + platform_config_path / f"{cross_multiarch}.py", + venv_site_packages / f"{cross_multiarch}.py", + ) + shutil.copy( + platform_config_path / "_cross_venv.py", + venv_site_packages / "_cross_venv.py", + ) + # Write the .pth file that will enable the cross-env modifications + (venv_site_packages / "_cross_venv.pth").write_text( + f"import {cross_multiarch}; import _cross_venv\n" + ) + + print("done.") + + +if __name__ == "__main__": + try: + platform_config_path = Path(sys.argv[2]).resolve() + except IndexError: + platform_config_path = Path(__file__).parent + + try: + venv_path = Path(sys.argv[1]).resolve() + make_cross_venv(venv_path, platform_config_path) + except IndexError: + print(""" +Convert a virtual environment in to a cross-platform environment. + +Usage: + make_cross_venv () + +If an explicit platform config isn't provided, it is assumed the directory +containing the make_cross_venv script *is* a platform config. +""") diff --git a/patch/Python/module.modulemap.prefix b/patch/Python/module.modulemap.prefix new file mode 100644 index 00000000..e3b3aafb --- /dev/null +++ b/patch/Python/module.modulemap.prefix @@ -0,0 +1,20 @@ +module Python { + umbrella header "Python.h" + export * + link "Python" + + exclude header "datetime.h" + exclude header "dynamic_annotations.h" + exclude header "errcode.h" + exclude header "frameobject.h" + exclude header "marshal.h" + exclude header "opcode_ids.h" + exclude header "opcode.h" + exclude header "osdefs.h" + exclude header "py_curses.h" + exclude header "pyconfig-arm32_64.h" + exclude header "pyconfig-arm64.h" + exclude header "pyconfig-x86_64.h" + exclude header "pydtrace.h" + exclude header "pyexpat.h" + exclude header "structmember.h" diff --git a/patch/Python/pyconfig-iOS.h b/patch/Python/pyconfig-iOS.h deleted file mode 100644 index 4acff2c6..00000000 --- a/patch/Python/pyconfig-iOS.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifdef __arm64__ -#include "pyconfig-arm64.h" -#endif - -#ifdef __x86_64__ -#include "pyconfig-x86_64.h" -#endif diff --git a/patch/Python/pyconfig-tvOS.h b/patch/Python/pyconfig-tvOS.h deleted file mode 100644 index d4afe05b..00000000 --- a/patch/Python/pyconfig-tvOS.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifdef __arm64__ -#include "pyconfig-arm64.h" -#endif - -#ifdef __x86_64__ -#include "pyconfig-x86_64.h" -#endif \ No newline at end of file diff --git a/patch/Python/pyconfig-watchOS.h b/patch/Python/pyconfig-watchOS.h deleted file mode 100644 index f842b987..00000000 --- a/patch/Python/pyconfig-watchOS.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifdef __arm64__ -# ifdef __LP64__ -#include "pyconfig-arm64.h" -# else -#include "pyconfig-arm64_32.h" -# endif -#endif - -#ifdef __x86_64__ -#include "pyconfig-x86_64.h" -#endif diff --git a/patch/Python/release.common.exclude b/patch/Python/release.common.exclude deleted file mode 100644 index bec2b860..00000000 --- a/patch/Python/release.common.exclude +++ /dev/null @@ -1,26 +0,0 @@ -# This is a list of support package path patterns that we exclude -# from all Python-Apple-support tarballs. -# It is used by `tar -X` during the Makefile build. -# Remove standard library test suites. -python-stdlib/ctypes/test -python-stdlib/distutils/tests -python-stdlib/lib2to3/tests -python-stdlib/sqlite3/test -python-stdlib/test -# Remove config-* directory, which is used for compiling C extension modules. -python-stdlib/config-* -# Remove ensurepip. If user code needs pip, it can add it to -python-stdlib/ensurepip -# Remove libraries supporting IDLE. We don't need to ship an IDE -python-stdlib/idlelib -# Remove Tcl/Tk GUI code. We don't build against Tcl/Tk at the moment, so this -# will not work. -python-stdlib/tkinter -python-stdlib/turtle.py -python-stdlib/turtledemo -# Remove site-packages directory. The template unpacks user code and -# dependencies to a different path. -python-stdlib/site-packages -# Remove pyc files. These take up space, but since most stdlib modules are -# never imported by user code, they mostly have no value. -*/__pycache__ diff --git a/patch/Python/release.iOS.exclude b/patch/Python/release.iOS.exclude index fb1f841b..f1d0f75e 100644 --- a/patch/Python/release.iOS.exclude +++ b/patch/Python/release.iOS.exclude @@ -1,10 +1,6 @@ # This is a list of support package path patterns that we exclude -# from iOS Python-Apple-support tarballs. +# from all Python-Apple-support tarballs. # It is used by `tar -X` during the Makefile build. -# -# Remove command-line curses toolkit. -python-stdlib/curses -# Remove the testing binary modules -python-stdlib/lib-dynload/*_test*.dylib -python-stdlib/lib-dynload/_xx*.dylib -python-stdlib/lib-dynload/xx*.dylib +# Remove pyc files. These take up space, but since most stdlib modules are +# never imported by user code, they mostly have no value. +*/__pycache__ diff --git a/patch/Python/release.macOS.exclude b/patch/Python/release.macOS.exclude index 1428348e..3bc247c1 100644 --- a/patch/Python/release.macOS.exclude +++ b/patch/Python/release.macOS.exclude @@ -1,8 +1,28 @@ -# 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 +._Headers +._Python +._Resources +Resources/._Python.app +Resources/Python.app +Versions/._Current +Versions/*/.__CodeSignature +Versions/*/._bin +Versions/*/._etc +Versions/*/._Frameworks +Versions/*/._Headers +Versions/*/._include +Versions/*/._lib +Versions/*/._Resources +Versions/*/._share +Versions/*/bin +Versions/*/etc +Versions/*/Frameworks +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 e9ff418a..f1d0f75e 100644 --- a/patch/Python/release.tvOS.exclude +++ b/patch/Python/release.tvOS.exclude @@ -1,10 +1,6 @@ # This is a list of support package path patterns that we exclude -# from tvOS Python-Apple-support tarballs. +# from all Python-Apple-support tarballs. # It is used by `tar -X` during the Makefile build. -# -# Remove command-line curses toolkit. -python-stdlib/curses -# Remove the testing binary modules -python-stdlib/lib-dynload/*_test*.dylib -python-stdlib/lib-dynload/_xx*.dylib -python-stdlib/lib-dynload/xx*.dylib +# Remove pyc files. These take up space, but since most stdlib modules are +# never imported by user code, they mostly have no value. +*/__pycache__ diff --git a/patch/Python/test.exclude b/patch/Python/release.visionOS.exclude similarity index 55% rename from patch/Python/test.exclude rename to patch/Python/release.visionOS.exclude index add994a7..f1d0f75e 100644 --- a/patch/Python/test.exclude +++ b/patch/Python/release.visionOS.exclude @@ -1,7 +1,6 @@ -# This is a list of Python standard library path patterns -# we exclude from the embedded device Python-Apple-support test tarballs. +# This is a list of support package path patterns that we exclude +# from all Python-Apple-support tarballs. # It is used by `tar -X` during the Makefile build. -# # Remove pyc files. These take up space, but since most stdlib modules are # never imported by user code, they mostly have no value. -*/__pycache__ \ No newline at end of file +*/__pycache__ diff --git a/patch/Python/release.watchOS.exclude b/patch/Python/release.watchOS.exclude index c8b1d66c..f1d0f75e 100644 --- a/patch/Python/release.watchOS.exclude +++ b/patch/Python/release.watchOS.exclude @@ -1,10 +1,6 @@ # This is a list of support package path patterns that we exclude -# from watchOS Python-Apple-support tarballs. +# from all Python-Apple-support tarballs. # It is used by `tar -X` during the Makefile build. -# -# Remove command-line curses toolkit. -python-stdlib/curses -# Remove the testing binary modules -python-stdlib/lib-dynload/*_test*.dylib -python-stdlib/lib-dynload/_xx*.dylib -python-stdlib/lib-dynload/xx*.dylib +# Remove pyc files. These take up space, but since most stdlib modules are +# never imported by user code, they mostly have no value. +*/__pycache__ diff --git a/patch/Python/sitecustomize.iOS.py b/patch/Python/sitecustomize.iOS.py deleted file mode 100644 index d7d86e36..00000000 --- a/patch/Python/sitecustomize.iOS.py +++ /dev/null @@ -1,99 +0,0 @@ -# A site customization that can be used to trick pip into installing -# packages cross-platform. If the folder containing this file is on -# your PYTHONPATH when you invoke pip, pip will behave as if it were -# running on {{os}}. -import distutils.ccompiler -import distutils.unixccompiler -import os -import platform -import sys -import sysconfig -import types - -# Make platform.system() return "{{os}}" -def custom_system(): - return "{{os}}" - -platform.system = custom_system - -# Make sysconfig.get_platform() return "{{tag}}" -def custom_get_platform(): - return "{{tag}}" - -sysconfig.get_platform = custom_get_platform - -# Make distutils raise errors if you try to use it to build modules. -DISABLED_COMPILER_ERROR = "Cannot compile native modules" - -distutils.ccompiler.get_default_compiler = lambda *args, **kwargs: "disabled" -distutils.ccompiler.compiler_class["disabled"] = ( - "disabledcompiler", - "DisabledCompiler", - "Compiler disabled ({})".format(DISABLED_COMPILER_ERROR), -) - - -def disabled_compiler(prefix): - # No need to give any more advice here: that will come from the higher-level code in pip. - from distutils.errors import DistutilsPlatformError - - raise DistutilsPlatformError("{}: {}".format(prefix, DISABLED_COMPILER_ERROR)) - - -class DisabledCompiler(distutils.ccompiler.CCompiler): - compiler_type = "disabled" - - def preprocess(*args, **kwargs): - disabled_compiler("CCompiler.preprocess") - - def compile(*args, **kwargs): - disabled_compiler("CCompiler.compile") - - def create_static_lib(*args, **kwargs): - disabled_compiler("CCompiler.create_static_lib") - - def link(*args, **kwargs): - disabled_compiler("CCompiler.link") - - -# To maximize the chance of the build getting as far as actually calling compile(), make -# sure the class has all of the expected attributes. -for name in [ - "src_extensions", - "obj_extension", - "static_lib_extension", - "shared_lib_extension", - "static_lib_format", - "shared_lib_format", - "exe_extension", -]: - setattr( - DisabledCompiler, name, getattr(distutils.unixccompiler.UnixCCompiler, name) - ) - -DisabledCompiler.executables = { - name: [DISABLED_COMPILER_ERROR.replace(" ", "_")] - for name in distutils.unixccompiler.UnixCCompiler.executables -} - -disabled_mod = types.ModuleType("distutils.disabledcompiler") -disabled_mod.DisabledCompiler = DisabledCompiler -sys.modules["distutils.disabledcompiler"] = disabled_mod - - -# Try to disable native builds for packages which don't use the distutils native build -# system at all (e.g. uwsgi), or only use it to wrap an external build script (e.g. pynacl). -for tool in ["ar", "as", "cc", "cxx", "ld"]: - os.environ[tool.upper()] = DISABLED_COMPILER_ERROR.replace(" ", "_") - - -# Call the next sitecustomize script if there is one -# (https://nedbatchelder.com/blog/201001/running_code_at_python_startup.html). -del sys.modules["sitecustomize"] -this_dir = os.path.dirname(__file__) -path_index = sys.path.index(this_dir) -del sys.path[path_index] -try: - import sitecustomize # noqa: F401 -finally: - sys.path.insert(path_index, this_dir) diff --git a/patch/Python/sitecustomize.macOS.py b/patch/Python/sitecustomize.macOS.py deleted file mode 100644 index 500714da..00000000 --- a/patch/Python/sitecustomize.macOS.py +++ /dev/null @@ -1,14 +0,0 @@ -# A site customization that can be used to trick pip into installing -# packages cross-platform. If the folder containing this file is on -# your PYTHONPATH when you invoke pip, pip will behave as if it were -# running on {{arch}}. -import platform - -# Make platform.mac_ver() return {{arch}} -orig_mac_ver = platform.mac_ver - -def custom_mac_ver(): - orig = orig_mac_ver() - return orig[0], orig[1], "{{arch}}" - -platform.mac_ver = custom_mac_ver diff --git a/patch/Python/sitecustomize.py.tmpl b/patch/Python/sitecustomize.py.tmpl new file mode 100644 index 00000000..0330575a --- /dev/null +++ b/patch/Python/sitecustomize.py.tmpl @@ -0,0 +1,22 @@ +# A site customization that can be used to trick pip into installing packages +# cross-platform. If the folder containing this file is on your PYTHONPATH when +# you invoke python, the interpreter will behave as if it were running on +# {{arch}} {{sdk}}. +import sys +import os + +# Apply the cross-platform patch +import _cross_{{arch}}_{{sdk}} +import _cross_venv + + +# Call the next sitecustomize script if there is one +# (https://nedbatchelder.com/blog/201001/running_code_at_python_startup.html). +del sys.modules["sitecustomize"] +this_dir = os.path.dirname(__file__) +path_index = sys.path.index(this_dir) +del sys.path[path_index] +try: + import sitecustomize # noqa: F401 +finally: + sys.path.insert(path_index, this_dir) diff --git a/patch/Python/sitecustomize.tvOS.py b/patch/Python/sitecustomize.tvOS.py deleted file mode 100644 index d7d86e36..00000000 --- a/patch/Python/sitecustomize.tvOS.py +++ /dev/null @@ -1,99 +0,0 @@ -# A site customization that can be used to trick pip into installing -# packages cross-platform. If the folder containing this file is on -# your PYTHONPATH when you invoke pip, pip will behave as if it were -# running on {{os}}. -import distutils.ccompiler -import distutils.unixccompiler -import os -import platform -import sys -import sysconfig -import types - -# Make platform.system() return "{{os}}" -def custom_system(): - return "{{os}}" - -platform.system = custom_system - -# Make sysconfig.get_platform() return "{{tag}}" -def custom_get_platform(): - return "{{tag}}" - -sysconfig.get_platform = custom_get_platform - -# Make distutils raise errors if you try to use it to build modules. -DISABLED_COMPILER_ERROR = "Cannot compile native modules" - -distutils.ccompiler.get_default_compiler = lambda *args, **kwargs: "disabled" -distutils.ccompiler.compiler_class["disabled"] = ( - "disabledcompiler", - "DisabledCompiler", - "Compiler disabled ({})".format(DISABLED_COMPILER_ERROR), -) - - -def disabled_compiler(prefix): - # No need to give any more advice here: that will come from the higher-level code in pip. - from distutils.errors import DistutilsPlatformError - - raise DistutilsPlatformError("{}: {}".format(prefix, DISABLED_COMPILER_ERROR)) - - -class DisabledCompiler(distutils.ccompiler.CCompiler): - compiler_type = "disabled" - - def preprocess(*args, **kwargs): - disabled_compiler("CCompiler.preprocess") - - def compile(*args, **kwargs): - disabled_compiler("CCompiler.compile") - - def create_static_lib(*args, **kwargs): - disabled_compiler("CCompiler.create_static_lib") - - def link(*args, **kwargs): - disabled_compiler("CCompiler.link") - - -# To maximize the chance of the build getting as far as actually calling compile(), make -# sure the class has all of the expected attributes. -for name in [ - "src_extensions", - "obj_extension", - "static_lib_extension", - "shared_lib_extension", - "static_lib_format", - "shared_lib_format", - "exe_extension", -]: - setattr( - DisabledCompiler, name, getattr(distutils.unixccompiler.UnixCCompiler, name) - ) - -DisabledCompiler.executables = { - name: [DISABLED_COMPILER_ERROR.replace(" ", "_")] - for name in distutils.unixccompiler.UnixCCompiler.executables -} - -disabled_mod = types.ModuleType("distutils.disabledcompiler") -disabled_mod.DisabledCompiler = DisabledCompiler -sys.modules["distutils.disabledcompiler"] = disabled_mod - - -# Try to disable native builds for packages which don't use the distutils native build -# system at all (e.g. uwsgi), or only use it to wrap an external build script (e.g. pynacl). -for tool in ["ar", "as", "cc", "cxx", "ld"]: - os.environ[tool.upper()] = DISABLED_COMPILER_ERROR.replace(" ", "_") - - -# Call the next sitecustomize script if there is one -# (https://nedbatchelder.com/blog/201001/running_code_at_python_startup.html). -del sys.modules["sitecustomize"] -this_dir = os.path.dirname(__file__) -path_index = sys.path.index(this_dir) -del sys.path[path_index] -try: - import sitecustomize # noqa: F401 -finally: - sys.path.insert(path_index, this_dir) diff --git a/patch/Python/sitecustomize.watchOS.py b/patch/Python/sitecustomize.watchOS.py deleted file mode 100644 index d7d86e36..00000000 --- a/patch/Python/sitecustomize.watchOS.py +++ /dev/null @@ -1,99 +0,0 @@ -# A site customization that can be used to trick pip into installing -# packages cross-platform. If the folder containing this file is on -# your PYTHONPATH when you invoke pip, pip will behave as if it were -# running on {{os}}. -import distutils.ccompiler -import distutils.unixccompiler -import os -import platform -import sys -import sysconfig -import types - -# Make platform.system() return "{{os}}" -def custom_system(): - return "{{os}}" - -platform.system = custom_system - -# Make sysconfig.get_platform() return "{{tag}}" -def custom_get_platform(): - return "{{tag}}" - -sysconfig.get_platform = custom_get_platform - -# Make distutils raise errors if you try to use it to build modules. -DISABLED_COMPILER_ERROR = "Cannot compile native modules" - -distutils.ccompiler.get_default_compiler = lambda *args, **kwargs: "disabled" -distutils.ccompiler.compiler_class["disabled"] = ( - "disabledcompiler", - "DisabledCompiler", - "Compiler disabled ({})".format(DISABLED_COMPILER_ERROR), -) - - -def disabled_compiler(prefix): - # No need to give any more advice here: that will come from the higher-level code in pip. - from distutils.errors import DistutilsPlatformError - - raise DistutilsPlatformError("{}: {}".format(prefix, DISABLED_COMPILER_ERROR)) - - -class DisabledCompiler(distutils.ccompiler.CCompiler): - compiler_type = "disabled" - - def preprocess(*args, **kwargs): - disabled_compiler("CCompiler.preprocess") - - def compile(*args, **kwargs): - disabled_compiler("CCompiler.compile") - - def create_static_lib(*args, **kwargs): - disabled_compiler("CCompiler.create_static_lib") - - def link(*args, **kwargs): - disabled_compiler("CCompiler.link") - - -# To maximize the chance of the build getting as far as actually calling compile(), make -# sure the class has all of the expected attributes. -for name in [ - "src_extensions", - "obj_extension", - "static_lib_extension", - "shared_lib_extension", - "static_lib_format", - "shared_lib_format", - "exe_extension", -]: - setattr( - DisabledCompiler, name, getattr(distutils.unixccompiler.UnixCCompiler, name) - ) - -DisabledCompiler.executables = { - name: [DISABLED_COMPILER_ERROR.replace(" ", "_")] - for name in distutils.unixccompiler.UnixCCompiler.executables -} - -disabled_mod = types.ModuleType("distutils.disabledcompiler") -disabled_mod.DisabledCompiler = DisabledCompiler -sys.modules["distutils.disabledcompiler"] = disabled_mod - - -# Try to disable native builds for packages which don't use the distutils native build -# system at all (e.g. uwsgi), or only use it to wrap an external build script (e.g. pynacl). -for tool in ["ar", "as", "cc", "cxx", "ld"]: - os.environ[tool.upper()] = DISABLED_COMPILER_ERROR.replace(" ", "_") - - -# Call the next sitecustomize script if there is one -# (https://nedbatchelder.com/blog/201001/running_code_at_python_startup.html). -del sys.modules["sitecustomize"] -this_dir = os.path.dirname(__file__) -path_index = sys.path.index(this_dir) -del sys.path[path_index] -try: - import sitecustomize # noqa: F401 -finally: - sys.path.insert(path_index, this_dir) diff --git a/patch/libffi-3.4.4.patch b/patch/libffi-3.4.4.patch deleted file mode 100644 index 6e143449..00000000 --- a/patch/libffi-3.4.4.patch +++ /dev/null @@ -1,174 +0,0 @@ -diff --git a/config.sub b/config.sub -old mode 100644 -new mode 100755 -index dba16e8..630b5d9 ---- a/config.sub -+++ b/config.sub -@@ -1126,7 +1126,7 @@ case $cpu-$vendor in - xscale-* | xscalee[bl]-*) - cpu=`echo "$cpu" | sed 's/^xscale/arm/'` - ;; -- arm64-* | aarch64le-*) -+ arm64-* | arm64_32-* | aarch64le-*) - cpu=aarch64 - ;; - -@@ -1728,7 +1728,7 @@ case $os in - | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ - | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ - | hiux* | abug | nacl* | netware* | windows* \ -- | os9* | macos* | osx* | ios* \ -+ | os9* | macos* | osx* | ios* | watchos* | tvos* \ - | mpw* | magic* | mmixware* | mon960* | lnews* \ - | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ - | aos* | aros* | cloudabi* | sortix* | twizzler* \ -@@ -1790,6 +1790,8 @@ case $kernel-$os in - ;; - os2-emx) - ;; -+ ios*-simulator | tvos*-simulator | watchos*-simulator) -+ ;; - *-eabi* | *-gnueabi*) - ;; - -*) -diff --git a/generate-darwin-source-and-headers.py b/generate-darwin-source-and-headers.py -index 5b60ccc..c801dc0 100755 ---- a/generate-darwin-source-and-headers.py -+++ b/generate-darwin-source-and-headers.py -@@ -48,7 +48,6 @@ class armv7_platform(Platform): - - - class ios_simulator_i386_platform(i386_platform): -- triple = 'i386-apple-darwin11' - target = 'i386-apple-ios-simulator' - directory = 'darwin_ios' - sdk = 'iphonesimulator' -@@ -56,7 +55,6 @@ class ios_simulator_i386_platform(i386_platform): - - - class ios_simulator_x86_64_platform(x86_64_platform): -- triple = 'x86_64-apple-darwin13' - target = 'x86_64-apple-ios-simulator' - directory = 'darwin_ios' - sdk = 'iphonesimulator' -@@ -64,7 +62,6 @@ class ios_simulator_x86_64_platform(x86_64_platform): - - - class ios_simulator_arm64_platform(arm64_platform): -- triple = 'aarch64-apple-darwin20' - target = 'arm64-apple-ios-simulator' - directory = 'darwin_ios' - sdk = 'iphonesimulator' -@@ -72,7 +69,6 @@ class ios_simulator_arm64_platform(arm64_platform): - - - class ios_device_armv7_platform(armv7_platform): -- triple = 'arm-apple-darwin11' - target = 'armv7-apple-ios' - directory = 'darwin_ios' - sdk = 'iphoneos' -@@ -80,7 +76,6 @@ class ios_device_armv7_platform(armv7_platform): - - - class ios_device_arm64_platform(arm64_platform): -- triple = 'aarch64-apple-darwin13' - target = 'arm64-apple-ios' - directory = 'darwin_ios' - sdk = 'iphoneos' -@@ -88,7 +83,6 @@ class ios_device_arm64_platform(arm64_platform): - - - class desktop_x86_64_platform(x86_64_platform): -- triple = 'x86_64-apple-darwin10' - target = 'x86_64-apple-macos' - directory = 'darwin_osx' - sdk = 'macosx' -@@ -96,7 +90,6 @@ class desktop_x86_64_platform(x86_64_platform): - - - class desktop_arm64_platform(arm64_platform): -- triple = 'aarch64-apple-darwin20' - target = 'arm64-apple-macos' - directory = 'darwin_osx' - sdk = 'macosx' -@@ -104,7 +97,6 @@ class desktop_arm64_platform(arm64_platform): - - - class tvos_simulator_x86_64_platform(x86_64_platform): -- triple = 'x86_64-apple-darwin13' - target = 'x86_64-apple-tvos-simulator' - directory = 'darwin_tvos' - sdk = 'appletvsimulator' -@@ -112,7 +104,6 @@ class tvos_simulator_x86_64_platform(x86_64_platform): - - - class tvos_simulator_arm64_platform(arm64_platform): -- triple = 'aarch64-apple-darwin20' - target = 'arm64-apple-tvos-simulator' - directory = 'darwin_tvos' - sdk = 'appletvsimulator' -@@ -120,7 +111,6 @@ class tvos_simulator_arm64_platform(arm64_platform): - - - class tvos_device_arm64_platform(arm64_platform): -- triple = 'aarch64-apple-darwin13' - target = 'arm64-apple-tvos' - directory = 'darwin_tvos' - sdk = 'appletvos' -@@ -128,7 +118,6 @@ class tvos_device_arm64_platform(arm64_platform): - - - class watchos_simulator_i386_platform(i386_platform): -- triple = 'i386-apple-darwin11' - target = 'i386-apple-watchos-simulator' - directory = 'darwin_watchos' - sdk = 'watchsimulator' -@@ -136,7 +125,6 @@ class watchos_simulator_i386_platform(i386_platform): - - - class watchos_simulator_x86_64_platform(x86_64_platform): -- triple = 'x86_64-apple-darwin13' - target = 'x86_64-apple-watchos-simulator' - directory = 'darwin_watchos' - sdk = 'watchsimulator' -@@ -144,7 +132,6 @@ class watchos_simulator_x86_64_platform(x86_64_platform): - - - class watchos_simulator_arm64_platform(arm64_platform): -- triple = 'aarch64-apple-darwin20' - target = 'arm64-apple-watchos-simulator' - directory = 'darwin_watchos' - sdk = 'watchsimulator' -@@ -152,7 +139,6 @@ class watchos_simulator_arm64_platform(arm64_platform): - - - class watchos_device_armv7k_platform(armv7_platform): -- triple = 'arm-apple-darwin11' - target = 'armv7k-apple-watchos' - directory = 'darwin_watchos' - sdk = 'watchos' -@@ -161,7 +147,6 @@ class watchos_device_armv7k_platform(armv7_platform): - - - class watchos_device_arm64_32_platform(arm64_platform): -- triple = 'aarch64-apple-darwin13' - target = 'arm64_32-apple-watchos' - directory = 'darwin_watchos' - sdk = 'watchos' -@@ -229,7 +214,15 @@ def build_target(platform, platform_headers): - working_dir = os.getcwd() - try: - os.chdir(build_dir) -- subprocess.check_call(['../configure', '-host', platform.triple], env=env) -+ subprocess.check_call( -+ [ -+ "../configure", -+ f"--host={platform.target}", -+ ] + ( -+ [] if platform.sdk == "macosx" else [f"--build={os.uname().machine}-apple-darwin"] -+ ), -+ env=env -+ ) - finally: - os.chdir(working_dir) - diff --git a/patch/make-relocatable.sh b/patch/make-relocatable.sh new file mode 100755 index 00000000..f268cd70 --- /dev/null +++ b/patch/make-relocatable.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +FRAMEWORK_BASEDIR=$1 +echo "Making $1 relocatable" +PYTHON_VER=${FRAMEWORK_BASEDIR##*/} +echo "Python version ${PYTHON_VER}" + +pushd ${FRAMEWORK_BASEDIR} + +echo "Rewrite ID of Python library" +install_name_tool -id @rpath/Python.framework/Versions/${PYTHON_VER}/Python Python > /dev/null +for dylib in `ls lib/*.*.dylib`; do + # lib + if [ "${dylib}" != "lib/libpython${PYTHON_VER}.dylib" ] ; then + echo Rewrite ID of ${dylib} + install_name_tool -id @rpath/Python.framework/Versions/${PYTHON_VER}/${dylib} ${FRAMEWORK_BASEDIR}/${dylib} + fi +done +for module in `find . -name "*.dylib" -type f -o -name "*.so" -type f`; do + if [ "$(otool -L ${module} | grep -c /Library/Frameworks/Python.framework)" != "0" ]; then + for dylib in `ls lib/*.*.dylib`; do + echo Rewrite references to ${dylib} in ${module} + install_name_tool -change /Library/Frameworks/Python.framework/Versions/${PYTHON_VER}/${dylib} @rpath/Python.framework/Versions/${PYTHON_VER}/${dylib} ${module} + done + fi +done +popd diff --git a/patch/make-xcrun-alias b/patch/make-xcrun-alias deleted file mode 100755 index 50340e14..00000000 --- a/patch/make-xcrun-alias +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -# A script that writes an executable xcrun alias. -# Arg 1: The name of the file to output -# Arg 2: The arguments to pass to xcrun -mkdir -p $(dirname $1) -cat << EOF > $1 -#!/bin/bash -xcrun $2 \$@ -EOF -chmod +x $1 diff --git a/patch/xz-5.4.4.patch b/patch/xz-5.4.4.patch deleted file mode 100644 index a964e51c..00000000 --- a/patch/xz-5.4.4.patch +++ /dev/null @@ -1,30 +0,0 @@ -diff -ur xz-5.4.2-orig/build-aux/config.sub xz-5.4.2/build-aux/config.sub ---- xz-5.4.2-orig/build-aux/config.sub 2023-03-19 00:08:55 -+++ xz-5.4.2/build-aux/config.sub 2023-04-13 12:54:51 -@@ -1126,7 +1126,7 @@ - xscale-* | xscalee[bl]-*) - cpu=`echo "$cpu" | sed 's/^xscale/arm/'` - ;; -- arm64-* | aarch64le-*) -+ arm64-* | arm64_32-* | aarch64le-*) - cpu=aarch64 - ;; - -@@ -1728,7 +1728,7 @@ - | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ - | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ - | hiux* | abug | nacl* | netware* | windows* \ -- | os9* | macos* | osx* | ios* \ -+ | os9* | macos* | osx* | ios* | tvos* | watchos* \ - | mpw* | magic* | mmixware* | mon960* | lnews* \ - | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ - | aos* | aros* | cloudabi* | sortix* | twizzler* \ -@@ -1791,6 +1791,8 @@ - os2-emx) - ;; - *-eabi* | *-gnueabi*) -+ ;; -+ ios*-simulator | tvos*-simulator | watchos*-simulator) - ;; - -*) - # Blank kernel with real OS is always fine.