https://github.com/python/cpython/commit/b33980a2e3f195c63e3aadeeebd8e50eb41ad70c
commit: b33980a2e3f195c63e3aadeeebd8e50eb41ad70c
branch: main
author: Russell Keith-Magee <russ...@keith-magee.com>
committer: ned-deily <n...@python.org>
date: 2024-03-06T23:24:52-05:00
summary:

gh-114099 - Add iOS testbed, plus Makefile target to invoke it. (gh-115930)

files:
A Misc/NEWS.d/next/Build/2024-02-26-13-13-53.gh-issue-114099.8lpX-7.rst
A iOS/testbed/Python.xcframework/Info.plist
A iOS/testbed/Python.xcframework/ios-arm64/README
A iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator/README
A iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj
A iOS/testbed/iOSTestbed/AppDelegate.h
A iOS/testbed/iOSTestbed/AppDelegate.m
A iOS/testbed/iOSTestbed/Assets.xcassets/AccentColor.colorset/Contents.json
A iOS/testbed/iOSTestbed/Assets.xcassets/AppIcon.appiconset/Contents.json
A iOS/testbed/iOSTestbed/Assets.xcassets/Contents.json
A iOS/testbed/iOSTestbed/Base.lproj/LaunchScreen.storyboard
A iOS/testbed/iOSTestbed/dylib-Info-template.plist
A iOS/testbed/iOSTestbed/iOSTestbed-Info.plist
A iOS/testbed/iOSTestbed/main.m
A iOS/testbed/iOSTestbedTests/iOSTestbedTests.m
M .gitignore
M Makefile.pre.in
M Modules/_testexternalinspection.c
M configure
M configure.ac
M iOS/README.rst

diff --git a/.gitignore b/.gitignore
index 2194b393aa4821..3e1213ef925305 100644
--- a/.gitignore
+++ b/.gitignore
@@ -69,7 +69,17 @@ Lib/test/data/*
 /_bootstrap_python
 /Makefile
 /Makefile.pre
+iOSTestbed.*
+iOS/Frameworks/
 iOS/Resources/Info.plist
+iOS/testbed/build
+iOS/testbed/Python.xcframework/ios-*/bin
+iOS/testbed/Python.xcframework/ios-*/include
+iOS/testbed/Python.xcframework/ios-*/lib
+iOS/testbed/Python.xcframework/ios-*/Python.framework
+iOS/testbed/iOSTestbed.xcodeproj/project.xcworkspace
+iOS/testbed/iOSTestbed.xcodeproj/xcuserdata
+iOS/testbed/iOSTestbed.xcodeproj/xcshareddata
 Mac/Makefile
 Mac/PythonLauncher/Info.plist
 Mac/PythonLauncher/Makefile
diff --git a/Makefile.pre.in b/Makefile.pre.in
index ee65ecd918ce2a..b1aaa594c9f6ef 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -1993,6 +1993,42 @@ testuniversal: all
        $(RUNSHARED) /usr/libexec/oah/translate \
                ./$(BUILDPYTHON) -E -m test -j 0 -u all $(TESTOPTS)
 
+# Run the test suite on the iOS simulator. Must be run on a macOS machine with
+# a full Xcode install that has an iPhone SE (3rd edition) simulator available.
+# This must be run *after* a `make install` has completed the build. The
+# `--with-framework-name` argument *cannot* be used when configuring the build.
+XCFOLDER=iOSTestbed.$(MULTIARCH).$(shell date +%s)
+XCRESULT=$(XCFOLDER)/$(MULTIARCH).xcresult
+.PHONY: testios
+testios:
+       @if test "$(MACHDEP)" != "ios"; then \
+               echo "Cannot run the iOS testbed for a non-iOS build."; \
+               exit 1;\
+       fi
+       @if test "$(findstring -iphonesimulator,$(MULTIARCH))" != 
"-iphonesimulator"; then \
+               echo "Cannot run the iOS testbed for non-simulator builds."; \
+               exit 1;\
+       fi
+       @if test $(PYTHONFRAMEWORK) != "Python"; then \
+               echo "Cannot run the iOS testbed with a non-default framework 
name."; \
+               exit 1;\
+       fi
+       @if ! test -d $(PYTHONFRAMEWORKPREFIX); then \
+               echo "Cannot find a finalized iOS Python.framework. Have you 
run 'make install' to finalize the framework build?"; \
+               exit 1;\
+       fi
+       # Copy the testbed project into the build folder
+       cp -r $(srcdir)/iOS/testbed $(XCFOLDER)
+       # Copy the framework from the install location to the testbed project.
+       cp -r $(PYTHONFRAMEWORKPREFIX)/* 
$(XCFOLDER)/Python.xcframework/ios-arm64_x86_64-simulator
+       # Run the test suite for the Xcode project, targeting the iOS simulator.
+       # If the suite fails, extract and print the console output, then 
re-raise the failure
+       if ! xcodebuild test -project $(XCFOLDER)/iOSTestbed.xcodeproj -scheme 
"iOSTestbed" -destination "platform=iOS Simulator,name=iPhone SE (3rd 
Generation)" -resultBundlePath $(XCRESULT) ; then \
+               xcrun xcresulttool get --path $(XCRESULT) --id $$(xcrun 
xcresulttool get --path $(XCRESULT) --format json | $(PYTHON_FOR_BUILD) -c 
"import sys, json; result = json.load(sys.stdin); 
print(result['actions']['_values'][0]['actionResult']['logRef']['id']['_value'])");
 \
+               echo ; \
+               exit 1; \
+       fi
+
 # Like test, but using --slow-ci which enables all test resources and use
 # longer timeout. Run an optional pybuildbot.identify script to include
 # information about the build environment.
@@ -2712,7 +2748,7 @@ frameworkinstallextras:
 # subdirectory. The install has put these folders in the same folder as
 # Python.framework; Move the headers to their final framework-compatible home.
 .PHONY: frameworkinstallmobileheaders
-frameworkinstallmobileheaders:
+frameworkinstallmobileheaders: frameworkinstallunversionedstructure inclinstall
        if test -d $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Headers; then \
                echo "Removing old framework headers"; \
                rm -rf $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Headers; \
@@ -2847,6 +2883,10 @@ clean-retain-profile: pycremoval
        -find build -type f -a ! -name '*.gc??' -exec rm -f {} ';'
        -rm -f Include/pydtrace_probes.h
        -rm -f profile-gen-stamp
+       -rm -rf iOS/testbed/Python.xcframework/ios-*/bin
+       -rm -rf iOS/testbed/Python.xcframework/ios-*/lib
+       -rm -rf iOS/testbed/Python.xcframework/ios-*/include
+       -rm -rf iOS/testbed/Python.xcframework/ios-*/Python.framework
 
 .PHONY: profile-removal
 profile-removal:
@@ -2872,6 +2912,8 @@ clobber: clean
                config.cache config.log pyconfig.h Modules/config.c
        -rm -rf build platform
        -rm -rf $(PYTHONFRAMEWORKDIR)
+       -rm -rf iOS/Frameworks
+       -rm -rf iOSTestbed.*
        -rm -f python-config.py python-config
        -rm -rf cross-build
 
diff --git 
a/Misc/NEWS.d/next/Build/2024-02-26-13-13-53.gh-issue-114099.8lpX-7.rst 
b/Misc/NEWS.d/next/Build/2024-02-26-13-13-53.gh-issue-114099.8lpX-7.rst
new file mode 100644
index 00000000000000..12b6b39a0a2300
--- /dev/null
+++ b/Misc/NEWS.d/next/Build/2024-02-26-13-13-53.gh-issue-114099.8lpX-7.rst
@@ -0,0 +1 @@
+A testbed project was added to run the test suite on iOS.
diff --git a/Modules/_testexternalinspection.c 
b/Modules/_testexternalinspection.c
index 4929a7bf5a984e..bd77f0cd0f1fc7 100644
--- a/Modules/_testexternalinspection.c
+++ b/Modules/_testexternalinspection.c
@@ -15,7 +15,9 @@
 #    include <sys/mman.h>
 #endif
 
-#ifdef __APPLE__
+#if defined(__APPLE__)
+#  include <TargetConditionals.h>
+#  if TARGET_OS_OSX
 #    include <libproc.h>
 #    include <mach-o/fat.h>
 #    include <mach-o/loader.h>
@@ -26,6 +28,7 @@
 #    include <sys/mman.h>
 #    include <sys/proc.h>
 #    include <sys/sysctl.h>
+#  endif
 #endif
 
 #include <errno.h>
@@ -50,7 +53,7 @@
 #    define HAVE_PROCESS_VM_READV 0
 #endif
 
-#ifdef __APPLE__
+#if defined(__APPLE__) && TARGET_OS_OSX
 static void*
 analyze_macho64(mach_port_t proc_ref, void* base, void* map)
 {
@@ -373,7 +376,7 @@ read_memory(pid_t pid, void* remote_address, size_t len, 
void* dst)
         result += read;
     } while ((size_t)read != local[0].iov_len);
     total_bytes_read = result;
-#elif defined(__APPLE__)
+#elif defined(__APPLE__) && TARGET_OS_OSX
     ssize_t result = -1;
     kern_return_t kr = mach_vm_read_overwrite(
             pid_to_task(pid),
@@ -429,7 +432,7 @@ get_py_runtime(pid_t pid)
 {
 #if defined(__linux__)
     return get_py_runtime_linux(pid);
-#elif defined(__APPLE__)
+#elif defined(__APPLE__) && TARGET_OS_OSX
     return get_py_runtime_macos(pid);
 #else
     return NULL;
diff --git a/configure b/configure
index c758749cd371ed..e62cb2baa29a78 100755
--- a/configure
+++ b/configure
@@ -4085,6 +4085,52 @@ fi
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: \"$MACHDEP\"" >&5
 printf "%s\n" "\"$MACHDEP\"" >&6; }
 
+# 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-ios12.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
+# these tools; the binary names of these scripts are better defaults than 
"gcc".
+# This only requires that the user put the platform scripts folder (e.g.,
+# "iOS/Resources/bin") in their path, rather than defining platform-specific
+# names/paths for AR, CC, CPP, and CXX explicitly; and if the user forgets to
+# either put the platform scripts folder in the path, or specify CC etc,
+# configure will fail.
+if test -z "$AR"; then
+       case "$host" in
+               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 
;;
+               *)
+       esac
+fi
+if test -z "$CC"; then
+       case "$host" in
+               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 ;;
+               *)
+       esac
+fi
+if test -z "$CPP"; then
+       case "$host" in
+               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 ;;
+               *)
+       esac
+fi
+if test -z "$CXX"; then
+       case "$host" in
+               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 ;;
+               *)
+       esac
+fi
+
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for 
--enable-universalsdk" >&5
 printf %s "checking for --enable-universalsdk... " >&6; }
 # Check whether --enable-universalsdk was given.
@@ -4200,37 +4246,38 @@ then :
   enableval=$enable_framework;
        case $enableval in
        yes)
-               if test "$ac_sys_system" = "iOS"; then
-                       as_fn_error $? "iOS builds must provide an explicit 
path for --enable-framework" "$LINENO" 5
-               fi
-
-               enableval=/Library/Frameworks
+               case $ac_sys_system in
+                       Darwin) enableval=/Library/Frameworks ;;
+                       iOS)    enableval=iOS/Frameworks/\$\(MULTIARCH\) ;;
+                       *) as_fn_error $? "Unknown platform for framework 
build" "$LINENO" 5
+               esac
        esac
+
        case $enableval in
        no)
-               if test "$ac_sys_system" = "iOS"; then
-                       as_fn_error $? "iOS builds must use 
--enable-framework=<install path>" "$LINENO" 5
-               fi
+               case $ac_sys_system in
+                       iOS) as_fn_error $? "iOS builds must use 
--enable-framework" "$LINENO" 5 ;;
+                       *)
+                               PYTHONFRAMEWORK=
+                               PYTHONFRAMEWORKDIR=no-framework
+                               PYTHONFRAMEWORKPREFIX=
+                               PYTHONFRAMEWORKINSTALLDIR=
+                               PYTHONFRAMEWORKINSTALLNAMEPREFIX=
+                               RESSRCDIR=
+                               FRAMEWORKINSTALLFIRST=
+                               FRAMEWORKINSTALLLAST=
+                               FRAMEWORKALTINSTALLFIRST=
+                               FRAMEWORKALTINSTALLLAST=
+                               FRAMEWORKPYTHONW=
+                               INSTALLTARGETS="commoninstall bininstall 
maninstall"
 
-               PYTHONFRAMEWORK=
-               PYTHONFRAMEWORKDIR=no-framework
-               PYTHONFRAMEWORKPREFIX=
-               PYTHONFRAMEWORKINSTALLDIR=
-               PYTHONFRAMEWORKINSTALLNAMEPREFIX=
-               RESSRCDIR=
-               FRAMEWORKINSTALLFIRST=
-               FRAMEWORKINSTALLLAST=
-               FRAMEWORKALTINSTALLFIRST=
-               FRAMEWORKALTINSTALLLAST=
-               FRAMEWORKPYTHONW=
-               INSTALLTARGETS="commoninstall bininstall maninstall"
-
-               if test "x${prefix}" = "xNONE"; then
-                       FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}"
-               else
-                       FRAMEWORKUNIXTOOLSPREFIX="${prefix}"
-               fi
-               enable_framework=
+                               if test "x${prefix}" = "xNONE"; then
+                                       
FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}"
+                               else
+                                       FRAMEWORKUNIXTOOLSPREFIX="${prefix}"
+                               fi
+                               enable_framework=
+               esac
                ;;
        *)
                PYTHONFRAMEWORKPREFIX="${enableval}"
@@ -4325,29 +4372,28 @@ then :
 
 else $as_nop
 
-       if test "$ac_sys_system" = "iOS"; then
-               as_fn_error $? "iOS builds must use --enable-framework=<install 
path>" "$LINENO" 5
-       fi
-
-       PYTHONFRAMEWORK=
-       PYTHONFRAMEWORKDIR=no-framework
-       PYTHONFRAMEWORKPREFIX=
-       PYTHONFRAMEWORKINSTALLDIR=
-       PYTHONFRAMEWORKINSTALLNAMEPREFIX=
-       RESSRCDIR=
-       FRAMEWORKINSTALLFIRST=
-       FRAMEWORKINSTALLLAST=
-       FRAMEWORKALTINSTALLFIRST=
-       FRAMEWORKALTINSTALLLAST=
-       FRAMEWORKPYTHONW=
-       INSTALLTARGETS="commoninstall bininstall maninstall"
-       if test "x${prefix}" = "xNONE" ; then
-               FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}"
-       else
-               FRAMEWORKUNIXTOOLSPREFIX="${prefix}"
-       fi
-       enable_framework=
-
+       case $ac_sys_system in
+               iOS) as_fn_error $? "iOS builds must use --enable-framework" 
"$LINENO" 5 ;;
+               *)
+                       PYTHONFRAMEWORK=
+                       PYTHONFRAMEWORKDIR=no-framework
+                       PYTHONFRAMEWORKPREFIX=
+                       PYTHONFRAMEWORKINSTALLDIR=
+                       PYTHONFRAMEWORKINSTALLNAMEPREFIX=
+                       RESSRCDIR=
+                       FRAMEWORKINSTALLFIRST=
+                       FRAMEWORKINSTALLLAST=
+                       FRAMEWORKALTINSTALLFIRST=
+                       FRAMEWORKALTINSTALLLAST=
+                       FRAMEWORKPYTHONW=
+                       INSTALLTARGETS="commoninstall bininstall maninstall"
+                       if test "x${prefix}" = "xNONE" ; then
+                               FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}"
+                       else
+                               FRAMEWORKUNIXTOOLSPREFIX="${prefix}"
+                       fi
+                       enable_framework=
+       esac
 
 fi
 
@@ -12773,8 +12819,8 @@ then
                fi
                ;;
        iOS/*)
-               LDSHARED='$(CC) -dynamiclib -F . -framework Python'
-               LDCXXSHARED='$(CXX) -dynamiclib -F . -framework Python'
+               LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)'
+               LDCXXSHARED='$(CXX) -dynamiclib -F . -framework 
$(PYTHONFRAMEWORK)'
                BLDSHARED="$LDSHARED"
                ;;
        Emscripten|WASI)
diff --git a/configure.ac b/configure.ac
index 80d0a7edc7cad4..990215b613aef4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -378,6 +378,52 @@ then
 fi
 AC_MSG_RESULT(["$MACHDEP"])
 
+# 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-ios12.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
+# these tools; the binary names of these scripts are better defaults than 
"gcc".
+# This only requires that the user put the platform scripts folder (e.g.,
+# "iOS/Resources/bin") in their path, rather than defining platform-specific
+# names/paths for AR, CC, CPP, and CXX explicitly; and if the user forgets to
+# either put the platform scripts folder in the path, or specify CC etc,
+# configure will fail.
+if test -z "$AR"; then
+       case "$host" in
+               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 
;;
+               *)
+       esac
+fi
+if test -z "$CC"; then
+       case "$host" in
+               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 ;;
+               *)
+       esac
+fi
+if test -z "$CPP"; then
+       case "$host" in
+               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 ;;
+               *)
+       esac
+fi
+if test -z "$CXX"; then
+       case "$host" in
+               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 ;;
+               *)
+       esac
+fi
+
 AC_MSG_CHECKING([for --enable-universalsdk])
 AC_ARG_ENABLE([universalsdk],
        AS_HELP_STRING([--enable-universalsdk@<:@=SDKDIR@:>@],
@@ -487,37 +533,38 @@ AC_ARG_ENABLE([framework],
 [
        case $enableval in
        yes)
-               if test "$ac_sys_system" = "iOS"; then
-                       AC_MSG_ERROR([iOS builds must provide an explicit path 
for --enable-framework])
-               fi
-
-               enableval=/Library/Frameworks
+               case $ac_sys_system in
+                       Darwin) enableval=/Library/Frameworks ;;
+                       iOS)    enableval=iOS/Frameworks/\$\(MULTIARCH\) ;;
+                       *) AC_MSG_ERROR([Unknown platform for framework build])
+               esac
        esac
+
        case $enableval in
        no)
-               if test "$ac_sys_system" = "iOS"; then
-                       AC_MSG_ERROR([iOS builds must use 
--enable-framework=<install path>])
-               fi
+               case $ac_sys_system in
+                       iOS) AC_MSG_ERROR([iOS builds must use 
--enable-framework]) ;;
+                       *)
+                               PYTHONFRAMEWORK=
+                               PYTHONFRAMEWORKDIR=no-framework
+                               PYTHONFRAMEWORKPREFIX=
+                               PYTHONFRAMEWORKINSTALLDIR=
+                               PYTHONFRAMEWORKINSTALLNAMEPREFIX=
+                               RESSRCDIR=
+                               FRAMEWORKINSTALLFIRST=
+                               FRAMEWORKINSTALLLAST=
+                               FRAMEWORKALTINSTALLFIRST=
+                               FRAMEWORKALTINSTALLLAST=
+                               FRAMEWORKPYTHONW=
+                               INSTALLTARGETS="commoninstall bininstall 
maninstall"
 
-               PYTHONFRAMEWORK=
-               PYTHONFRAMEWORKDIR=no-framework
-               PYTHONFRAMEWORKPREFIX=
-               PYTHONFRAMEWORKINSTALLDIR=
-               PYTHONFRAMEWORKINSTALLNAMEPREFIX=
-               RESSRCDIR=
-               FRAMEWORKINSTALLFIRST=
-               FRAMEWORKINSTALLLAST=
-               FRAMEWORKALTINSTALLFIRST=
-               FRAMEWORKALTINSTALLLAST=
-               FRAMEWORKPYTHONW=
-               INSTALLTARGETS="commoninstall bininstall maninstall"
-
-               if test "x${prefix}" = "xNONE"; then
-                       FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}"
-               else
-                       FRAMEWORKUNIXTOOLSPREFIX="${prefix}"
-               fi
-               enable_framework=
+                               if test "x${prefix}" = "xNONE"; then
+                                       
FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}"
+                               else
+                                       FRAMEWORKUNIXTOOLSPREFIX="${prefix}"
+                               fi
+                               enable_framework=
+               esac
                ;;
        *)
                PYTHONFRAMEWORKPREFIX="${enableval}"
@@ -605,29 +652,28 @@ AC_ARG_ENABLE([framework],
                        esac
                esac
        ],[
-       if test "$ac_sys_system" = "iOS"; then
-               AC_MSG_ERROR([iOS builds must use --enable-framework=<install 
path>])
-       fi
-
-       PYTHONFRAMEWORK=
-       PYTHONFRAMEWORKDIR=no-framework
-       PYTHONFRAMEWORKPREFIX=
-       PYTHONFRAMEWORKINSTALLDIR=
-       PYTHONFRAMEWORKINSTALLNAMEPREFIX=
-       RESSRCDIR=
-       FRAMEWORKINSTALLFIRST=
-       FRAMEWORKINSTALLLAST=
-       FRAMEWORKALTINSTALLFIRST=
-       FRAMEWORKALTINSTALLLAST=
-       FRAMEWORKPYTHONW=
-       INSTALLTARGETS="commoninstall bininstall maninstall"
-       if test "x${prefix}" = "xNONE" ; then
-               FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}"
-       else
-               FRAMEWORKUNIXTOOLSPREFIX="${prefix}"
-       fi
-       enable_framework=
-
+       case $ac_sys_system in
+               iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;;
+               *)
+                       PYTHONFRAMEWORK=
+                       PYTHONFRAMEWORKDIR=no-framework
+                       PYTHONFRAMEWORKPREFIX=
+                       PYTHONFRAMEWORKINSTALLDIR=
+                       PYTHONFRAMEWORKINSTALLNAMEPREFIX=
+                       RESSRCDIR=
+                       FRAMEWORKINSTALLFIRST=
+                       FRAMEWORKINSTALLLAST=
+                       FRAMEWORKALTINSTALLFIRST=
+                       FRAMEWORKALTINSTALLLAST=
+                       FRAMEWORKPYTHONW=
+                       INSTALLTARGETS="commoninstall bininstall maninstall"
+                       if test "x${prefix}" = "xNONE" ; then
+                               FRAMEWORKUNIXTOOLSPREFIX="${ac_default_prefix}"
+                       else
+                               FRAMEWORKUNIXTOOLSPREFIX="${prefix}"
+                       fi
+                       enable_framework=
+       esac
 ])
 AC_SUBST([PYTHONFRAMEWORK])
 AC_SUBST([PYTHONFRAMEWORKIDENTIFIER])
@@ -3320,8 +3366,8 @@ then
                fi
                ;;
        iOS/*)
-               LDSHARED='$(CC) -dynamiclib -F . -framework Python'
-               LDCXXSHARED='$(CXX) -dynamiclib -F . -framework Python'
+               LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)'
+               LDCXXSHARED='$(CXX) -dynamiclib -F . -framework 
$(PYTHONFRAMEWORK)'
                BLDSHARED="$LDSHARED"
                ;;
        Emscripten|WASI)
diff --git a/iOS/README.rst b/iOS/README.rst
index 1043b5ecebbc0c..b67199e66f95b3 100644
--- a/iOS/README.rst
+++ b/iOS/README.rst
@@ -33,16 +33,27 @@ Platform by selecting an open the Platforms tab of the 
Xcode Settings panel.
 iOS specific arguments to configure
 ===================================
 
-* ``--enable-framework=DIR``
+* ``--enable-framework[=DIR]``
 
   This argument specifies the location where the Python.framework will be
-  installed. This argument is required for all iOS builds; a directory *must*
-  be specified.
+  installed. If ``DIR`` is not specified, the framework will be installed into
+  a subdirectory of the ``iOS/Frameworks`` folder.
+
+  This argument *must* be provided when configuring iOS builds. iOS does not
+  support non-framework builds.
 
 * ``--with-framework-name=NAME``
 
   Specify the name for the Python framework; defaults to ``Python``.
 
+  .. admonition:: Use this option with care!
+
+    Unless you know what you're doing, changing the name of the Python
+    framework on iOS is not advised. If you use this option, you won't be able
+    to run the ``make testios`` target without making signficant manual
+    alterations, and you won't be able to use any binary packages unless you
+    compile them yourself using your own framework name.
+
 Building Python on iOS
 ======================
 
@@ -85,13 +96,9 @@ provide the ``--enable-framework`` flag when configuring the 
build. The build
 also requires the use of cross-compilation. The minimal commands for building
 Python for the ARM64 iOS simulator will look something like::
 
-  $ export 
PATH="`pwd`/iOS/Resources/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin"
+  $ export 
PATH="$(pwd)/iOS/Resources/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin"
   $ ./configure \
-        AR=arm64-apple-ios-simulator-ar \
-        CC=arm64-apple-ios-simulator-clang \
-        CPP=arm64-apple-ios-simulator-cpp \
-        CXX=arm64-apple-ios-simulator-clang \
-        --enable-framework=/path/to/install \
+        --enable-framework \
         --host=arm64-apple-ios-simulator \
         --build=arm64-apple-darwin \
         --with-build-python=/path/to/python.exe
@@ -132,9 +139,6 @@ In this invocation:
   build. Resetting the path to a known "bare bones" value is the easiest way to
   avoid these problems.
 
-* ``/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:
 
@@ -161,6 +165,17 @@ In this invocation:
   release has been stable, the more likely it is that this constraint can be
   relaxed - the same micro version will often be sufficient.
 
+* The ``install`` target for iOS builds is slightly different to other
+  platforms. On most platforms, ``make install`` will install the build into
+  the final runtime location. This won't be the case for iOS, as the final
+  runtime location will be on a physical device.
+
+  However, you still need to run the ``install`` target for iOS builds, as it
+  performs some final framework assembly steps. The location specified with
+  ``--enable-framework`` will be the location where ``make install`` will
+  assemble the complete iOS framework. This completed framework can then
+  be copied and relocated as required.
+
 For a full CPython build, you also need to specify the paths to iOS builds of
 the binary libraries that CPython depends on (XZ, BZip2, LibFFI and OpenSSL).
 This can be done by defining the ``LIBLZMA_CFLAGS``, ``LIBLZMA_LIBS``,
@@ -256,13 +271,21 @@ suite passes.
 
 To run the test suite, configure a Python build for an iOS simulator (i.e.,
 ``--host=arm64-apple-ios-simulator`` or ``--host=x86_64-apple-ios-simulator``
-), setting the framework location to the testbed project::
+), specifying a framework build (i.e. ``--enable-framework``). Ensure that your
+``PATH`` has been configured to include the ``iOS/Resources/bin`` folder and
+exclude any non-iOS tools, then run::
+
+    $ make all
+    $ make install
+    $ make testios
 
-    
--enable-framework="./iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator"
+This will:
 
-Then run ``make all install testiOS``. This will build an iOS framework for 
your
-chosen architecture, install the Python iOS framework into the testbed project,
-and run the test suite on an "iPhone SE (3rd generation)" simulator.
+* Build an iOS framework for your chosen architecture;
+* Finalize the single-platform framework;
+* Make a clean copy of the testbed project;
+* Install the Python iOS framework into the copy of the testbed project; and
+* Run the test suite on an "iPhone SE (3rd generation)" simulator.
 
 While the test suite is running, Xcode does not display any console output.
 After showing some Xcode build commands, the console output will print 
``Testing
diff --git a/iOS/testbed/Python.xcframework/Info.plist 
b/iOS/testbed/Python.xcframework/Info.plist
new file mode 100644
index 00000000000000..c6418de6e74a4e
--- /dev/null
+++ b/iOS/testbed/Python.xcframework/Info.plist
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" 
"http://www.apple.com/DTDs/PropertyList-1.0.dtd";>
+<plist version="1.0">
+<dict>
+       <key>AvailableLibraries</key>
+       <array>
+               <dict>
+                       <key>BinaryPath</key>
+                       <string>Python.framework/Python</string>
+                       <key>LibraryIdentifier</key>
+                       <string>ios-arm64</string>
+                       <key>LibraryPath</key>
+                       <string>Python.framework</string>
+                       <key>SupportedArchitectures</key>
+                       <array>
+                               <string>arm64</string>
+                       </array>
+                       <key>SupportedPlatform</key>
+                       <string>ios</string>
+               </dict>
+               <dict>
+                       <key>BinaryPath</key>
+                       <string>Python.framework/Python</string>
+                       <key>LibraryIdentifier</key>
+                       <string>ios-arm64_x86_64-simulator</string>
+                       <key>LibraryPath</key>
+                       <string>Python.framework</string>
+                       <key>SupportedArchitectures</key>
+                       <array>
+                               <string>arm64</string>
+                               <string>x86_64</string>
+                       </array>
+                       <key>SupportedPlatform</key>
+                       <string>ios</string>
+                       <key>SupportedPlatformVariant</key>
+                       <string>simulator</string>
+               </dict>
+       </array>
+       <key>CFBundlePackageType</key>
+       <string>XFWK</string>
+       <key>XCFrameworkFormatVersion</key>
+       <string>1.0</string>
+</dict>
+</plist>
diff --git a/iOS/testbed/Python.xcframework/ios-arm64/README 
b/iOS/testbed/Python.xcframework/ios-arm64/README
new file mode 100644
index 00000000000000..c1b076d12cddb7
--- /dev/null
+++ b/iOS/testbed/Python.xcframework/ios-arm64/README
@@ -0,0 +1,4 @@
+This directory is intentionally empty.
+
+It should be used as a target for `--enable-framework` when compiling an iOS 
on-device
+build for testing purposes.
diff --git a/iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator/README 
b/iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator/README
new file mode 100644
index 00000000000000..ae334e5d769d9d
--- /dev/null
+++ b/iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator/README
@@ -0,0 +1,4 @@
+This directory is intentionally empty.
+
+It should be used as a target for `--enable-framework` when compiling an iOS 
simulator
+build for testing purposes (either x86_64 or ARM64).
diff --git a/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj 
b/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj
new file mode 100644
index 00000000000000..4f138a4e7ccefd
--- /dev/null
+++ b/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj
@@ -0,0 +1,570 @@
+// !$*UTF8*$!
+{
+       archiveVersion = 1;
+       classes = {
+       };
+       objectVersion = 56;
+       objects = {
+
+/* Begin PBXBuildFile section */
+               607A66172B0EFA380010BFC8 /* AppDelegate.m in Sources */ = {isa 
= PBXBuildFile; fileRef = 607A66162B0EFA380010BFC8 /* AppDelegate.m */; };
+               607A66222B0EFA390010BFC8 /* Assets.xcassets in Resources */ = 
{isa = PBXBuildFile; fileRef = 607A66212B0EFA390010BFC8 /* Assets.xcassets */; 
};
+               607A66252B0EFA390010BFC8 /* LaunchScreen.storyboard in 
Resources */ = {isa = PBXBuildFile; fileRef = 607A66232B0EFA390010BFC8 /* 
LaunchScreen.storyboard */; };
+               607A66282B0EFA390010BFC8 /* main.m in Sources */ = {isa = 
PBXBuildFile; fileRef = 607A66272B0EFA390010BFC8 /* main.m */; };
+               607A66322B0EFA3A0010BFC8 /* iOSTestbedTests.m in Sources */ = 
{isa = PBXBuildFile; fileRef = 607A66312B0EFA3A0010BFC8 /* iOSTestbedTests.m 
*/; };
+               607A664C2B0EFC080010BFC8 /* Python.xcframework in Frameworks */ 
= {isa = PBXBuildFile; fileRef = 607A664A2B0EFB310010BFC8 /* Python.xcframework 
*/; };
+               607A664D2B0EFC080010BFC8 /* Python.xcframework in Embed 
Frameworks */ = {isa = PBXBuildFile; fileRef = 607A664A2B0EFB310010BFC8 /* 
Python.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, 
RemoveHeadersOnCopy, ); }; };
+               607A66502B0EFFE00010BFC8 /* Python.xcframework in Frameworks */ 
= {isa = PBXBuildFile; fileRef = 607A664A2B0EFB310010BFC8 /* Python.xcframework 
*/; };
+               607A66512B0EFFE00010BFC8 /* Python.xcframework in Embed 
Frameworks */ = {isa = PBXBuildFile; fileRef = 607A664A2B0EFB310010BFC8 /* 
Python.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, 
RemoveHeadersOnCopy, ); }; };
+               607A66582B0F079F0010BFC8 /* dylib-Info-template.plist in 
Resources */ = {isa = PBXBuildFile; fileRef = 607A66572B0F079F0010BFC8 /* 
dylib-Info-template.plist */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+               607A662E2B0EFA3A0010BFC8 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 607A660A2B0EFA380010BFC8 /* Project 
object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 607A66112B0EFA380010BFC8;
+                       remoteInfo = iOSTestbed;
+               };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+               607A664E2B0EFC080010BFC8 /* Embed Frameworks */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 2147483647;
+                       dstPath = "";
+                       dstSubfolderSpec = 10;
+                       files = (
+                               607A664D2B0EFC080010BFC8 /* Python.xcframework 
in Embed Frameworks */,
+                       );
+                       name = "Embed Frameworks";
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               607A66522B0EFFE00010BFC8 /* Embed Frameworks */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 2147483647;
+                       dstPath = "";
+                       dstSubfolderSpec = 10;
+                       files = (
+                               607A66512B0EFFE00010BFC8 /* Python.xcframework 
in Embed Frameworks */,
+                       );
+                       name = "Embed Frameworks";
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+               607A66122B0EFA380010BFC8 /* iOSTestbed.app */ = {isa = 
PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; 
path = iOSTestbed.app; sourceTree = BUILT_PRODUCTS_DIR; };
+               607A66152B0EFA380010BFC8 /* AppDelegate.h */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; 
sourceTree = "<group>"; };
+               607A66162B0EFA380010BFC8 /* AppDelegate.m */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; 
sourceTree = "<group>"; };
+               607A66212B0EFA390010BFC8 /* Assets.xcassets */ = {isa = 
PBXFileReference; lastKnownFileType = folder.assetcatalog; path = 
Assets.xcassets; sourceTree = "<group>"; };
+               607A66242B0EFA390010BFC8 /* Base */ = {isa = PBXFileReference; 
lastKnownFileType = file.storyboard; name = Base; path = 
Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+               607A66272B0EFA390010BFC8 /* main.m */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; 
sourceTree = "<group>"; };
+               607A662D2B0EFA3A0010BFC8 /* iOSTestbedTests.xctest */ = {isa = 
PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path 
= iOSTestbedTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+               607A66312B0EFA3A0010BFC8 /* iOSTestbedTests.m */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = 
iOSTestbedTests.m; sourceTree = "<group>"; };
+               607A664A2B0EFB310010BFC8 /* Python.xcframework */ = {isa = 
PBXFileReference; lastKnownFileType = wrapper.xcframework; path = 
Python.xcframework; sourceTree = "<group>"; };
+               607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */ = {isa 
= PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path 
= "dylib-Info-template.plist"; sourceTree = "<group>"; };
+               607A66592B0F08600010BFC8 /* iOSTestbed-Info.plist */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = 
"iOSTestbed-Info.plist"; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+               607A660F2B0EFA380010BFC8 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               607A664C2B0EFC080010BFC8 /* Python.xcframework 
in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               607A662A2B0EFA3A0010BFC8 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               607A66502B0EFFE00010BFC8 /* Python.xcframework 
in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+               607A66092B0EFA380010BFC8 = {
+                       isa = PBXGroup;
+                       children = (
+                               607A664A2B0EFB310010BFC8 /* Python.xcframework 
*/,
+                               607A66142B0EFA380010BFC8 /* iOSTestbed */,
+                               607A66302B0EFA3A0010BFC8 /* iOSTestbedTests */,
+                               607A66132B0EFA380010BFC8 /* Products */,
+                               607A664F2B0EFFE00010BFC8 /* Frameworks */,
+                       );
+                       sourceTree = "<group>";
+               };
+               607A66132B0EFA380010BFC8 /* Products */ = {
+                       isa = PBXGroup;
+                       children = (
+                               607A66122B0EFA380010BFC8 /* iOSTestbed.app */,
+                               607A662D2B0EFA3A0010BFC8 /* 
iOSTestbedTests.xctest */,
+                       );
+                       name = Products;
+                       sourceTree = "<group>";
+               };
+               607A66142B0EFA380010BFC8 /* iOSTestbed */ = {
+                       isa = PBXGroup;
+                       children = (
+                               607A66592B0F08600010BFC8 /* 
iOSTestbed-Info.plist */,
+                               607A66572B0F079F0010BFC8 /* 
dylib-Info-template.plist */,
+                               607A66152B0EFA380010BFC8 /* AppDelegate.h */,
+                               607A66162B0EFA380010BFC8 /* AppDelegate.m */,
+                               607A66212B0EFA390010BFC8 /* Assets.xcassets */,
+                               607A66232B0EFA390010BFC8 /* 
LaunchScreen.storyboard */,
+                               607A66272B0EFA390010BFC8 /* main.m */,
+                       );
+                       path = iOSTestbed;
+                       sourceTree = "<group>";
+               };
+               607A66302B0EFA3A0010BFC8 /* iOSTestbedTests */ = {
+                       isa = PBXGroup;
+                       children = (
+                               607A66312B0EFA3A0010BFC8 /* iOSTestbedTests.m 
*/,
+                       );
+                       path = iOSTestbedTests;
+                       sourceTree = "<group>";
+               };
+               607A664F2B0EFFE00010BFC8 /* Frameworks */ = {
+                       isa = PBXGroup;
+                       children = (
+                       );
+                       name = Frameworks;
+                       sourceTree = "<group>";
+               };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+               607A66112B0EFA380010BFC8 /* iOSTestbed */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 607A66412B0EFA3A0010BFC8 /* 
Build configuration list for PBXNativeTarget "iOSTestbed" */;
+                       buildPhases = (
+                               607A660E2B0EFA380010BFC8 /* Sources */,
+                               607A660F2B0EFA380010BFC8 /* Frameworks */,
+                               607A66102B0EFA380010BFC8 /* Resources */,
+                               607A66552B0F061D0010BFC8 /* Install Target 
Specific Python Standard Library */,
+                               607A66562B0F06200010BFC8 /* Prepare Python 
Binary Modules */,
+                               607A664E2B0EFC080010BFC8 /* Embed Frameworks */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = iOSTestbed;
+                       productName = iOSTestbed;
+                       productReference = 607A66122B0EFA380010BFC8 /* 
iOSTestbed.app */;
+                       productType = "com.apple.product-type.application";
+               };
+               607A662C2B0EFA3A0010BFC8 /* iOSTestbedTests */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 607A66442B0EFA3A0010BFC8 /* 
Build configuration list for PBXNativeTarget "iOSTestbedTests" */;
+                       buildPhases = (
+                               607A66292B0EFA3A0010BFC8 /* Sources */,
+                               607A662A2B0EFA3A0010BFC8 /* Frameworks */,
+                               607A662B2B0EFA3A0010BFC8 /* Resources */,
+                               607A66522B0EFFE00010BFC8 /* Embed Frameworks */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                               607A662F2B0EFA3A0010BFC8 /* PBXTargetDependency 
*/,
+                       );
+                       name = iOSTestbedTests;
+                       productName = iOSTestbedTests;
+                       productReference = 607A662D2B0EFA3A0010BFC8 /* 
iOSTestbedTests.xctest */;
+                       productType = "com.apple.product-type.bundle.unit-test";
+               };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+               607A660A2B0EFA380010BFC8 /* Project object */ = {
+                       isa = PBXProject;
+                       attributes = {
+                               BuildIndependentTargetsInParallel = 1;
+                               LastUpgradeCheck = 1500;
+                               TargetAttributes = {
+                                       607A66112B0EFA380010BFC8 = {
+                                               CreatedOnToolsVersion = 15.0.1;
+                                       };
+                                       607A662C2B0EFA3A0010BFC8 = {
+                                               CreatedOnToolsVersion = 15.0.1;
+                                               TestTargetID = 
607A66112B0EFA380010BFC8;
+                                       };
+                               };
+                       };
+                       buildConfigurationList = 607A660D2B0EFA380010BFC8 /* 
Build configuration list for PBXProject "iOSTestbed" */;
+                       compatibilityVersion = "Xcode 14.0";
+                       developmentRegion = en;
+                       hasScannedForEncodings = 0;
+                       knownRegions = (
+                               en,
+                               Base,
+                       );
+                       mainGroup = 607A66092B0EFA380010BFC8;
+                       productRefGroup = 607A66132B0EFA380010BFC8 /* Products 
*/;
+                       projectDirPath = "";
+                       projectRoot = "";
+                       targets = (
+                               607A66112B0EFA380010BFC8 /* iOSTestbed */,
+                               607A662C2B0EFA3A0010BFC8 /* iOSTestbedTests */,
+                       );
+               };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+               607A66102B0EFA380010BFC8 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               607A66252B0EFA390010BFC8 /* 
LaunchScreen.storyboard in Resources */,
+                               607A66582B0F079F0010BFC8 /* 
dylib-Info-template.plist in Resources */,
+                               607A66222B0EFA390010BFC8 /* Assets.xcassets in 
Resources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               607A662B2B0EFA3A0010BFC8 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+               607A66552B0F061D0010BFC8 /* Install Target Specific Python 
Standard Library */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       alwaysOutOfDate = 1;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       inputFileListPaths = (
+                       );
+                       inputPaths = (
+                       );
+                       name = "Install Target Specific Python Standard 
Library";
+                       outputFileListPaths = (
+                       );
+                       outputPaths = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+                       shellPath = /bin/sh;
+                       shellScript = "set -e\n\nmkdir -p 
\"$CODESIGNING_FOLDER_PATH/python/lib\"\nif [ \"$EFFECTIVE_PLATFORM_NAME\" = 
\"-iphonesimulator\" ]; then\n    echo \"Installing Python modules for iOS 
Simulator\"\n    rsync -au --delete 
\"$PROJECT_DIR/Python.xcframework/ios-arm64_x86_64-simulator/lib/\" 
\"$CODESIGNING_FOLDER_PATH/python/lib/\" \nelse\n    echo \"Installing Python 
modules for iOS Device\"\n    rsync -au --delete 
\"$PROJECT_DIR/Python.xcframework/ios-arm64/lib/\" 
\"$CODESIGNING_FOLDER_PATH/python/lib/\" \nfi\n";
+               };
+               607A66562B0F06200010BFC8 /* Prepare Python Binary Modules */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       alwaysOutOfDate = 1;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       inputFileListPaths = (
+                       );
+                       inputPaths = (
+                       );
+                       name = "Prepare Python Binary Modules";
+                       outputFileListPaths = (
+                       );
+                       outputPaths = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+                       shellPath = /bin/sh;
+                       shellScript = "set -e\n\ninstall_dylib () {\n    
INSTALL_BASE=$1\n    FULL_DYLIB=$2\n\n    # The name of the .dylib file\n    
DYLIB=$(basename \"$FULL_DYLIB\")\n    # The name of the .dylib file, relative 
to the install base\n    
RELATIVE_DYLIB=${FULL_DYLIB#$CODESIGNING_FOLDER_PATH/$INSTALL_BASE/}\n    # The 
full dotted name of the binary module, constructed from the file path.\n    
FULL_MODULE_NAME=$(echo $RELATIVE_DYLIB | cut -d \".\" -f 1 | tr \"/\" \".\"); 
\n    # A bundle identifier; not actually used, but required by Xcode framework 
packaging\n    FRAMEWORK_BUNDLE_ID=$(echo 
$PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr \"_\" \"-\")\n    # The name 
of the framework folder.\n    
FRAMEWORK_FOLDER=\"Frameworks/$FULL_MODULE_NAME.framework\"\n\n    # If the 
framework folder doesn't exist, create it.\n    if [ ! -d 
\"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\" ]; then\n        echo \"Creating 
framework for $RELATIVE_DYLIB\" \n        mkdir -p 
\"$CODESIGNING_FOLDER_PATH/$FRAME
 WORK_FOLDER\"\n        cp 
\"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\" 
\"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n        plutil 
-replace CFBundleExecutable -string \"$DYLIB\" 
\"$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 $RELATIVE_DYLIB\" \n    mv \"$FULL_DYLIB\" 
\"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\"\n}\n\nPYTHON_VER=$(ls -1 
\"$CODESIGNING_FOLDER_PATH/python/lib\")\necho \"Install Python $PYTHON_VER 
standard library dylibs...\"\nfind 
\"$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload\" -name 
\"*.dylib\" | while read FULL_DYLIB; do\n    install_dylib 
python/lib/$PYTHON_VER/lib-dynload \"$FULL_DYLIB\"\ndone\n\n# Clean up dylib 
template \nrm -f \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\"\necho 
\"Signing frameworks as $EXPANDED_CO
 DE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)...\"\nfind 
\"$CODESIGNING_FOLDER_PATH/Frameworks\" -name \"*.framework\" -exec 
/usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" 
${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none 
--preserve-metadata=identifier,entitlements,flags --generate-entitlement-der 
\"{}\" \\; \n";
+               };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+               607A660E2B0EFA380010BFC8 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               607A66172B0EFA380010BFC8 /* AppDelegate.m in 
Sources */,
+                               607A66282B0EFA390010BFC8 /* main.m in Sources 
*/,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               607A66292B0EFA3A0010BFC8 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               607A66322B0EFA3A0010BFC8 /* iOSTestbedTests.m 
in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+               607A662F2B0EFA3A0010BFC8 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = 607A66112B0EFA380010BFC8 /* iOSTestbed */;
+                       targetProxy = 607A662E2B0EFA3A0010BFC8 /* 
PBXContainerItemProxy */;
+               };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+               607A66232B0EFA390010BFC8 /* LaunchScreen.storyboard */ = {
+                       isa = PBXVariantGroup;
+                       children = (
+                               607A66242B0EFA390010BFC8 /* Base */,
+                       );
+                       name = LaunchScreen.storyboard;
+                       sourceTree = "<group>";
+               };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+               607A663F2B0EFA3A0010BFC8 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = 
YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_ENABLE_OBJC_WEAK = YES;
+                               CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+                               CLANG_WARN_BOOL_CONVERSION = YES;
+                               CLANG_WARN_COMMA = YES;
+                               CLANG_WARN_CONSTANT_CONVERSION = YES;
+                               CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = 
YES;
+                               CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+                               CLANG_WARN_EMPTY_BODY = YES;
+                               CLANG_WARN_ENUM_CONVERSION = YES;
+                               CLANG_WARN_INFINITE_RECURSION = YES;
+                               CLANG_WARN_INT_CONVERSION = YES;
+                               CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+                               CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = 
YES;
+                               CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+                               CLANG_WARN_STRICT_PROTOTYPES = YES;
+                               CLANG_WARN_SUSPICIOUS_MOVE = YES;
+                               CLANG_WARN_UNGUARDED_AVAILABILITY = 
YES_AGGRESSIVE;
+                               CLANG_WARN_UNREACHABLE_CODE = YES;
+                               CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               COPY_PHASE_STRIP = NO;
+                               DEBUG_INFORMATION_FORMAT = dwarf;
+                               ENABLE_STRICT_OBJC_MSGSEND = YES;
+                               ENABLE_TESTABILITY = YES;
+                               ENABLE_USER_SCRIPT_SANDBOXING = YES;
+                               GCC_C_LANGUAGE_STANDARD = gnu17;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_NO_COMMON_BLOCKS = YES;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "DEBUG=1",
+                                       "$(inherited)",
+                               );
+                               GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNDECLARED_SELECTOR = YES;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               GCC_WARN_UNUSED_FUNCTION = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
+                               IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+                               LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+                               MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+                               MTL_FAST_MATH = YES;
+                               ONLY_ACTIVE_ARCH = YES;
+                               SDKROOT = iphoneos;
+                       };
+                       name = Debug;
+               };
+               607A66402B0EFA3A0010BFC8 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = 
YES_AGGRESSIVE;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_ENABLE_OBJC_WEAK = YES;
+                               CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+                               CLANG_WARN_BOOL_CONVERSION = YES;
+                               CLANG_WARN_COMMA = YES;
+                               CLANG_WARN_CONSTANT_CONVERSION = YES;
+                               CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = 
YES;
+                               CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+                               CLANG_WARN_EMPTY_BODY = YES;
+                               CLANG_WARN_ENUM_CONVERSION = YES;
+                               CLANG_WARN_INFINITE_RECURSION = YES;
+                               CLANG_WARN_INT_CONVERSION = YES;
+                               CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+                               CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = 
YES;
+                               CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+                               CLANG_WARN_STRICT_PROTOTYPES = YES;
+                               CLANG_WARN_SUSPICIOUS_MOVE = YES;
+                               CLANG_WARN_UNGUARDED_AVAILABILITY = 
YES_AGGRESSIVE;
+                               CLANG_WARN_UNREACHABLE_CODE = YES;
+                               CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               COPY_PHASE_STRIP = NO;
+                               DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+                               ENABLE_NS_ASSERTIONS = NO;
+                               ENABLE_STRICT_OBJC_MSGSEND = YES;
+                               ENABLE_USER_SCRIPT_SANDBOXING = YES;
+                               GCC_C_LANGUAGE_STANDARD = gnu17;
+                               GCC_NO_COMMON_BLOCKS = YES;
+                               GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNDECLARED_SELECTOR = YES;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               GCC_WARN_UNUSED_FUNCTION = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
+                               IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+                               LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+                               MTL_ENABLE_DEBUG_INFO = NO;
+                               MTL_FAST_MATH = YES;
+                               SDKROOT = iphoneos;
+                               VALIDATE_PRODUCT = YES;
+                       };
+                       name = Release;
+               };
+               607A66422B0EFA3A0010BFC8 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+                               ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME 
= AccentColor;
+                               CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = 
NO;
+                               CODE_SIGN_STYLE = Automatic;
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEVELOPMENT_TEAM = 3HEZE76D99;
+                               ENABLE_USER_SCRIPT_SANDBOXING = NO;
+                               HEADER_SEARCH_PATHS = 
"\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\"";
+                               INFOPLIST_FILE = 
"iOSTestbed/iOSTestbed-Info.plist";
+                               
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+                               INFOPLIST_KEY_UILaunchStoryboardName = 
LaunchScreen;
+                               INFOPLIST_KEY_UIMainStoryboardFile = Main;
+                               
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = 
"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown 
UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+                               
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = 
"UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft 
UIInterfaceOrientationLandscapeRight";
+                               IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+                               LD_RUNPATH_SEARCH_PATHS = (
+                                       "$(inherited)",
+                                       "@executable_path/Frameworks",
+                               );
+                               MARKETING_VERSION = 3.13.0a1;
+                               PRODUCT_BUNDLE_IDENTIFIER = 
org.python.iOSTestbed;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SWIFT_EMIT_LOC_STRINGS = YES;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                       };
+                       name = Debug;
+               };
+               607A66432B0EFA3A0010BFC8 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+                               ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME 
= AccentColor;
+                               CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = 
NO;
+                               CODE_SIGN_STYLE = Automatic;
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEVELOPMENT_TEAM = 3HEZE76D99;
+                               ENABLE_TESTABILITY = YES;
+                               ENABLE_USER_SCRIPT_SANDBOXING = NO;
+                               HEADER_SEARCH_PATHS = 
"\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\"";
+                               INFOPLIST_FILE = 
"iOSTestbed/iOSTestbed-Info.plist";
+                               
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+                               INFOPLIST_KEY_UILaunchStoryboardName = 
LaunchScreen;
+                               INFOPLIST_KEY_UIMainStoryboardFile = Main;
+                               
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = 
"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown 
UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+                               
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = 
"UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft 
UIInterfaceOrientationLandscapeRight";
+                               IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+                               LD_RUNPATH_SEARCH_PATHS = (
+                                       "$(inherited)",
+                                       "@executable_path/Frameworks",
+                               );
+                               MARKETING_VERSION = 3.13.0a1;
+                               PRODUCT_BUNDLE_IDENTIFIER = 
org.python.iOSTestbed;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SWIFT_EMIT_LOC_STRINGS = YES;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                       };
+                       name = Release;
+               };
+               607A66452B0EFA3A0010BFC8 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               BUNDLE_LOADER = "$(TEST_HOST)";
+                               CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = 
NO;
+                               CODE_SIGN_STYLE = Automatic;
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEVELOPMENT_TEAM = 3HEZE76D99;
+                               GENERATE_INFOPLIST_FILE = YES;
+                               HEADER_SEARCH_PATHS = 
"\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\"";
+                               IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+                               MARKETING_VERSION = 1.0;
+                               PRODUCT_BUNDLE_IDENTIFIER = 
org.python.iOSTestbedTests;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SWIFT_EMIT_LOC_STRINGS = NO;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               TEST_HOST = 
"$(BUILT_PRODUCTS_DIR)/iOSTestbed.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/iOSTestbed";
+                       };
+                       name = Debug;
+               };
+               607A66462B0EFA3A0010BFC8 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               BUNDLE_LOADER = "$(TEST_HOST)";
+                               CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = 
NO;
+                               CODE_SIGN_STYLE = Automatic;
+                               CURRENT_PROJECT_VERSION = 1;
+                               DEVELOPMENT_TEAM = 3HEZE76D99;
+                               GENERATE_INFOPLIST_FILE = YES;
+                               HEADER_SEARCH_PATHS = 
"\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\"";
+                               IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+                               MARKETING_VERSION = 1.0;
+                               PRODUCT_BUNDLE_IDENTIFIER = 
org.python.iOSTestbedTests;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SWIFT_EMIT_LOC_STRINGS = NO;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               TEST_HOST = 
"$(BUILT_PRODUCTS_DIR)/iOSTestbed.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/iOSTestbed";
+                       };
+                       name = Release;
+               };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+               607A660D2B0EFA380010BFC8 /* Build configuration list for 
PBXProject "iOSTestbed" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               607A663F2B0EFA3A0010BFC8 /* Debug */,
+                               607A66402B0EFA3A0010BFC8 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               607A66412B0EFA3A0010BFC8 /* Build configuration list for 
PBXNativeTarget "iOSTestbed" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               607A66422B0EFA3A0010BFC8 /* Debug */,
+                               607A66432B0EFA3A0010BFC8 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               607A66442B0EFA3A0010BFC8 /* Build configuration list for 
PBXNativeTarget "iOSTestbedTests" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               607A66452B0EFA3A0010BFC8 /* Debug */,
+                               607A66462B0EFA3A0010BFC8 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+/* End XCConfigurationList section */
+       };
+       rootObject = 607A660A2B0EFA380010BFC8 /* Project object */;
+}
diff --git a/iOS/testbed/iOSTestbed/AppDelegate.h 
b/iOS/testbed/iOSTestbed/AppDelegate.h
new file mode 100644
index 00000000000000..f695b3b5efc08b
--- /dev/null
+++ b/iOS/testbed/iOSTestbed/AppDelegate.h
@@ -0,0 +1,11 @@
+//
+//  AppDelegate.h
+//  iOSTestbed
+//
+
+#import <UIKit/UIKit.h>
+
+@interface AppDelegate : UIResponder <UIApplicationDelegate>
+
+
+@end
diff --git a/iOS/testbed/iOSTestbed/AppDelegate.m 
b/iOS/testbed/iOSTestbed/AppDelegate.m
new file mode 100644
index 00000000000000..e5085399d0ca5f
--- /dev/null
+++ b/iOS/testbed/iOSTestbed/AppDelegate.m
@@ -0,0 +1,19 @@
+//
+//  AppDelegate.m
+//  iOSTestbed
+//
+
+#import "AppDelegate.h"
+
+@interface AppDelegate ()
+
+@end
+
+@implementation AppDelegate
+
+
+- (BOOL)application:(UIApplication *)application 
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+    return YES;
+}
+
+@end
diff --git 
a/iOS/testbed/iOSTestbed/Assets.xcassets/AccentColor.colorset/Contents.json 
b/iOS/testbed/iOSTestbed/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 00000000000000..eb878970081645
--- /dev/null
+++ b/iOS/testbed/iOSTestbed/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+  "colors" : [
+    {
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git 
a/iOS/testbed/iOSTestbed/Assets.xcassets/AppIcon.appiconset/Contents.json 
b/iOS/testbed/iOSTestbed/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000000000..13613e3ee1a934
--- /dev/null
+++ b/iOS/testbed/iOSTestbed/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,13 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "platform" : "ios",
+      "size" : "1024x1024"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/iOS/testbed/iOSTestbed/Assets.xcassets/Contents.json 
b/iOS/testbed/iOSTestbed/Assets.xcassets/Contents.json
new file mode 100644
index 00000000000000..73c00596a7fca3
--- /dev/null
+++ b/iOS/testbed/iOSTestbed/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/iOS/testbed/iOSTestbed/Base.lproj/LaunchScreen.storyboard 
b/iOS/testbed/iOSTestbed/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 00000000000000..5daafe73a866b7
--- /dev/null
+++ b/iOS/testbed/iOSTestbed/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" 
version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" 
propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" 
useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" 
version="22131"/>
+    </dependencies>
+    <scenes/>
+</document>
diff --git a/iOS/testbed/iOSTestbed/dylib-Info-template.plist 
b/iOS/testbed/iOSTestbed/dylib-Info-template.plist
new file mode 100644
index 00000000000000..f652e272f71c88
--- /dev/null
+++ b/iOS/testbed/iOSTestbed/dylib-Info-template.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" 
"http://www.apple.com/DTDs/PropertyList-1.0.dtd";>
+<plist version="1.0">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>en</string>
+       <key>CFBundleExecutable</key>
+       <string></string>
+       <key>CFBundleIdentifier</key>
+       <string></string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundlePackageType</key>
+       <string>APPL</string>
+       <key>CFBundleShortVersionString</key>
+       <string>1.0</string>
+       <key>CFBundleSupportedPlatforms</key>
+       <array>
+               <string>iPhoneOS</string>
+       </array>
+       <key>MinimumOSVersion</key>
+       <string>12.0</string>
+       <key>CFBundleVersion</key>
+       <string>1</string>
+</dict>
+</plist>
diff --git a/iOS/testbed/iOSTestbed/iOSTestbed-Info.plist 
b/iOS/testbed/iOSTestbed/iOSTestbed-Info.plist
new file mode 100644
index 00000000000000..e2aa460b6fd5ee
--- /dev/null
+++ b/iOS/testbed/iOSTestbed/iOSTestbed-Info.plist
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" 
"http://www.apple.com/DTDs/PropertyList-1.0.dtd";>
+<plist version="1.0">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>en</string>
+       <key>CFBundleDisplayName</key>
+       <string>${PRODUCT_NAME}</string>
+       <key>CFBundleExecutable</key>
+       <string>${EXECUTABLE_NAME}</string>
+       <key>CFBundleIdentifier</key>
+       <string>org.python.iOSTestbed</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundleName</key>
+       <string>${PRODUCT_NAME}</string>
+       <key>CFBundlePackageType</key>
+       <string>APPL</string>
+       <key>CFBundleShortVersionString</key>
+       <string>1.0</string>
+       <key>CFBundleSignature</key>
+       <string>????</string>
+       <key>CFBundleVersion</key>
+       <string>1</string>
+       <key>LSRequiresIPhoneOS</key>
+       <true/>
+       <key>UIRequiresFullScreen</key>
+       <true/>
+       <key>UILaunchStoryboardName</key>
+       <string>Launch Screen</string>
+       <key>UISupportedInterfaceOrientations</key>
+       <array>
+               <string>UIInterfaceOrientationPortrait</string>
+               <string>UIInterfaceOrientationLandscapeLeft</string>
+               <string>UIInterfaceOrientationLandscapeRight</string>
+       </array>
+       <key>UISupportedInterfaceOrientations~ipad</key>
+       <array>
+               <string>UIInterfaceOrientationPortrait</string>
+               <string>UIInterfaceOrientationPortraitUpsideDown</string>
+               <string>UIInterfaceOrientationLandscapeLeft</string>
+               <string>UIInterfaceOrientationLandscapeRight</string>
+       </array>
+       <key>MainModule</key>
+       <string>ios</string>
+       <key>UIApplicationSceneManifest</key>
+       <dict>
+               <key>UIApplicationSupportsMultipleScenes</key>
+               <false/>
+               <key>UISceneConfigurations</key>
+               <dict/>
+       </dict>
+</dict>
+</plist>
diff --git a/iOS/testbed/iOSTestbed/main.m b/iOS/testbed/iOSTestbed/main.m
new file mode 100644
index 00000000000000..e32bd78c9b42ee
--- /dev/null
+++ b/iOS/testbed/iOSTestbed/main.m
@@ -0,0 +1,16 @@
+//
+//  main.m
+//  iOSTestbed
+//
+
+#import <UIKit/UIKit.h>
+#import "AppDelegate.h"
+
+int main(int argc, char * argv[]) {
+    NSString * appDelegateClassName;
+    @autoreleasepool {
+        appDelegateClassName = NSStringFromClass([AppDelegate class]);
+
+        return UIApplicationMain(argc, argv, nil, appDelegateClassName);
+    }
+}
diff --git a/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m 
b/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m
new file mode 100644
index 00000000000000..53ea107db4a2de
--- /dev/null
+++ b/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m
@@ -0,0 +1,111 @@
+#import <XCTest/XCTest.h>
+#import <Python/Python.h>
+
+@interface iOSTestbedTests : XCTestCase
+
+@end
+
+@implementation iOSTestbedTests
+
+
+- (void)testPython {
+    // Arguments to pass into the test suite runner.
+    // argv[0] must identify the process; any subsequent arg
+    // will be handled as if it were an argument to `python -m test`
+    const char *argv[] = {
+        "iOSTestbed", // argv[0] is the process that is running.
+        "-uall",  // Enable all resources
+        "-v",  // run in verbose mode so we get test failure information
+        // To run a subset of tests, add the test names below; e.g.,
+        // "test_os",
+        // "test_sys",
+    };
+
+    // Start a Python interpreter.
+    int exit_code;
+    PyStatus status;
+    PyPreConfig preconfig;
+    PyConfig config;
+    NSString *python_home;
+    wchar_t *wtmp_str;
+
+    NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
+
+    // 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;
+    // 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", 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, sizeof(argv) / sizeof(char *), 
(char**) argv);
+    if (PyStatus_Exception(status)) {
+        XCTFail(@"Unable to configure argc/argv: %s", status.err_msg);
+        PyConfig_Clear(&config);
+        return;
+    }
+
+    NSLog(@"Initializing Python runtime...");
+    status = Py_InitializeFromConfig(&config);
+    if (PyStatus_Exception(status)) {
+        XCTFail(@"Unable to initialize Python interpreter: %s", 
status.err_msg);
+        PyConfig_Clear(&config);
+        return;
+    }
+
+    // Start the test suite. Print a separator to differentiate Python startup 
logs from app logs
+    
NSLog(@"---------------------------------------------------------------------------");
+
+    exit_code = Py_RunMain();
+    XCTAssertEqual(exit_code, 0, @"Python test suite did not pass");
+
+    
NSLog(@"---------------------------------------------------------------------------");
+
+    Py_Finalize();
+}
+
+
+@end

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to