From ffd44767625825804c54683b1f82835dc45dfd1b Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 13 Dec 2024 13:06:39 +0800 Subject: [PATCH 01/26] Update patch to Python 3.13.1. --- Makefile | 2 +- README.rst | 4 +- patch/Python/Python.patch | 256 +++++++++++++++++--------------------- 3 files changed, 115 insertions(+), 147 deletions(-) diff --git a/Makefile b/Makefile index 04b19588..c0b18f8a 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ BUILD_NUMBER=custom # of a release cycle, as official binaries won't be published. # PYTHON_MICRO_VERSION is the full version number, without any alpha/beta/rc suffix. (e.g., 3.10.0) # PYTHON_VER is the major/minor version (e.g., 3.10) -PYTHON_VERSION=3.14.0a2 +PYTHON_VERSION=3.13.1 PYTHON_PKG_VERSION=$(PYTHON_VERSION) PYTHON_MICRO_VERSION=$(shell echo $(PYTHON_VERSION) | grep -Eo "\d+\.\d+\.\d+") PYTHON_PKG_MICRO_VERSION=$(shell echo $(PYTHON_PKG_VERSION) | grep -Eo "\d+\.\d+\.\d+") diff --git a/README.rst b/README.rst index 94c0ae94..39c2a5a2 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,7 @@ Python Apple Support This is a meta-package for building a version of Python that can be embedded into a macOS, iOS, tvOS or watchOS project. -**This branch builds a packaged version of Python 3.14**. +**This branch builds a packaged version of Python 3.13**. Other Python versions are available by cloning other branches of the main repository: @@ -12,7 +12,7 @@ repository: * `Python 3.10 `__ * `Python 3.11 `__ * `Python 3.12 `__ -* `Python 3.13 `__ +* `Python 3.14 `__ It works by downloading, patching, and building a fat binary of Python and selected pre-requisites, and packaging them as frameworks that can be diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 36d93a03..fa07bc1c 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1,8 +1,8 @@ diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst -index 6194d7446c7..55a9dd1f25f 100644 +index 612aa2aa711..cd78fe18e35 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst -@@ -1279,6 +1279,17 @@ +@@ -1271,6 +1271,17 @@ Default: ``1`` in Python config and ``0`` in isolated config. @@ -91,26 +91,8 @@ index 4d4eb2031ee..aa43f75ec35 100644 App Store Compliance ==================== -diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst -index 3eabf22e499..b509f2a0607 100644 ---- a/Doc/whatsnew/3.14.rst -+++ b/Doc/whatsnew/3.14.rst -@@ -205,6 +205,13 @@ - making it a :term:`generic type`. - (Contributed by Brian Schubert in :gh:`126012`.) - -+* iOS and macOS apps can now be configured to redirect ``stdout`` and -+ ``stderr`` content to the system log. (Contributed by Russell Keith-Magee in -+ :gh:`127592`.) -+ -+* The iOS testbed is now able to stream test output while the test is running. -+ The testbed can also be used to run the test suite of projects other than -+ CPython itself. (Contributed by Russell Keith-Magee in :gh:`127592`.) - - New modules - =========== diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h -index f69c586a4f9..8ef19f67706 100644 +index 5da5ef9e543..20f5c9ad9bb 100644 --- a/Include/cpython/initconfig.h +++ b/Include/cpython/initconfig.h @@ -179,6 +179,9 @@ @@ -193,10 +175,10 @@ index f69c586a4f9..8ef19f67706 100644 + + return len(b) diff --git a/Lib/platform.py b/Lib/platform.py -index 239e660cd16..8e007c3c3b5 100644 +index 5958382276e..5db5eb276a2 100755 --- a/Lib/platform.py +++ b/Lib/platform.py -@@ -520,6 +520,54 @@ +@@ -521,6 +521,54 @@ return IOSVersionInfo(system, release, model, is_simulator) @@ -251,7 +233,7 @@ index 239e660cd16..8e007c3c3b5 100644 def _java_getprop(name, default): """This private helper is deprecated in 3.13 and will be removed in 3.15""" from java.lang import System -@@ -883,14 +931,25 @@ +@@ -884,14 +932,25 @@ csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0) return 'Alpha' if cpu_number >= 128 else 'VAX' @@ -280,7 +262,7 @@ index 239e660cd16..8e007c3c3b5 100644 def from_subprocess(): """ Fall back to `uname -p` -@@ -1050,9 +1109,13 @@ +@@ -1051,9 +1110,13 @@ system = 'Android' release = android_ver().release @@ -295,7 +277,7 @@ index 239e660cd16..8e007c3c3b5 100644 vals = system, node, release, version, machine # Replace 'unknown' values with the more portable '' -@@ -1342,6 +1405,10 @@ +@@ -1343,6 +1406,10 @@ # macOS and iOS both report as a "Darwin" kernel if sys.platform == "ios": system, release, _, _ = ios_ver() @@ -307,10 +289,10 @@ index 239e660cd16..8e007c3c3b5 100644 macos_release = mac_ver()[0] if macos_release: diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py -index 67a071963d8..eefcac66cb5 100644 +index ec3b638f007..58d6ac7f108 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py -@@ -669,6 +669,14 @@ +@@ -668,6 +668,14 @@ release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0") osname = sys.platform machine = sys.implementation._multiarch @@ -483,28 +465,13 @@ index 67a071963d8..eefcac66cb5 100644 + fr"{type(obj).__name__}" + ): + self.log.buffer.write(obj) -diff --git a/Lib/test/test_capi/test_config.py b/Lib/test/test_capi/test_config.py -index 77730ad2f32..a3179efe4a8 100644 ---- a/Lib/test/test_capi/test_config.py -+++ b/Lib/test/test_capi/test_config.py -@@ -110,6 +110,10 @@ - options.extend(( - ("_pystats", bool, None), - )) -+ if support.is_apple: -+ options.extend(( -+ ("use_system_logger", bool, None), -+ )) - - for name, option_type, sys_attr in options: - with self.subTest(name=name, option_type=option_type, diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py -index bf861ef06ee..468f821370e 100644 +index 3b43e422f82..5f70632182e 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py -@@ -649,6 +649,8 @@ +@@ -627,6 +627,8 @@ CONFIG_COMPAT.update({ - 'legacy_windows_stdio': False, + 'legacy_windows_stdio': 0, }) + if support.is_apple: + CONFIG_COMPAT['use_system_logger'] = False @@ -512,10 +479,10 @@ index bf861ef06ee..468f821370e 100644 CONFIG_PYTHON = dict(CONFIG_COMPAT, _config_init=API_PYTHON, diff --git a/Makefile.pre.in b/Makefile.pre.in -index 8d94ba361fd..6c1c95d4dd9 100644 +index 03ca4cb635b..46a37ded970 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in -@@ -2132,7 +2132,6 @@ +@@ -2061,7 +2061,6 @@ # This must be run *after* a `make install` has completed the build. The # `--with-framework-name` argument *cannot* be used when configuring the build. XCFOLDER:=iOSTestbed.$(MULTIARCH).$(shell date +%s) @@ -523,7 +490,7 @@ index 8d94ba361fd..6c1c95d4dd9 100644 .PHONY: testios testios: @if test "$(MACHDEP)" != "ios"; then \ -@@ -2151,29 +2150,12 @@ +@@ -2080,29 +2079,12 @@ echo "Cannot find a finalized iOS Python.framework. Have you run 'make install' to finalize the framework build?"; \ exit 1;\ fi @@ -589,20 +556,21 @@ index ec0857a4a99..2350e9dc821 100644 # elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX PLATFORM_TRIPLET=darwin diff --git a/Python/initconfig.c b/Python/initconfig.c -index 438f8a5c1cf..7851b86db1f 100644 +index 84717b4e3c9..5746416c826 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c -@@ -168,6 +168,9 @@ - SPEC(tracemalloc, UINT, READ_ONLY, NO_SYS), - SPEC(use_frozen_modules, BOOL, READ_ONLY, NO_SYS), - SPEC(use_hash_seed, BOOL, READ_ONLY, NO_SYS), +@@ -129,6 +129,10 @@ + #ifdef Py_DEBUG + SPEC(run_presite, WSTR_OPT), + #endif +#ifdef __APPLE__ -+ SPEC(use_system_logger, BOOL, PUBLIC, NO_SYS), ++ SPEC(use_system_logger, BOOL), +#endif - SPEC(user_site_directory, BOOL, READ_ONLY, NO_SYS), // sys.flags.no_user_site - SPEC(warn_default_encoding, BOOL, READ_ONLY, NO_SYS), ++ + {NULL, 0, 0}, + }; -@@ -884,6 +887,9 @@ +@@ -744,6 +748,9 @@ assert(config->cpu_count != 0); // config->use_frozen_modules is initialized later // by _PyConfig_InitImportConfig(). @@ -612,7 +580,7 @@ index 438f8a5c1cf..7851b86db1f 100644 #ifdef Py_STATS assert(config->_pystats >= 0); #endif -@@ -986,6 +992,9 @@ +@@ -846,6 +853,9 @@ config->_is_python_build = 0; config->code_debug_ranges = 1; config->cpu_count = -1; @@ -621,8 +589,8 @@ index 438f8a5c1cf..7851b86db1f 100644 +#endif #ifdef Py_GIL_DISABLED config->enable_gil = _PyConfig_GIL_DEFAULT; - config->tlbc_enabled = 1; -@@ -1015,6 +1024,9 @@ + #endif +@@ -874,6 +884,9 @@ #ifdef MS_WINDOWS config->legacy_windows_stdio = 0; #endif @@ -632,7 +600,7 @@ index 438f8a5c1cf..7851b86db1f 100644 } -@@ -1049,6 +1061,9 @@ +@@ -909,6 +922,9 @@ #ifdef MS_WINDOWS config->legacy_windows_stdio = 0; #endif @@ -643,10 +611,10 @@ index 438f8a5c1cf..7851b86db1f 100644 diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c -index 23882d08384..64985527606 100644 +index 8fe5bb8b300..d23cfc62d0f 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c -@@ -45,7 +45,9 @@ +@@ -43,7 +43,9 @@ #endif #if defined(__APPLE__) @@ -656,7 +624,7 @@ index 23882d08384..64985527606 100644 #endif #ifdef HAVE_SIGNAL_H -@@ -75,6 +77,9 @@ +@@ -73,6 +75,9 @@ #ifdef __ANDROID__ static PyStatus init_android_streams(PyThreadState *tstate); #endif @@ -666,7 +634,7 @@ index 23882d08384..64985527606 100644 static void wait_for_thread_shutdown(PyThreadState *tstate); static void finalize_subinterpreters(void); static void call_ll_exitfuncs(_PyRuntimeState *runtime); -@@ -1257,6 +1262,14 @@ +@@ -1253,6 +1258,14 @@ return status; } #endif @@ -681,7 +649,7 @@ index 23882d08384..64985527606 100644 #ifdef Py_DEBUG run_presite(tstate); -@@ -2931,6 +2944,75 @@ +@@ -2920,6 +2933,75 @@ #endif // __ANDROID__ @@ -758,7 +726,7 @@ index 23882d08384..64985527606 100644 static void _Py_FatalError_DumpTracebacks(int fd, PyInterpreterState *interp, diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h -index c8cdb933bb1..584b050fc4b 100644 +index faeed0b7125..dfe0fa2acd8 100644 --- a/Python/stdlib_module_names.h +++ b/Python/stdlib_module_names.h @@ -6,6 +6,7 @@ @@ -770,10 +738,10 @@ index c8cdb933bb1..584b050fc4b 100644 "_asyncio", "_bisect", diff --git a/configure b/configure -index 5b44a3d6992..83803f12853 100755 +index ae70f02f70e..af518f926d8 100755 --- a/configure +++ b/configure -@@ -979,6 +979,8 @@ +@@ -978,6 +978,8 @@ CFLAGS CC HAS_XCRUN @@ -782,7 +750,7 @@ index 5b44a3d6992..83803f12853 100755 IPHONEOS_DEPLOYMENT_TARGET EXPORT_MACOSX_DEPLOYMENT_TARGET CONFIGURE_MACOSX_DEPLOYMENT_TARGET -@@ -4053,6 +4055,12 @@ +@@ -4054,6 +4056,12 @@ *-apple-ios*) ac_sys_system=iOS ;; @@ -795,7 +763,7 @@ index 5b44a3d6992..83803f12853 100755 *-*-vxworks*) ac_sys_system=VxWorks ;; -@@ -4130,7 +4138,7 @@ +@@ -4108,7 +4116,7 @@ # On cross-compile builds, configure will look for a host-specific compiler by # prepending the user-provided host triple to the required binary name. # @@ -804,7 +772,7 @@ index 5b44a3d6992..83803f12853 100755 # which isn't a binary that exists, and isn't very convenient, as it contains the # iOS version. As the default cross-compiler name won't exist, configure falls # back to gcc, which *definitely* won't work. We're providing wrapper scripts for -@@ -4145,6 +4153,14 @@ +@@ -4123,6 +4131,14 @@ aarch64-apple-ios*-simulator) AR=arm64-apple-ios-simulator-ar ;; aarch64-apple-ios*) AR=arm64-apple-ios-ar ;; x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;; @@ -819,7 +787,7 @@ index 5b44a3d6992..83803f12853 100755 *) esac fi -@@ -4153,6 +4169,14 @@ +@@ -4131,6 +4147,14 @@ aarch64-apple-ios*-simulator) CC=arm64-apple-ios-simulator-clang ;; aarch64-apple-ios*) CC=arm64-apple-ios-clang ;; x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;; @@ -834,7 +802,7 @@ index 5b44a3d6992..83803f12853 100755 *) esac fi -@@ -4161,6 +4185,14 @@ +@@ -4139,6 +4163,14 @@ aarch64-apple-ios*-simulator) CPP=arm64-apple-ios-simulator-cpp ;; aarch64-apple-ios*) CPP=arm64-apple-ios-cpp ;; x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;; @@ -849,7 +817,7 @@ index 5b44a3d6992..83803f12853 100755 *) esac fi -@@ -4169,6 +4201,14 @@ +@@ -4147,6 +4179,14 @@ aarch64-apple-ios*-simulator) CXX=arm64-apple-ios-simulator-clang++ ;; aarch64-apple-ios*) CXX=arm64-apple-ios-clang++ ;; x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;; @@ -864,7 +832,7 @@ index 5b44a3d6992..83803f12853 100755 *) esac fi -@@ -4289,8 +4329,10 @@ +@@ -4267,8 +4307,10 @@ case $enableval in yes) case $ac_sys_system in @@ -877,7 +845,7 @@ index 5b44a3d6992..83803f12853 100755 *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 esac esac -@@ -4299,6 +4341,8 @@ +@@ -4277,6 +4319,8 @@ no) case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -886,7 +854,7 @@ index 5b44a3d6992..83803f12853 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4405,6 +4449,36 @@ +@@ -4383,6 +4427,36 @@ ac_config_files="$ac_config_files iOS/Resources/Info.plist" @@ -923,7 +891,7 @@ index 5b44a3d6992..83803f12853 100755 ;; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 -@@ -4416,6 +4490,8 @@ +@@ -4394,6 +4468,8 @@ case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -932,7 +900,7 @@ index 5b44a3d6992..83803f12853 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4469,8 +4545,8 @@ +@@ -4447,8 +4523,8 @@ case "$withval" in yes) case $ac_sys_system in @@ -943,7 +911,7 @@ index 5b44a3d6992..83803f12853 100755 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" ;; *) as_fn_error $? "no default app store compliance patch available for $ac_sys_system" "$LINENO" 5 ;; -@@ -4488,8 +4564,8 @@ +@@ -4466,8 +4542,8 @@ else $as_nop case $ac_sys_system in @@ -954,7 +922,7 @@ index 5b44a3d6992..83803f12853 100755 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: applying default app store compliance patch" >&5 printf "%s\n" "applying default app store compliance patch" >&6; } -@@ -4543,6 +4619,50 @@ +@@ -4521,6 +4597,50 @@ ;; esac ;; @@ -1005,7 +973,7 @@ index 5b44a3d6992..83803f12853 100755 *-*-vxworks*) _host_ident=$host_cpu ;; -@@ -4621,9 +4741,13 @@ +@@ -4599,9 +4719,13 @@ define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; @@ -1020,7 +988,7 @@ index 5b44a3d6992..83803f12853 100755 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -4686,7 +4810,10 @@ +@@ -4664,7 +4788,10 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' @@ -1032,7 +1000,7 @@ index 5b44a3d6992..83803f12853 100755 # checks for alternative programs -@@ -4727,6 +4854,16 @@ +@@ -4705,6 +4832,16 @@ as_fn_append CFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" as_fn_append LDFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" ;; #( @@ -1049,7 +1017,7 @@ index 5b44a3d6992..83803f12853 100755 *) : ;; esac -@@ -7031,6 +7168,10 @@ +@@ -7006,6 +7143,10 @@ MULTIARCH="" ;; #( iOS) : MULTIARCH="" ;; #( @@ -1060,7 +1028,7 @@ index 5b44a3d6992..83803f12853 100755 FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -7051,7 +7192,7 @@ +@@ -7026,7 +7167,7 @@ printf "%s\n" "$MULTIARCH" >&6; } case $ac_sys_system in #( @@ -1069,7 +1037,7 @@ index 5b44a3d6992..83803f12853 100755 SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2` ;; #( *) : SOABI_PLATFORM=$PLATFORM_TRIPLET -@@ -7102,6 +7243,14 @@ +@@ -7077,6 +7218,14 @@ PY_SUPPORT_TIER=3 ;; #( aarch64-apple-ios*/clang) : PY_SUPPORT_TIER=3 ;; #( @@ -1084,7 +1052,7 @@ index 5b44a3d6992..83803f12853 100755 aarch64-*-linux-android/clang) : PY_SUPPORT_TIER=3 ;; #( x86_64-*-linux-android/clang) : -@@ -7531,7 +7680,7 @@ +@@ -7550,7 +7699,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; @@ -1093,7 +1061,7 @@ index 5b44a3d6992..83803f12853 100755 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5;; -@@ -7597,7 +7746,7 @@ +@@ -7616,7 +7765,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -1102,7 +1070,7 @@ index 5b44a3d6992..83803f12853 100755 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -13150,7 +13299,7 @@ +@@ -12955,7 +13104,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -1111,7 +1079,7 @@ index 5b44a3d6992..83803f12853 100755 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -13283,7 +13432,7 @@ +@@ -13088,7 +13237,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -1120,7 +1088,7 @@ index 5b44a3d6992..83803f12853 100755 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -13307,7 +13456,7 @@ +@@ -13112,7 +13261,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -1129,7 +1097,7 @@ index 5b44a3d6992..83803f12853 100755 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -14759,7 +14908,7 @@ +@@ -14511,7 +14660,7 @@ ctypes_malloc_closure=yes ;; #( @@ -1138,7 +1106,7 @@ index 5b44a3d6992..83803f12853 100755 ctypes_malloc_closure=yes ;; #( -@@ -18262,12 +18411,6 @@ +@@ -17962,12 +18111,6 @@ then : printf "%s\n" "#define HAVE_DUP3 1" >>confdefs.h @@ -1151,7 +1119,7 @@ index 5b44a3d6992..83803f12853 100755 fi ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes -@@ -18328,18 +18471,6 @@ +@@ -18028,18 +18171,6 @@ then : printf "%s\n" "#define HAVE_FEXECVE 1" >>confdefs.h @@ -1170,7 +1138,7 @@ index 5b44a3d6992..83803f12853 100755 fi ac_fn_c_check_func "$LINENO" "fpathconf" "ac_cv_func_fpathconf" if test "x$ac_cv_func_fpathconf" = xyes -@@ -18766,24 +18897,6 @@ +@@ -18466,24 +18597,6 @@ then : printf "%s\n" "#define HAVE_POSIX_OPENPT 1" >>confdefs.h @@ -1195,7 +1163,7 @@ index 5b44a3d6992..83803f12853 100755 fi ac_fn_c_check_func "$LINENO" "pread" "ac_cv_func_pread" if test "x$ac_cv_func_pread" = xyes -@@ -19072,12 +19185,6 @@ +@@ -18772,12 +18885,6 @@ then : printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h @@ -1208,7 +1176,7 @@ index 5b44a3d6992..83803f12853 100755 fi ac_fn_c_check_func "$LINENO" "sigfillset" "ac_cv_func_sigfillset" if test "x$ac_cv_func_sigfillset" = xyes -@@ -19346,11 +19453,11 @@ +@@ -19046,11 +19153,11 @@ fi @@ -1222,7 +1190,7 @@ index 5b44a3d6992..83803f12853 100755 ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" if test "x$ac_cv_func_getentropy" = xyes then : -@@ -19372,6 +19479,53 @@ +@@ -19072,6 +19179,53 @@ fi @@ -1276,7 +1244,7 @@ index 5b44a3d6992..83803f12853 100755 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5 printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; } if test ${ac_cv_c_undeclared_builtin_options+y} -@@ -22247,7 +22401,8 @@ +@@ -21868,7 +22022,8 @@ # check for openpty, login_tty, and forkpty @@ -1286,7 +1254,7 @@ index 5b44a3d6992..83803f12853 100755 for ac_func in openpty do : -@@ -22343,7 +22498,7 @@ +@@ -21964,7 +22119,7 @@ fi done @@ -1295,7 +1263,7 @@ index 5b44a3d6992..83803f12853 100755 printf %s "checking for library containing login_tty... " >&6; } if test ${ac_cv_search_login_tty+y} then : -@@ -22500,6 +22655,7 @@ +@@ -22121,6 +22276,7 @@ fi done @@ -1303,7 +1271,7 @@ index 5b44a3d6992..83803f12853 100755 # check for long file support functions ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" -@@ -22746,10 +22902,10 @@ +@@ -22367,10 +22523,10 @@ done @@ -1316,7 +1284,7 @@ index 5b44a3d6992..83803f12853 100755 then for ac_func in clock_settime -@@ -24977,8 +25133,8 @@ +@@ -24602,8 +24758,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -1327,7 +1295,7 @@ index 5b44a3d6992..83803f12853 100755 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -27730,7 +27886,7 @@ +@@ -27251,7 +27407,7 @@ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for device files" >&5 printf "%s\n" "$as_me: checking for device files" >&6;} @@ -1336,7 +1304,7 @@ index 5b44a3d6992..83803f12853 100755 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -28162,7 +28318,7 @@ +@@ -27684,7 +27840,7 @@ with_ensurepip=no ;; #( WASI) : with_ensurepip=no ;; #( @@ -1345,7 +1313,7 @@ index 5b44a3d6992..83803f12853 100755 with_ensurepip=no ;; #( *) : with_ensurepip=upgrade -@@ -29091,7 +29247,7 @@ +@@ -28703,7 +28859,7 @@ ;; #( Darwin) : ;; #( @@ -1354,7 +1322,7 @@ index 5b44a3d6992..83803f12853 100755 -@@ -32989,6 +33145,8 @@ +@@ -32468,6 +32624,8 @@ "Mac/Resources/framework/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/framework/Info.plist" ;; "Mac/Resources/app/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/app/Info.plist" ;; "iOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES iOS/Resources/Info.plist" ;; @@ -1364,7 +1332,7 @@ index 5b44a3d6992..83803f12853 100755 "Misc/python.pc") CONFIG_FILES="$CONFIG_FILES Misc/python.pc" ;; "Misc/python-embed.pc") CONFIG_FILES="$CONFIG_FILES Misc/python-embed.pc" ;; diff --git a/configure.ac b/configure.ac -index 7904f8990c4..f2367cd47d3 100644 +index a764028e49f..be1b9e784db 100644 --- a/configure.ac +++ b/configure.ac @@ -330,6 +330,12 @@ @@ -1380,7 +1348,7 @@ index 7904f8990c4..f2367cd47d3 100644 *-*-vxworks*) ac_sys_system=VxWorks ;; -@@ -401,7 +407,7 @@ +@@ -382,7 +388,7 @@ # On cross-compile builds, configure will look for a host-specific compiler by # prepending the user-provided host triple to the required binary name. # @@ -1389,7 +1357,7 @@ index 7904f8990c4..f2367cd47d3 100644 # which isn't a binary that exists, and isn't very convenient, as it contains the # iOS version. As the default cross-compiler name won't exist, configure falls # back to gcc, which *definitely* won't work. We're providing wrapper scripts for -@@ -416,6 +422,14 @@ +@@ -397,6 +403,14 @@ aarch64-apple-ios*-simulator) AR=arm64-apple-ios-simulator-ar ;; aarch64-apple-ios*) AR=arm64-apple-ios-ar ;; x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;; @@ -1404,7 +1372,7 @@ index 7904f8990c4..f2367cd47d3 100644 *) esac fi -@@ -424,6 +438,14 @@ +@@ -405,6 +419,14 @@ aarch64-apple-ios*-simulator) CC=arm64-apple-ios-simulator-clang ;; aarch64-apple-ios*) CC=arm64-apple-ios-clang ;; x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;; @@ -1419,7 +1387,7 @@ index 7904f8990c4..f2367cd47d3 100644 *) esac fi -@@ -432,6 +454,14 @@ +@@ -413,6 +435,14 @@ aarch64-apple-ios*-simulator) CPP=arm64-apple-ios-simulator-cpp ;; aarch64-apple-ios*) CPP=arm64-apple-ios-cpp ;; x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;; @@ -1434,7 +1402,7 @@ index 7904f8990c4..f2367cd47d3 100644 *) esac fi -@@ -440,6 +470,14 @@ +@@ -421,6 +451,14 @@ aarch64-apple-ios*-simulator) CXX=arm64-apple-ios-simulator-clang++ ;; aarch64-apple-ios*) CXX=arm64-apple-ios-clang++ ;; x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;; @@ -1449,7 +1417,7 @@ index 7904f8990c4..f2367cd47d3 100644 *) esac fi -@@ -554,8 +592,10 @@ +@@ -535,8 +573,10 @@ case $enableval in yes) case $ac_sys_system in @@ -1462,7 +1430,7 @@ index 7904f8990c4..f2367cd47d3 100644 *) AC_MSG_ERROR([Unknown platform for framework build]) esac esac -@@ -564,6 +604,8 @@ +@@ -545,6 +585,8 @@ no) case $ac_sys_system in iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;; @@ -1471,7 +1439,7 @@ index 7904f8990c4..f2367cd47d3 100644 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -666,6 +708,34 @@ +@@ -647,6 +689,34 @@ AC_CONFIG_FILES([iOS/Resources/Info.plist]) ;; @@ -1506,7 +1474,7 @@ index 7904f8990c4..f2367cd47d3 100644 *) AC_MSG_ERROR([Unknown platform for framework build]) ;; -@@ -674,6 +744,8 @@ +@@ -655,6 +725,8 @@ ],[ case $ac_sys_system in iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;; @@ -1515,7 +1483,7 @@ index 7904f8990c4..f2367cd47d3 100644 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -726,8 +798,8 @@ +@@ -707,8 +779,8 @@ case "$withval" in yes) case $ac_sys_system in @@ -1526,7 +1494,7 @@ index 7904f8990c4..f2367cd47d3 100644 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" ;; *) AC_MSG_ERROR([no default app store compliance patch available for $ac_sys_system]) ;; -@@ -741,8 +813,8 @@ +@@ -722,8 +794,8 @@ esac ],[ case $ac_sys_system in @@ -1537,7 +1505,7 @@ index 7904f8990c4..f2367cd47d3 100644 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" AC_MSG_RESULT([applying default app store compliance patch]) ;; -@@ -790,6 +862,46 @@ +@@ -771,6 +843,46 @@ ;; esac ;; @@ -1584,7 +1552,7 @@ index 7904f8990c4..f2367cd47d3 100644 *-*-vxworks*) _host_ident=$host_cpu ;; -@@ -867,9 +979,13 @@ +@@ -848,9 +960,13 @@ define_xopen_source=no;; Darwin/@<:@[12]@:>@@<:@0-9@:>@.*) define_xopen_source=no;; @@ -1599,7 +1567,7 @@ index 7904f8990c4..f2367cd47d3 100644 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -928,8 +1044,11 @@ +@@ -909,8 +1025,11 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' @@ -1612,7 +1580,7 @@ index 7904f8990c4..f2367cd47d3 100644 # checks for alternative programs -@@ -963,11 +1082,17 @@ +@@ -944,11 +1063,17 @@ ], ) @@ -1631,7 +1599,7 @@ index 7904f8990c4..f2367cd47d3 100644 ], ) -@@ -1156,6 +1281,8 @@ +@@ -1136,6 +1261,8 @@ AS_CASE([$ac_sys_system], [Darwin*], [MULTIARCH=""], [iOS], [MULTIARCH=""], @@ -1640,7 +1608,7 @@ index 7904f8990c4..f2367cd47d3 100644 [FreeBSD*], [MULTIARCH=""], [MULTIARCH=$($CC --print-multiarch 2>/dev/null)] ) -@@ -1177,7 +1304,7 @@ +@@ -1157,7 +1284,7 @@ dnl use a single "fat" binary at runtime. SOABI_PLATFORM is the component of dnl the PLATFORM_TRIPLET that will be used in binary module extensions. AS_CASE([$ac_sys_system], @@ -1649,7 +1617,7 @@ index 7904f8990c4..f2367cd47d3 100644 [SOABI_PLATFORM=$PLATFORM_TRIPLET] ) -@@ -1211,6 +1338,10 @@ +@@ -1191,6 +1318,10 @@ [x86_64-*-freebsd*/clang], [PY_SUPPORT_TIER=3], dnl FreeBSD on AMD64 [aarch64-apple-ios*-simulator/clang], [PY_SUPPORT_TIER=3], dnl iOS Simulator on arm64 [aarch64-apple-ios*/clang], [PY_SUPPORT_TIER=3], dnl iOS on ARM64 @@ -1660,7 +1628,7 @@ index 7904f8990c4..f2367cd47d3 100644 [aarch64-*-linux-android/clang], [PY_SUPPORT_TIER=3], dnl Android on ARM64 [x86_64-*-linux-android/clang], [PY_SUPPORT_TIER=3], dnl Android on AMD64 -@@ -1520,7 +1651,7 @@ +@@ -1525,7 +1656,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; @@ -1669,7 +1637,7 @@ index 7904f8990c4..f2367cd47d3 100644 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) AC_MSG_ERROR([Unknown platform for framework build]);; -@@ -1585,7 +1716,7 @@ +@@ -1590,7 +1721,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -1678,7 +1646,7 @@ index 7904f8990c4..f2367cd47d3 100644 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -3407,7 +3538,7 @@ +@@ -3465,7 +3596,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -1687,7 +1655,7 @@ index 7904f8990c4..f2367cd47d3 100644 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -3531,7 +3662,7 @@ +@@ -3589,7 +3720,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -1696,7 +1664,7 @@ index 7904f8990c4..f2367cd47d3 100644 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -3555,7 +3686,7 @@ +@@ -3613,7 +3744,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -1705,7 +1673,7 @@ index 7904f8990c4..f2367cd47d3 100644 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -3975,7 +4106,7 @@ +@@ -3997,7 +4128,7 @@ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], @@ -1714,7 +1682,7 @@ index 7904f8990c4..f2367cd47d3 100644 ctypes_malloc_closure=yes ], [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] -@@ -5093,9 +5224,9 @@ +@@ -5091,9 +5222,9 @@ # checks for library functions AC_CHECK_FUNCS([ \ accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ @@ -1726,7 +1694,7 @@ index 7904f8990c4..f2367cd47d3 100644 gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \ getgrnam_r getgrouplist gethostname getitimer getloadavg getlogin \ getpeername getpgid getpid getppid getpriority _getpty \ -@@ -5103,15 +5234,14 @@ +@@ -5101,15 +5232,14 @@ getspnam getuid getwd grantpt if_nameindex initgroups kill killpg lchown linkat \ lockf lstat lutimes madvise mbrtowc memrchr mkdirat mkfifo mkfifoat \ mknod mknodat mktime mmap mremap nice openat opendir pathconf pause pipe \ @@ -1744,7 +1712,7 @@ index 7904f8990c4..f2367cd47d3 100644 sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ -@@ -5126,12 +5256,20 @@ +@@ -5124,12 +5254,20 @@ AC_CHECK_FUNCS([lchmod]) fi @@ -1768,7 +1736,7 @@ index 7904f8990c4..f2367cd47d3 100644 fi AC_CHECK_DECL([dirfd], -@@ -5385,20 +5523,22 @@ +@@ -5380,20 +5518,22 @@ ]) # check for openpty, login_tty, and forkpty @@ -1805,7 +1773,7 @@ index 7904f8990c4..f2367cd47d3 100644 # check for long file support functions AC_CHECK_FUNCS([fseek64 fseeko fstatvfs ftell64 ftello statvfs]) -@@ -5437,10 +5577,10 @@ +@@ -5432,10 +5572,10 @@ ]) ]) @@ -1818,7 +1786,7 @@ index 7904f8990c4..f2367cd47d3 100644 then AC_CHECK_FUNCS([clock_settime], [], [ AC_CHECK_LIB([rt], [clock_settime], [ -@@ -6191,8 +6331,8 @@ +@@ -6184,8 +6324,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -1829,7 +1797,7 @@ index 7904f8990c4..f2367cd47d3 100644 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -6856,7 +6996,7 @@ +@@ -6793,7 +6933,7 @@ dnl NOTE: Inform user how to proceed with files when cross compiling. dnl Some cross-compile builds are predictable; they won't ever dnl have /dev/ptmx or /dev/ptc, so we can set them explicitly. @@ -1838,7 +1806,7 @@ index 7904f8990c4..f2367cd47d3 100644 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -7112,7 +7252,7 @@ +@@ -7050,7 +7190,7 @@ AS_CASE([$ac_sys_system], [Emscripten], [with_ensurepip=no], [WASI], [with_ensurepip=no], @@ -1847,7 +1815,7 @@ index 7904f8990c4..f2367cd47d3 100644 [with_ensurepip=upgrade] ) ]) -@@ -7506,7 +7646,7 @@ +@@ -7458,7 +7598,7 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], From 5ca1c793a77e76b5cd5966feecf2598eab6d9a69 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 18 Dec 2024 07:11:07 +0800 Subject: [PATCH 02/26] Update build to use 3.14.0a3 --- Makefile | 2 +- patch/Python/Python.patch | 1359 ++----------------------------------- 2 files changed, 68 insertions(+), 1293 deletions(-) diff --git a/Makefile b/Makefile index 04b19588..720461ea 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ BUILD_NUMBER=custom # of a release cycle, as official binaries won't be published. # PYTHON_MICRO_VERSION is the full version number, without any alpha/beta/rc suffix. (e.g., 3.10.0) # PYTHON_VER is the major/minor version (e.g., 3.10) -PYTHON_VERSION=3.14.0a2 +PYTHON_VERSION=3.14.0a3 PYTHON_PKG_VERSION=$(PYTHON_VERSION) PYTHON_MICRO_VERSION=$(shell echo $(PYTHON_VERSION) | grep -Eo "\d+\.\d+\.\d+") PYTHON_PKG_MICRO_VERSION=$(shell echo $(PYTHON_PKG_VERSION) | grep -Eo "\d+\.\d+\.\d+") diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 36d93a03..19768c8e 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1,202 +1,8 @@ -diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst -index 6194d7446c7..55a9dd1f25f 100644 ---- a/Doc/c-api/init_config.rst -+++ b/Doc/c-api/init_config.rst -@@ -1279,6 +1279,17 @@ - - Default: ``1`` in Python config and ``0`` in isolated config. - -+ .. c:member:: int use_system_logger -+ -+ If non-zero, ``stdout`` and ``stderr`` will be redirected to the system -+ log. -+ -+ Only available on macOS 10.12 and later, and on iOS. -+ -+ Default: ``0`` (don't use system log). -+ -+ .. versionadded:: 3.13.2 -+ - .. c:member:: int user_site_directory - - If non-zero, add the user site directory to :data:`sys.path`. -diff --git a/Doc/using/ios.rst b/Doc/using/ios.rst -index 4d4eb2031ee..aa43f75ec35 100644 ---- a/Doc/using/ios.rst -+++ b/Doc/using/ios.rst -@@ -292,10 +292,12 @@ - 10. Add Objective C code to initialize and use a Python interpreter in embedded - mode. You should ensure that: - -- * :c:member:`UTF-8 mode ` is *enabled*; -- * :c:member:`Buffered stdio ` is *disabled*; -- * :c:member:`Writing bytecode ` is *disabled*; -- * :c:member:`Signal handlers ` are *enabled*; -+ * UTF-8 mode (:c:member:`PyPreConfig.utf8_mode`) is *enabled*; -+ * Buffered stdio (:c:member:`PyConfig.buffered_stdio`) is *disabled*; -+ * Writing bytecode (:c:member:`PyConfig.write_bytecode`) is *disabled*; -+ * Signal handlers (:c:member:`PyConfig.install_signal_handlers`) are *enabled*; -+ * System logging (:c:member:`PyConfig.use_system_logger`) is *enabled* -+ (optional, but strongly recommended); - * ``PYTHONHOME`` for the interpreter is configured to point at the - ``python`` subfolder of your app's bundle; and - * The ``PYTHONPATH`` for the interpreter includes: -@@ -324,6 +326,49 @@ - * If you're using a separate folder for third-party packages, ensure that folder - is included as part of the ``PYTHONPATH`` configuration in step 10. - -+Testing a Python package -+------------------------ -+ -+The CPython source tree contains :source:`a testbed project ` that -+is used to run the CPython test suite on the iOS simulator. This testbed can also -+be used as a testbed project for running your Python library's test suite on iOS. -+ -+After building or obtaining an iOS XCFramework (See :source:`iOS/README.rst` -+for details), create a clone of the Python iOS testbed project by running: -+ -+.. code-block:: bash -+ -+ $ python iOS/testbed clone --framework --app --app app-testbed -+ -+You will need to modify the ``iOS/testbed`` reference to point to that -+directory in the CPython source tree; any folders specified with the ``--app`` -+flag will be copied into the cloned testbed project. The resulting testbed will -+be created in the ``app-testbed`` folder. In this example, the ``module1`` and -+``module2`` would be importable modules at runtime. If your project has -+additional dependencies, they can be installed into the -+``app-testbed/iOSTestbed/app_packages`` folder (using ``pip install --target -+app-testbed/iOSTestbed/app_packages`` or similar). -+ -+You can then use the ``app-testbed`` folder to run the test suite for your app, -+For example, if ``module1.tests`` was the entry point to your test suite, you -+could run: -+ -+.. code-block:: bash -+ -+ $ python app-testbed run -- module1.tests -+ -+This is the equivalent of running ``python -m module1.tests`` on a desktop -+Python build. Any arguments after the ``--`` will be passed to the testbed as -+if they were arguments to ``python -m`` on a desktop machine. -+ -+You can also open the testbed project in Xcode by running: -+ -+.. code-block:: bash -+ -+ $ open app-testbed/iOSTestbed.xcodeproj -+ -+This will allow you to use the full Xcode suite of tools for debugging. -+ - App Store Compliance - ==================== - -diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst -index 3eabf22e499..b509f2a0607 100644 ---- a/Doc/whatsnew/3.14.rst -+++ b/Doc/whatsnew/3.14.rst -@@ -205,6 +205,13 @@ - making it a :term:`generic type`. - (Contributed by Brian Schubert in :gh:`126012`.) - -+* iOS and macOS apps can now be configured to redirect ``stdout`` and -+ ``stderr`` content to the system log. (Contributed by Russell Keith-Magee in -+ :gh:`127592`.) -+ -+* The iOS testbed is now able to stream test output while the test is running. -+ The testbed can also be used to run the test suite of projects other than -+ CPython itself. (Contributed by Russell Keith-Magee in :gh:`127592`.) - - New modules - =========== -diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h -index f69c586a4f9..8ef19f67706 100644 ---- a/Include/cpython/initconfig.h -+++ b/Include/cpython/initconfig.h -@@ -179,6 +179,9 @@ - int use_frozen_modules; - int safe_path; - int int_max_str_digits; -+#ifdef __APPLE__ -+ int use_system_logger; -+#endif - - int cpu_count; - #ifdef Py_GIL_DISABLED ---- /dev/null -+++ b/Lib/_apple_support.py -@@ -0,0 +1,66 @@ -+import io -+import sys -+ -+ -+def init_streams(log_write, stdout_level, stderr_level): -+ # Redirect stdout and stderr to the Apple system log. This method is -+ # invoked by init_apple_streams() (initconfig.c) if config->use_system_logger -+ # is enabled. -+ sys.stdout = SystemLog(log_write, stdout_level, errors=sys.stderr.errors) -+ sys.stderr = SystemLog(log_write, stderr_level, errors=sys.stderr.errors) -+ -+ -+class SystemLog(io.TextIOWrapper): -+ def __init__(self, log_write, level, **kwargs): -+ kwargs.setdefault("encoding", "UTF-8") -+ kwargs.setdefault("line_buffering", True) -+ super().__init__(LogStream(log_write, level), **kwargs) -+ -+ def __repr__(self): -+ return f"" -+ -+ def write(self, s): -+ if not isinstance(s, str): -+ raise TypeError( -+ f"write() argument must be str, not {type(s).__name__}") -+ -+ # In case `s` is a str subclass that writes itself to stdout or stderr -+ # when we call its methods, convert it to an actual str. -+ s = str.__str__(s) -+ -+ # We want to emit one log message per line, so split -+ # the string before sending it to the superclass. -+ for line in s.splitlines(keepends=True): -+ super().write(line) -+ -+ return len(s) -+ -+ -+class LogStream(io.RawIOBase): -+ def __init__(self, log_write, level): -+ self.log_write = log_write -+ self.level = level -+ -+ def __repr__(self): -+ return f"" -+ -+ def writable(self): -+ return True -+ -+ def write(self, b): -+ if type(b) is not bytes: -+ try: -+ b = bytes(memoryview(b)) -+ except TypeError: -+ raise TypeError( -+ f"write() argument must be bytes-like, not {type(b).__name__}" -+ ) from None -+ -+ # Writing an empty string to the stream should have no effect. -+ if b: -+ # Encode null bytes using "modified UTF-8" to avoid truncating the -+ # message. This should not affect the return value, as the caller -+ # may be expecting it to match the length of the input. -+ self.log_write(self.level, b.replace(b"\x00", b"\xc0\x80")) -+ -+ return len(b) diff --git a/Lib/platform.py b/Lib/platform.py -index 239e660cd16..8e007c3c3b5 100644 +index 1f6baed66d3..235dd98c60a 100644 --- a/Lib/platform.py +++ b/Lib/platform.py -@@ -520,6 +520,54 @@ +@@ -521,6 +521,54 @@ return IOSVersionInfo(system, release, model, is_simulator) @@ -251,7 +57,7 @@ index 239e660cd16..8e007c3c3b5 100644 def _java_getprop(name, default): """This private helper is deprecated in 3.13 and will be removed in 3.15""" from java.lang import System -@@ -883,14 +931,25 @@ +@@ -884,14 +932,25 @@ csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0) return 'Alpha' if cpu_number >= 128 else 'VAX' @@ -280,7 +86,7 @@ index 239e660cd16..8e007c3c3b5 100644 def from_subprocess(): """ Fall back to `uname -p` -@@ -1050,9 +1109,13 @@ +@@ -1051,9 +1110,13 @@ system = 'Android' release = android_ver().release @@ -295,7 +101,7 @@ index 239e660cd16..8e007c3c3b5 100644 vals = system, node, release, version, machine # Replace 'unknown' values with the more portable '' -@@ -1342,6 +1405,10 @@ +@@ -1343,6 +1406,10 @@ # macOS and iOS both report as a "Darwin" kernel if sys.platform == "ios": system, release, _, _ = ios_ver() @@ -307,10 +113,10 @@ index 239e660cd16..8e007c3c3b5 100644 macos_release = mac_ver()[0] if macos_release: diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py -index 67a071963d8..eefcac66cb5 100644 +index ed7b6a335d0..322e6cf1eee 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py -@@ -669,6 +669,14 @@ +@@ -690,6 +690,14 @@ release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0") osname = sys.platform machine = sys.implementation._multiarch @@ -325,238 +131,6 @@ index 67a071963d8..eefcac66cb5 100644 else: import _osx_support osname, release, machine = _osx_support.get_platform_osx( ---- /dev/null -+++ b/Lib/test/test_apple.py -@@ -0,0 +1,155 @@ -+import unittest -+from _apple_support import SystemLog -+from test.support import is_apple -+from unittest.mock import Mock, call -+ -+if not is_apple: -+ raise unittest.SkipTest("Apple-specific") -+ -+ -+# Test redirection of stdout and stderr to the Apple system log. -+class TestAppleSystemLogOutput(unittest.TestCase): -+ maxDiff = None -+ -+ def assert_writes(self, output): -+ self.assertEqual( -+ self.log_write.mock_calls, -+ [ -+ call(self.log_level, line) -+ for line in output -+ ] -+ ) -+ -+ self.log_write.reset_mock() -+ -+ def setUp(self): -+ self.log_write = Mock() -+ self.log_level = 42 -+ self.log = SystemLog(self.log_write, self.log_level, errors="replace") -+ -+ def test_repr(self): -+ self.assertEqual(repr(self.log), "") -+ self.assertEqual(repr(self.log.buffer), "") -+ -+ def test_log_config(self): -+ self.assertIs(self.log.writable(), True) -+ self.assertIs(self.log.readable(), False) -+ -+ self.assertEqual("UTF-8", self.log.encoding) -+ self.assertEqual("replace", self.log.errors) -+ -+ self.assertIs(self.log.line_buffering, True) -+ self.assertIs(self.log.write_through, False) -+ -+ def test_empty_str(self): -+ self.log.write("") -+ self.log.flush() -+ -+ self.assert_writes([]) -+ -+ def test_simple_str(self): -+ self.log.write("hello world\n") -+ -+ self.assert_writes([b"hello world\n"]) -+ -+ def test_buffered_str(self): -+ self.log.write("h") -+ self.log.write("ello") -+ self.log.write(" ") -+ self.log.write("world\n") -+ self.log.write("goodbye.") -+ self.log.flush() -+ -+ self.assert_writes([b"hello world\n", b"goodbye."]) -+ -+ def test_manual_flush(self): -+ self.log.write("Hello") -+ -+ self.assert_writes([]) -+ -+ self.log.write(" world\nHere for a while...\nGoodbye") -+ self.assert_writes([b"Hello world\n", b"Here for a while...\n"]) -+ -+ self.log.write(" world\nHello again") -+ self.assert_writes([b"Goodbye world\n"]) -+ -+ self.log.flush() -+ self.assert_writes([b"Hello again"]) -+ -+ def test_non_ascii(self): -+ # Spanish -+ self.log.write("ol\u00e9\n") -+ self.assert_writes([b"ol\xc3\xa9\n"]) -+ -+ # Chinese -+ self.log.write("\u4e2d\u6587\n") -+ self.assert_writes([b"\xe4\xb8\xad\xe6\x96\x87\n"]) -+ -+ # Printing Non-BMP emoji -+ self.log.write("\U0001f600\n") -+ self.assert_writes([b"\xf0\x9f\x98\x80\n"]) -+ -+ # Non-encodable surrogates are replaced -+ self.log.write("\ud800\udc00\n") -+ self.assert_writes([b"??\n"]) -+ -+ def test_modified_null(self): -+ # Null characters are logged using "modified UTF-8". -+ self.log.write("\u0000\n") -+ self.assert_writes([b"\xc0\x80\n"]) -+ self.log.write("a\u0000\n") -+ self.assert_writes([b"a\xc0\x80\n"]) -+ self.log.write("\u0000b\n") -+ self.assert_writes([b"\xc0\x80b\n"]) -+ self.log.write("a\u0000b\n") -+ self.assert_writes([b"a\xc0\x80b\n"]) -+ -+ def test_nonstandard_str(self): -+ # String subclasses are accepted, but they should be converted -+ # to a standard str without calling any of their methods. -+ class CustomStr(str): -+ def splitlines(self, *args, **kwargs): -+ raise AssertionError() -+ -+ def __len__(self): -+ raise AssertionError() -+ -+ def __str__(self): -+ raise AssertionError() -+ -+ self.log.write(CustomStr("custom\n")) -+ self.assert_writes([b"custom\n"]) -+ -+ def test_non_str(self): -+ # Non-string classes are not accepted. -+ for obj in [b"", b"hello", None, 42]: -+ with self.subTest(obj=obj): -+ with self.assertRaisesRegex( -+ TypeError, -+ fr"write\(\) argument must be str, not " -+ fr"{type(obj).__name__}" -+ ): -+ self.log.write(obj) -+ -+ def test_byteslike_in_buffer(self): -+ # The underlying buffer *can* accept bytes-like objects -+ self.log.buffer.write(bytearray(b"hello")) -+ self.log.flush() -+ -+ self.log.buffer.write(b"") -+ self.log.flush() -+ -+ self.log.buffer.write(b"goodbye") -+ self.log.flush() -+ -+ self.assert_writes([b"hello", b"goodbye"]) -+ -+ def test_non_byteslike_in_buffer(self): -+ for obj in ["hello", None, 42]: -+ with self.subTest(obj=obj): -+ with self.assertRaisesRegex( -+ TypeError, -+ fr"write\(\) argument must be bytes-like, not " -+ fr"{type(obj).__name__}" -+ ): -+ self.log.buffer.write(obj) -diff --git a/Lib/test/test_capi/test_config.py b/Lib/test/test_capi/test_config.py -index 77730ad2f32..a3179efe4a8 100644 ---- a/Lib/test/test_capi/test_config.py -+++ b/Lib/test/test_capi/test_config.py -@@ -110,6 +110,10 @@ - options.extend(( - ("_pystats", bool, None), - )) -+ if support.is_apple: -+ options.extend(( -+ ("use_system_logger", bool, None), -+ )) - - for name, option_type, sys_attr in options: - with self.subTest(name=name, option_type=option_type, -diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py -index bf861ef06ee..468f821370e 100644 ---- a/Lib/test/test_embed.py -+++ b/Lib/test/test_embed.py -@@ -649,6 +649,8 @@ - CONFIG_COMPAT.update({ - 'legacy_windows_stdio': False, - }) -+ if support.is_apple: -+ CONFIG_COMPAT['use_system_logger'] = False - - CONFIG_PYTHON = dict(CONFIG_COMPAT, - _config_init=API_PYTHON, -diff --git a/Makefile.pre.in b/Makefile.pre.in -index 8d94ba361fd..6c1c95d4dd9 100644 ---- a/Makefile.pre.in -+++ b/Makefile.pre.in -@@ -2132,7 +2132,6 @@ - # This must be run *after* a `make install` has completed the build. The - # `--with-framework-name` argument *cannot* be used when configuring the build. - XCFOLDER:=iOSTestbed.$(MULTIARCH).$(shell date +%s) --XCRESULT=$(XCFOLDER)/$(MULTIARCH).xcresult - .PHONY: testios - testios: - @if test "$(MACHDEP)" != "ios"; then \ -@@ -2151,29 +2150,12 @@ - echo "Cannot find a finalized iOS Python.framework. Have you run 'make install' to finalize the framework build?"; \ - exit 1;\ - fi -- # Copy the testbed project into the build folder -- cp -r $(srcdir)/iOS/testbed $(XCFOLDER) -- # Copy the framework from the install location to the testbed project. -- cp -r $(PYTHONFRAMEWORKPREFIX)/* $(XCFOLDER)/Python.xcframework/ios-arm64_x86_64-simulator -- -- # Run the test suite for the Xcode project, targeting the iOS simulator. -- # If the suite fails, touch a file in the test folder as a marker -- if ! xcodebuild test -project $(XCFOLDER)/iOSTestbed.xcodeproj -scheme "iOSTestbed" -destination "platform=iOS Simulator,name=iPhone SE (3rd Generation)" -resultBundlePath $(XCRESULT) -derivedDataPath $(XCFOLDER)/DerivedData ; then \ -- touch $(XCFOLDER)/failed; \ -- fi - -- # Regardless of success or failure, extract and print the test output -- xcrun xcresulttool get --path $(XCRESULT) \ -- --id $$( \ -- xcrun xcresulttool get --path $(XCRESULT) --format json | \ -- $(PYTHON_FOR_BUILD) -c "import sys, json; result = json.load(sys.stdin); print(result['actions']['_values'][0]['actionResult']['logRef']['id']['_value'])" \ -- ) \ -- --format json | \ -- $(PYTHON_FOR_BUILD) -c "import sys, json; result = json.load(sys.stdin); print(result['subsections']['_values'][1]['subsections']['_values'][0]['emittedOutput']['_value'])" -+ # Clone the testbed project into the XCFOLDER -+ $(PYTHON_FOR_BUILD) $(srcdir)/iOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER)" - -- @if test -e $(XCFOLDER)/failed ; then \ -- exit 1; \ -- fi -+ # Run the testbed project -+ $(PYTHON_FOR_BUILD) "$(XCFOLDER)" run --verbose -- test -uall --single-process --rerun -W - - # Like test, but using --slow-ci which enables all test resources and use - # longer timeout. Run an optional pybuildbot.identify script to include diff --git a/Misc/platform_triplet.c b/Misc/platform_triplet.c index ec0857a4a99..2350e9dc821 100644 --- a/Misc/platform_triplet.c @@ -588,192 +162,11 @@ index ec0857a4a99..2350e9dc821 100644 // Older macOS SDKs do not define TARGET_OS_OSX # elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX PLATFORM_TRIPLET=darwin -diff --git a/Python/initconfig.c b/Python/initconfig.c -index 438f8a5c1cf..7851b86db1f 100644 ---- a/Python/initconfig.c -+++ b/Python/initconfig.c -@@ -168,6 +168,9 @@ - SPEC(tracemalloc, UINT, READ_ONLY, NO_SYS), - SPEC(use_frozen_modules, BOOL, READ_ONLY, NO_SYS), - SPEC(use_hash_seed, BOOL, READ_ONLY, NO_SYS), -+#ifdef __APPLE__ -+ SPEC(use_system_logger, BOOL, PUBLIC, NO_SYS), -+#endif - SPEC(user_site_directory, BOOL, READ_ONLY, NO_SYS), // sys.flags.no_user_site - SPEC(warn_default_encoding, BOOL, READ_ONLY, NO_SYS), - -@@ -884,6 +887,9 @@ - assert(config->cpu_count != 0); - // config->use_frozen_modules is initialized later - // by _PyConfig_InitImportConfig(). -+#ifdef __APPLE__ -+ assert(config->use_system_logger >= 0); -+#endif - #ifdef Py_STATS - assert(config->_pystats >= 0); - #endif -@@ -986,6 +992,9 @@ - config->_is_python_build = 0; - config->code_debug_ranges = 1; - config->cpu_count = -1; -+#ifdef __APPLE__ -+ config->use_system_logger = 0; -+#endif - #ifdef Py_GIL_DISABLED - config->enable_gil = _PyConfig_GIL_DEFAULT; - config->tlbc_enabled = 1; -@@ -1015,6 +1024,9 @@ - #ifdef MS_WINDOWS - config->legacy_windows_stdio = 0; - #endif -+#ifdef __APPLE__ -+ config->use_system_logger = 0; -+#endif - } - - -@@ -1049,6 +1061,9 @@ - #ifdef MS_WINDOWS - config->legacy_windows_stdio = 0; - #endif -+#ifdef __APPLE__ -+ config->use_system_logger = 0; -+#endif - } - - -diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c -index 23882d08384..64985527606 100644 ---- a/Python/pylifecycle.c -+++ b/Python/pylifecycle.c -@@ -45,7 +45,9 @@ - #endif - - #if defined(__APPLE__) -+# include - # include -+# include - #endif - - #ifdef HAVE_SIGNAL_H -@@ -75,6 +77,9 @@ - #ifdef __ANDROID__ - static PyStatus init_android_streams(PyThreadState *tstate); - #endif -+#if defined(__APPLE__) -+static PyStatus init_apple_streams(PyThreadState *tstate); -+#endif - static void wait_for_thread_shutdown(PyThreadState *tstate); - static void finalize_subinterpreters(void); - static void call_ll_exitfuncs(_PyRuntimeState *runtime); -@@ -1257,6 +1262,14 @@ - return status; - } - #endif -+#if defined(__APPLE__) -+ if (config->use_system_logger) { -+ status = init_apple_streams(tstate); -+ if (_PyStatus_EXCEPTION(status)) { -+ return status; -+ } -+ } -+#endif - - #ifdef Py_DEBUG - run_presite(tstate); -@@ -2931,6 +2944,75 @@ - - #endif // __ANDROID__ - -+#if defined(__APPLE__) -+ -+static PyObject * -+apple_log_write_impl(PyObject *self, PyObject *args) -+{ -+ int logtype = 0; -+ const char *text = NULL; -+ if (!PyArg_ParseTuple(args, "iy", &logtype, &text)) { -+ return NULL; -+ } -+ -+ // Call the underlying Apple logging API. The os_log unified logging APIs -+ // were introduced in macOS 10.12, iOS 10.0, tvOS 10.0, and watchOS 3.0; -+ // this call is a no-op on older versions. -+ #if TARGET_OS_IPHONE || (TARGET_OS_OSX && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) -+ // Pass the user-provided text through explicit %s formatting -+ // to avoid % literals being interpreted as a formatting directive. -+ os_log_with_type(OS_LOG_DEFAULT, logtype, "%s", text); -+ #endif -+ Py_RETURN_NONE; -+} -+ -+ -+static PyMethodDef apple_log_write_method = { -+ "apple_log_write", apple_log_write_impl, METH_VARARGS -+}; -+ -+ -+static PyStatus -+init_apple_streams(PyThreadState *tstate) -+{ -+ PyStatus status = _PyStatus_OK(); -+ PyObject *_apple_support = NULL; -+ PyObject *apple_log_write = NULL; -+ PyObject *result = NULL; -+ -+ _apple_support = PyImport_ImportModule("_apple_support"); -+ if (_apple_support == NULL) { -+ goto error; -+ } -+ -+ apple_log_write = PyCFunction_New(&apple_log_write_method, NULL); -+ if (apple_log_write == NULL) { -+ goto error; -+ } -+ -+ // Initialize the logging streams, sending stdout -> Default; stderr -> Error -+ result = PyObject_CallMethod( -+ _apple_support, "init_streams", "Oii", -+ apple_log_write, OS_LOG_TYPE_DEFAULT, OS_LOG_TYPE_ERROR); -+ if (result == NULL) { -+ goto error; -+ } -+ -+ goto done; -+ -+error: -+ _PyErr_Print(tstate); -+ status = _PyStatus_ERR("failed to initialize Apple log streams"); -+ -+done: -+ Py_XDECREF(result); -+ Py_XDECREF(apple_log_write); -+ Py_XDECREF(_apple_support); -+ return status; -+} -+ -+#endif // __APPLE__ -+ - - static void - _Py_FatalError_DumpTracebacks(int fd, PyInterpreterState *interp, -diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h -index c8cdb933bb1..584b050fc4b 100644 ---- a/Python/stdlib_module_names.h -+++ b/Python/stdlib_module_names.h -@@ -6,6 +6,7 @@ - "_abc", - "_aix_support", - "_android_support", -+"_apple_support", - "_ast", - "_asyncio", - "_bisect", diff --git a/configure b/configure -index 5b44a3d6992..83803f12853 100755 +index 57be576e3ca..6d4ef3d0e01 100755 --- a/configure +++ b/configure -@@ -979,6 +979,8 @@ +@@ -980,6 +980,8 @@ CFLAGS CC HAS_XCRUN @@ -782,7 +175,7 @@ index 5b44a3d6992..83803f12853 100755 IPHONEOS_DEPLOYMENT_TARGET EXPORT_MACOSX_DEPLOYMENT_TARGET CONFIGURE_MACOSX_DEPLOYMENT_TARGET -@@ -4053,6 +4055,12 @@ +@@ -4052,6 +4054,12 @@ *-apple-ios*) ac_sys_system=iOS ;; @@ -795,7 +188,7 @@ index 5b44a3d6992..83803f12853 100755 *-*-vxworks*) ac_sys_system=VxWorks ;; -@@ -4130,7 +4138,7 @@ +@@ -4129,7 +4137,7 @@ # On cross-compile builds, configure will look for a host-specific compiler by # prepending the user-provided host triple to the required binary name. # @@ -804,7 +197,7 @@ index 5b44a3d6992..83803f12853 100755 # which isn't a binary that exists, and isn't very convenient, as it contains the # iOS version. As the default cross-compiler name won't exist, configure falls # back to gcc, which *definitely* won't work. We're providing wrapper scripts for -@@ -4145,6 +4153,14 @@ +@@ -4144,6 +4152,14 @@ aarch64-apple-ios*-simulator) AR=arm64-apple-ios-simulator-ar ;; aarch64-apple-ios*) AR=arm64-apple-ios-ar ;; x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;; @@ -819,7 +212,7 @@ index 5b44a3d6992..83803f12853 100755 *) esac fi -@@ -4153,6 +4169,14 @@ +@@ -4152,6 +4168,14 @@ aarch64-apple-ios*-simulator) CC=arm64-apple-ios-simulator-clang ;; aarch64-apple-ios*) CC=arm64-apple-ios-clang ;; x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;; @@ -834,7 +227,7 @@ index 5b44a3d6992..83803f12853 100755 *) esac fi -@@ -4161,6 +4185,14 @@ +@@ -4160,6 +4184,14 @@ aarch64-apple-ios*-simulator) CPP=arm64-apple-ios-simulator-cpp ;; aarch64-apple-ios*) CPP=arm64-apple-ios-cpp ;; x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;; @@ -849,7 +242,7 @@ index 5b44a3d6992..83803f12853 100755 *) esac fi -@@ -4169,6 +4201,14 @@ +@@ -4168,6 +4200,14 @@ aarch64-apple-ios*-simulator) CXX=arm64-apple-ios-simulator-clang++ ;; aarch64-apple-ios*) CXX=arm64-apple-ios-clang++ ;; x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;; @@ -864,7 +257,7 @@ index 5b44a3d6992..83803f12853 100755 *) esac fi -@@ -4289,8 +4329,10 @@ +@@ -4288,8 +4328,10 @@ case $enableval in yes) case $ac_sys_system in @@ -877,7 +270,7 @@ index 5b44a3d6992..83803f12853 100755 *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 esac esac -@@ -4299,6 +4341,8 @@ +@@ -4298,6 +4340,8 @@ no) case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -886,7 +279,7 @@ index 5b44a3d6992..83803f12853 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4405,6 +4449,36 @@ +@@ -4404,6 +4448,36 @@ ac_config_files="$ac_config_files iOS/Resources/Info.plist" @@ -923,7 +316,7 @@ index 5b44a3d6992..83803f12853 100755 ;; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 -@@ -4416,6 +4490,8 @@ +@@ -4415,6 +4489,8 @@ case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -932,7 +325,7 @@ index 5b44a3d6992..83803f12853 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4469,8 +4545,8 @@ +@@ -4468,8 +4544,8 @@ case "$withval" in yes) case $ac_sys_system in @@ -943,7 +336,7 @@ index 5b44a3d6992..83803f12853 100755 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" ;; *) as_fn_error $? "no default app store compliance patch available for $ac_sys_system" "$LINENO" 5 ;; -@@ -4488,8 +4564,8 @@ +@@ -4487,8 +4563,8 @@ else $as_nop case $ac_sys_system in @@ -954,7 +347,7 @@ index 5b44a3d6992..83803f12853 100755 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: applying default app store compliance patch" >&5 printf "%s\n" "applying default app store compliance patch" >&6; } -@@ -4543,6 +4619,50 @@ +@@ -4542,6 +4618,50 @@ ;; esac ;; @@ -1005,7 +398,7 @@ index 5b44a3d6992..83803f12853 100755 *-*-vxworks*) _host_ident=$host_cpu ;; -@@ -4621,9 +4741,13 @@ +@@ -4620,9 +4740,13 @@ define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; @@ -1020,7 +413,7 @@ index 5b44a3d6992..83803f12853 100755 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -4686,7 +4810,10 @@ +@@ -4685,7 +4809,10 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' @@ -1032,7 +425,7 @@ index 5b44a3d6992..83803f12853 100755 # checks for alternative programs -@@ -4727,6 +4854,16 @@ +@@ -4726,6 +4853,16 @@ as_fn_append CFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" as_fn_append LDFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" ;; #( @@ -1049,7 +442,7 @@ index 5b44a3d6992..83803f12853 100755 *) : ;; esac -@@ -7031,6 +7168,10 @@ +@@ -7030,6 +7167,10 @@ MULTIARCH="" ;; #( iOS) : MULTIARCH="" ;; #( @@ -1060,7 +453,7 @@ index 5b44a3d6992..83803f12853 100755 FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -7051,7 +7192,7 @@ +@@ -7050,7 +7191,7 @@ printf "%s\n" "$MULTIARCH" >&6; } case $ac_sys_system in #( @@ -1069,7 +462,7 @@ index 5b44a3d6992..83803f12853 100755 SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2` ;; #( *) : SOABI_PLATFORM=$PLATFORM_TRIPLET -@@ -7102,6 +7243,14 @@ +@@ -7101,6 +7242,14 @@ PY_SUPPORT_TIER=3 ;; #( aarch64-apple-ios*/clang) : PY_SUPPORT_TIER=3 ;; #( @@ -1084,7 +477,7 @@ index 5b44a3d6992..83803f12853 100755 aarch64-*-linux-android/clang) : PY_SUPPORT_TIER=3 ;; #( x86_64-*-linux-android/clang) : -@@ -7531,7 +7680,7 @@ +@@ -7530,7 +7679,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; @@ -1093,7 +486,7 @@ index 5b44a3d6992..83803f12853 100755 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5;; -@@ -7597,7 +7746,7 @@ +@@ -7596,7 +7745,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -1102,7 +495,7 @@ index 5b44a3d6992..83803f12853 100755 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -13150,7 +13299,7 @@ +@@ -13160,7 +13309,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -1111,7 +504,7 @@ index 5b44a3d6992..83803f12853 100755 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -13283,7 +13432,7 @@ +@@ -13293,7 +13442,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -1120,7 +513,7 @@ index 5b44a3d6992..83803f12853 100755 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -13307,7 +13456,7 @@ +@@ -13317,7 +13466,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -1129,7 +522,7 @@ index 5b44a3d6992..83803f12853 100755 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -14759,7 +14908,7 @@ +@@ -14769,7 +14918,7 @@ ctypes_malloc_closure=yes ;; #( @@ -1138,7 +531,7 @@ index 5b44a3d6992..83803f12853 100755 ctypes_malloc_closure=yes ;; #( -@@ -18262,12 +18411,6 @@ +@@ -18272,12 +18421,6 @@ then : printf "%s\n" "#define HAVE_DUP3 1" >>confdefs.h @@ -1151,7 +544,7 @@ index 5b44a3d6992..83803f12853 100755 fi ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes -@@ -18328,18 +18471,6 @@ +@@ -18338,18 +18481,6 @@ then : printf "%s\n" "#define HAVE_FEXECVE 1" >>confdefs.h @@ -1170,7 +563,7 @@ index 5b44a3d6992..83803f12853 100755 fi ac_fn_c_check_func "$LINENO" "fpathconf" "ac_cv_func_fpathconf" if test "x$ac_cv_func_fpathconf" = xyes -@@ -18766,24 +18897,6 @@ +@@ -18776,24 +18907,6 @@ then : printf "%s\n" "#define HAVE_POSIX_OPENPT 1" >>confdefs.h @@ -1195,7 +588,7 @@ index 5b44a3d6992..83803f12853 100755 fi ac_fn_c_check_func "$LINENO" "pread" "ac_cv_func_pread" if test "x$ac_cv_func_pread" = xyes -@@ -19072,12 +19185,6 @@ +@@ -19094,12 +19207,6 @@ then : printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h @@ -1208,7 +601,7 @@ index 5b44a3d6992..83803f12853 100755 fi ac_fn_c_check_func "$LINENO" "sigfillset" "ac_cv_func_sigfillset" if test "x$ac_cv_func_sigfillset" = xyes -@@ -19346,11 +19453,11 @@ +@@ -19368,11 +19475,11 @@ fi @@ -1222,7 +615,7 @@ index 5b44a3d6992..83803f12853 100755 ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" if test "x$ac_cv_func_getentropy" = xyes then : -@@ -19372,6 +19479,53 @@ +@@ -19394,6 +19501,53 @@ fi @@ -1276,7 +669,7 @@ index 5b44a3d6992..83803f12853 100755 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5 printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; } if test ${ac_cv_c_undeclared_builtin_options+y} -@@ -22247,7 +22401,8 @@ +@@ -22269,7 +22423,8 @@ # check for openpty, login_tty, and forkpty @@ -1286,7 +679,7 @@ index 5b44a3d6992..83803f12853 100755 for ac_func in openpty do : -@@ -22343,7 +22498,7 @@ +@@ -22365,7 +22520,7 @@ fi done @@ -1295,7 +688,7 @@ index 5b44a3d6992..83803f12853 100755 printf %s "checking for library containing login_tty... " >&6; } if test ${ac_cv_search_login_tty+y} then : -@@ -22500,6 +22655,7 @@ +@@ -22522,6 +22677,7 @@ fi done @@ -1303,7 +696,7 @@ index 5b44a3d6992..83803f12853 100755 # check for long file support functions ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" -@@ -22746,10 +22902,10 @@ +@@ -22768,10 +22924,10 @@ done @@ -1316,7 +709,7 @@ index 5b44a3d6992..83803f12853 100755 then for ac_func in clock_settime -@@ -24977,8 +25133,8 @@ +@@ -24999,8 +25155,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -1327,7 +720,7 @@ index 5b44a3d6992..83803f12853 100755 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -27730,7 +27886,7 @@ +@@ -27752,7 +27908,7 @@ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for device files" >&5 printf "%s\n" "$as_me: checking for device files" >&6;} @@ -1336,7 +729,7 @@ index 5b44a3d6992..83803f12853 100755 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -28162,7 +28318,7 @@ +@@ -28184,7 +28340,7 @@ with_ensurepip=no ;; #( WASI) : with_ensurepip=no ;; #( @@ -1345,7 +738,7 @@ index 5b44a3d6992..83803f12853 100755 with_ensurepip=no ;; #( *) : with_ensurepip=upgrade -@@ -29091,7 +29247,7 @@ +@@ -29130,7 +29286,7 @@ ;; #( Darwin) : ;; #( @@ -1354,7 +747,7 @@ index 5b44a3d6992..83803f12853 100755 -@@ -32989,6 +33145,8 @@ +@@ -33028,6 +33184,8 @@ "Mac/Resources/framework/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/framework/Info.plist" ;; "Mac/Resources/app/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/app/Info.plist" ;; "iOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES iOS/Resources/Info.plist" ;; @@ -1364,7 +757,7 @@ index 5b44a3d6992..83803f12853 100755 "Misc/python.pc") CONFIG_FILES="$CONFIG_FILES Misc/python.pc" ;; "Misc/python-embed.pc") CONFIG_FILES="$CONFIG_FILES Misc/python-embed.pc" ;; diff --git a/configure.ac b/configure.ac -index 7904f8990c4..f2367cd47d3 100644 +index bd0221481c5..6dca265f3cc 100644 --- a/configure.ac +++ b/configure.ac @@ -330,6 +330,12 @@ @@ -1678,7 +1071,7 @@ index 7904f8990c4..f2367cd47d3 100644 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -3407,7 +3538,7 @@ +@@ -3412,7 +3543,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -1687,7 +1080,7 @@ index 7904f8990c4..f2367cd47d3 100644 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -3531,7 +3662,7 @@ +@@ -3536,7 +3667,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -1696,7 +1089,7 @@ index 7904f8990c4..f2367cd47d3 100644 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -3555,7 +3686,7 @@ +@@ -3560,7 +3691,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -1705,7 +1098,7 @@ index 7904f8990c4..f2367cd47d3 100644 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -3975,7 +4106,7 @@ +@@ -3980,7 +4111,7 @@ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], @@ -1714,7 +1107,7 @@ index 7904f8990c4..f2367cd47d3 100644 ctypes_malloc_closure=yes ], [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] -@@ -5093,9 +5224,9 @@ +@@ -5098,9 +5229,9 @@ # checks for library functions AC_CHECK_FUNCS([ \ accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ @@ -1726,16 +1119,17 @@ index 7904f8990c4..f2367cd47d3 100644 gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \ getgrnam_r getgrouplist gethostname getitimer getloadavg getlogin \ getpeername getpgid getpid getppid getpriority _getpty \ -@@ -5103,15 +5234,14 @@ +@@ -5108,8 +5239,7 @@ getspnam getuid getwd grantpt if_nameindex initgroups kill killpg lchown linkat \ lockf lstat lutimes madvise mbrtowc memrchr mkdirat mkfifo mkfifoat \ mknod mknodat mktime mmap mremap nice openat opendir pathconf pause pipe \ - pipe2 plock poll posix_fadvise posix_fallocate posix_openpt posix_spawn posix_spawnp \ - posix_spawn_file_actions_addclosefrom_np \ + pipe2 plock poll posix_fadvise posix_fallocate posix_openpt \ - pread preadv preadv2 process_vm_readv pthread_cond_timedwait_relative_np pthread_condattr_setclock pthread_init \ - pthread_kill ptsname ptsname_r pwrite pwritev pwritev2 readlink readlinkat readv realpath renameat \ - rtpSpawn sched_get_priority_max sched_rr_get_interval sched_setaffinity \ + pread preadv preadv2 process_vm_readv \ + pthread_cond_timedwait_relative_np pthread_condattr_setclock pthread_init \ + pthread_kill pthread_getname_np pthread_setname_np \ +@@ -5118,7 +5248,7 @@ sched_setparam sched_setscheduler sem_clockwait sem_getvalue sem_open \ sem_timedwait sem_unlink sendfile setegid seteuid setgid sethostname \ setitimer setlocale setpgid setpgrp setpriority setregid setresgid \ @@ -1744,7 +1138,7 @@ index 7904f8990c4..f2367cd47d3 100644 sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ -@@ -5126,12 +5256,20 @@ +@@ -5133,12 +5263,20 @@ AC_CHECK_FUNCS([lchmod]) fi @@ -1768,7 +1162,7 @@ index 7904f8990c4..f2367cd47d3 100644 fi AC_CHECK_DECL([dirfd], -@@ -5385,20 +5523,22 @@ +@@ -5392,20 +5530,22 @@ ]) # check for openpty, login_tty, and forkpty @@ -1805,7 +1199,7 @@ index 7904f8990c4..f2367cd47d3 100644 # check for long file support functions AC_CHECK_FUNCS([fseek64 fseeko fstatvfs ftell64 ftello statvfs]) -@@ -5437,10 +5577,10 @@ +@@ -5444,10 +5584,10 @@ ]) ]) @@ -1818,7 +1212,7 @@ index 7904f8990c4..f2367cd47d3 100644 then AC_CHECK_FUNCS([clock_settime], [], [ AC_CHECK_LIB([rt], [clock_settime], [ -@@ -6191,8 +6331,8 @@ +@@ -6198,8 +6338,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -1829,7 +1223,7 @@ index 7904f8990c4..f2367cd47d3 100644 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -6856,7 +6996,7 @@ +@@ -6863,7 +7003,7 @@ dnl NOTE: Inform user how to proceed with files when cross compiling. dnl Some cross-compile builds are predictable; they won't ever dnl have /dev/ptmx or /dev/ptc, so we can set them explicitly. @@ -1838,7 +1232,7 @@ index 7904f8990c4..f2367cd47d3 100644 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -7112,7 +7252,7 @@ +@@ -7119,7 +7259,7 @@ AS_CASE([$ac_sys_system], [Emscripten], [with_ensurepip=no], [WASI], [with_ensurepip=no], @@ -1847,7 +1241,7 @@ index 7904f8990c4..f2367cd47d3 100644 [with_ensurepip=upgrade] ) ]) -@@ -7506,7 +7646,7 @@ +@@ -7529,7 +7669,7 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], @@ -1856,85 +1250,6 @@ index 7904f8990c4..f2367cd47d3 100644 dnl subprocess and multiprocessing are not supported (no fork syscall). dnl curses and tkinter user interface are not available. dnl gdbm and nis aren't available -diff --git a/iOS/README.rst b/iOS/README.rst -index e33455eef8f..13b88514493 100644 ---- a/iOS/README.rst -+++ b/iOS/README.rst -@@ -285,52 +285,42 @@ - * Install the Python iOS framework into the copy of the testbed project; and - * Run the test suite on an "iPhone SE (3rd generation)" simulator. - --While the test suite is running, Xcode does not display any console output. --After showing some Xcode build commands, the console output will print ``Testing --started``, and then appear to stop. It will remain in this state until the test --suite completes. On a 2022 M1 MacBook Pro, the test suite takes approximately 12 --minutes to run; a couple of extra minutes is required to boot and prepare the --iOS simulator. -- - On success, the test suite will exit and report successful completion of the --test suite. No output of the Python test suite will be displayed. -- --On failure, the output of the Python test suite *will* be displayed. This will --show the details of the tests that failed. -+test suite. On a 2022 M1 MacBook Pro, the test suite takes approximately 15 -+minutes to run; a couple of extra minutes is required to compile the testbed -+project, and then boot and prepare the iOS simulator. - - Debugging test failures - ----------------------- - --The easiest way to diagnose a single test failure is to open the testbed project --in Xcode and run the tests from there using the "Product > Test" menu item. -- --To test in Xcode, you must ensure the testbed project has a copy of a compiled --framework. If you've configured your build with the default install location of --``iOS/Frameworks``, you can copy from that location into the test project. To --test on an ARM64 simulator, run:: -- -- $ rm -rf iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator/* -- $ cp -r iOS/Frameworks/arm64-iphonesimulator/* iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator -+Running ``make test`` generates a standalone version of the ``iOS/testbed`` -+project, and runs the full test suite. It does this using ``iOS/testbed`` -+itself - the folder is an executable module that can be used to create and run -+a clone of the testbed project. - --To test on an x86-64 simulator, run:: -+You can generate your own standalone testbed instance by running:: - -- $ rm -rf iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator/* -- $ cp -r iOS/Frameworks/x86_64-iphonesimulator/* iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator -+ $ python iOS/testbed clone --framework iOS/Frameworks/arm64-iphonesimulator my-testbed - --To test on a physical device:: -+This invocation assumes that ``iOS/Frameworks/arm64-iphonesimulator`` is the -+path to the iOS simulator framework for your platform (ARM64 in this case); -+``my-testbed`` is the name of the folder for the new testbed clone. - -- $ rm -rf iOS/testbed/Python.xcframework/ios-arm64/* -- $ cp -r iOS/Frameworks/arm64-iphoneos/* iOS/testbed/Python.xcframework/ios-arm64 -+You can then use the ``my-testbed`` folder to run the Python test suite, -+passing in any command line arguments you may require. For example, if you're -+trying to diagnose a failure in the ``os`` module, you might run:: - --Alternatively, you can configure your build to install directly into the --testbed project. For a simulator, use:: -+ $ python my-testbed run -- test -W test_os - -- --enable-framework=$(pwd)/iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator -+This is the equivalent of running ``python -m test -W test_os`` on a desktop -+Python build. Any arguments after the ``--`` will be passed to testbed as if -+they were arguments to ``python -m`` on a desktop machine. - --For a physical device, use:: -+You can also open the testbed project in Xcode by running:: - -- --enable-framework=$(pwd)/iOS/testbed/Python.xcframework/ios-arm64 -+ $ open my-testbed/iOSTestbed.xcodeproj - -+This will allow you to use the full Xcode suite of tools for debugging. - - Testing on an iOS device - ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/iOS/Resources/Info.plist.in b/iOS/Resources/Info.plist.in index c3e261ecd9e..26ef7a95de4 100644 --- a/iOS/Resources/Info.plist.in @@ -1955,546 +1270,6 @@ index c3e261ecd9e..26ef7a95de4 100644 CFBundleSupportedPlatforms iPhoneOS -diff --git a/iOS/Resources/bin/arm64-apple-ios-ar b/iOS/Resources/bin/arm64-apple-ios-ar -index 8122332b9c1..3cf3eb21874 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-ar -+++ b/iOS/Resources/bin/arm64-apple-ios-ar -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphoneos${IOS_SDK_VERSION} ar $@ -+xcrun --sdk iphoneos${IOS_SDK_VERSION} ar "$@" -diff --git a/iOS/Resources/bin/arm64-apple-ios-clang b/iOS/Resources/bin/arm64-apple-ios-clang -index 4d525751eba..c39519cd1f8 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-clang -+++ b/iOS/Resources/bin/arm64-apple-ios-clang -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphoneos${IOS_SDK_VERSION} clang -target arm64-apple-ios $@ -+xcrun --sdk iphoneos${IOS_SDK_VERSION} clang -target arm64-apple-ios "$@" -diff --git a/iOS/Resources/bin/arm64-apple-ios-clang++ b/iOS/Resources/bin/arm64-apple-ios-clang++ -index f24bec11268..d9b12925f38 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-clang++ -+++ b/iOS/Resources/bin/arm64-apple-ios-clang++ -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphoneos${IOS_SDK_VERSION} clang++ -target arm64-apple-ios $@ -+xcrun --sdk iphoneos${IOS_SDK_VERSION} clang++ -target arm64-apple-ios "$@" -diff --git a/iOS/Resources/bin/arm64-apple-ios-cpp b/iOS/Resources/bin/arm64-apple-ios-cpp -index 891bb25bb43..24da23d3448 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-cpp -+++ b/iOS/Resources/bin/arm64-apple-ios-cpp -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphoneos${IOS_SDK_VERSION} clang -target arm64-apple-ios -E $@ -+xcrun --sdk iphoneos${IOS_SDK_VERSION} clang -target arm64-apple-ios -E "$@" -diff --git a/iOS/Resources/bin/arm64-apple-ios-simulator-ar b/iOS/Resources/bin/arm64-apple-ios-simulator-ar -index 74ed3bc6df1..b836b6db902 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-simulator-ar -+++ b/iOS/Resources/bin/arm64-apple-ios-simulator-ar -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} ar $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} ar "$@" -diff --git a/iOS/Resources/bin/arm64-apple-ios-simulator-clang b/iOS/Resources/bin/arm64-apple-ios-simulator-clang -index 32574cad284..92e8d853d6e 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-simulator-clang -+++ b/iOS/Resources/bin/arm64-apple-ios-simulator-clang -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target arm64-apple-ios-simulator $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target arm64-apple-ios-simulator "$@" -diff --git a/iOS/Resources/bin/arm64-apple-ios-simulator-clang++ b/iOS/Resources/bin/arm64-apple-ios-simulator-clang++ -index ef37d05b512..076469cc70c 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-simulator-clang++ -+++ b/iOS/Resources/bin/arm64-apple-ios-simulator-clang++ -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang++ -target arm64-apple-ios-simulator $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang++ -target arm64-apple-ios-simulator "$@" -diff --git a/iOS/Resources/bin/arm64-apple-ios-simulator-cpp b/iOS/Resources/bin/arm64-apple-ios-simulator-cpp -index 6aaf6fbe188..c57f28cee5b 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-simulator-cpp -+++ b/iOS/Resources/bin/arm64-apple-ios-simulator-cpp -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target arm64-apple-ios-simulator -E $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target arm64-apple-ios-simulator -E "$@" -diff --git a/iOS/Resources/bin/x86_64-apple-ios-simulator-ar b/iOS/Resources/bin/x86_64-apple-ios-simulator-ar -index 74ed3bc6df1..b836b6db902 100755 ---- a/iOS/Resources/bin/x86_64-apple-ios-simulator-ar -+++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-ar -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} ar $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} ar "$@" -diff --git a/iOS/Resources/bin/x86_64-apple-ios-simulator-clang b/iOS/Resources/bin/x86_64-apple-ios-simulator-clang -index bcbe91f6061..17cbe0c8a1e 100755 ---- a/iOS/Resources/bin/x86_64-apple-ios-simulator-clang -+++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-clang -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target x86_64-apple-ios-simulator $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target x86_64-apple-ios-simulator "$@" -diff --git a/iOS/Resources/bin/x86_64-apple-ios-simulator-clang++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-clang++ -index 86f03ea32bc..565d47b24c2 100755 ---- a/iOS/Resources/bin/x86_64-apple-ios-simulator-clang++ -+++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-clang++ -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang++ -target x86_64-apple-ios-simulator $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang++ -target x86_64-apple-ios-simulator "$@" -diff --git a/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp b/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp -index e6a42d9b85d..63fc8e8de2d 100755 ---- a/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp -+++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target x86_64-apple-ios-simulator -E $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target x86_64-apple-ios-simulator -E "$@" ---- /dev/null -+++ b/iOS/testbed/__main__.py -@@ -0,0 +1,395 @@ -+import argparse -+import asyncio -+import json -+import plistlib -+import shutil -+import subprocess -+import sys -+from contextlib import asynccontextmanager -+from datetime import datetime -+from pathlib import Path -+ -+ -+DECODE_ARGS = ("UTF-8", "backslashreplace") -+ -+ -+# Work around a bug involving sys.exit and TaskGroups -+# (https://github.com/python/cpython/issues/101515). -+def exit(*args): -+ raise MySystemExit(*args) -+ -+ -+class MySystemExit(Exception): -+ pass -+ -+ -+# All subprocesses are executed through this context manager so that no matter -+# what happens, they can always be cancelled from another task, and they will -+# always be cleaned up on exit. -+@asynccontextmanager -+async def async_process(*args, **kwargs): -+ process = await asyncio.create_subprocess_exec(*args, **kwargs) -+ try: -+ yield process -+ finally: -+ if process.returncode is None: -+ # Allow a reasonably long time for Xcode to clean itself up, -+ # because we don't want stale emulators left behind. -+ timeout = 10 -+ process.terminate() -+ try: -+ await asyncio.wait_for(process.wait(), timeout) -+ except TimeoutError: -+ print( -+ f"Command {args} did not terminate after {timeout} seconds " -+ f" - sending SIGKILL" -+ ) -+ process.kill() -+ -+ # Even after killing the process we must still wait for it, -+ # otherwise we'll get the warning "Exception ignored in __del__". -+ await asyncio.wait_for(process.wait(), timeout=1) -+ -+ -+async def async_check_output(*args, **kwargs): -+ async with async_process( -+ *args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs -+ ) as process: -+ stdout, stderr = await process.communicate() -+ if process.returncode == 0: -+ return stdout.decode(*DECODE_ARGS) -+ else: -+ raise subprocess.CalledProcessError( -+ process.returncode, -+ args, -+ stdout.decode(*DECODE_ARGS), -+ stderr.decode(*DECODE_ARGS), -+ ) -+ -+ -+# Return a list of UDIDs associated with booted simulators -+async def list_devices(): -+ # List the testing simulators, in JSON format -+ raw_json = await async_check_output( -+ "xcrun", "simctl", "--set", "testing", "list", "-j" -+ ) -+ json_data = json.loads(raw_json) -+ -+ # Filter out the booted iOS simulators -+ return [ -+ simulator["udid"] -+ for runtime, simulators in json_data["devices"].items() -+ for simulator in simulators -+ if runtime.split(".")[-1].startswith("iOS") and simulator["state"] == "Booted" -+ ] -+ -+ -+async def find_device(initial_devices): -+ while True: -+ new_devices = set(await list_devices()).difference(initial_devices) -+ if len(new_devices) == 0: -+ await asyncio.sleep(1) -+ elif len(new_devices) == 1: -+ udid = new_devices.pop() -+ print(f"{datetime.now():%Y-%m-%d %H:%M:%S}: New test simulator detected") -+ print(f"UDID: {udid}") -+ return udid -+ else: -+ exit(f"Found more than one new device: {new_devices}") -+ -+ -+async def log_stream_task(initial_devices): -+ # Wait up to 5 minutes for the build to complete and the simulator to boot. -+ udid = await asyncio.wait_for(find_device(initial_devices), 5 * 60) -+ -+ # Stream the iOS device's logs, filtering out messages that come from the -+ # XCTest test suite (catching NSLog messages from the test method), or -+ # Python itself (catching stdout/stderr content routed to the system log -+ # with config->use_system_logger). -+ args = [ -+ "xcrun", -+ "simctl", -+ "--set", -+ "testing", -+ "spawn", -+ udid, -+ "log", -+ "stream", -+ "--style", -+ "compact", -+ "--predicate", -+ ( -+ 'senderImagePath ENDSWITH "/iOSTestbedTests.xctest/iOSTestbedTests"' -+ ' OR senderImagePath ENDSWITH "/Python.framework/Python"' -+ ), -+ ] -+ -+ async with async_process( -+ *args, -+ stdout=subprocess.PIPE, -+ stderr=subprocess.STDOUT, -+ ) as process: -+ suppress_dupes = False -+ while line := (await process.stdout.readline()).decode(*DECODE_ARGS): -+ # The iOS log streamer can sometimes lag; when it does, it outputs -+ # a warning about messages being dropped... often multiple times. -+ # Only print the first of these duplicated warnings. -+ if line.startswith("=== Messages dropped "): -+ if not suppress_dupes: -+ suppress_dupes = True -+ sys.stdout.write(line) -+ else: -+ suppress_dupes = False -+ sys.stdout.write(line) -+ sys.stdout.flush() -+ -+ -+async def xcode_test(location, simulator, verbose): -+ # Run the test suite on the named simulator -+ print("Starting xcodebuild...") -+ args = [ -+ "xcodebuild", -+ "test", -+ "-project", -+ str(location / "iOSTestbed.xcodeproj"), -+ "-scheme", -+ "iOSTestbed", -+ "-destination", -+ f"platform=iOS Simulator,name={simulator}", -+ "-resultBundlePath", -+ str(location / f"{datetime.now():%Y%m%d-%H%M%S}.xcresult"), -+ "-derivedDataPath", -+ str(location / "DerivedData"), -+ ] -+ if not verbose: -+ args += ["-quiet"] -+ -+ async with async_process( -+ *args, -+ stdout=subprocess.PIPE, -+ stderr=subprocess.STDOUT, -+ ) as process: -+ while line := (await process.stdout.readline()).decode(*DECODE_ARGS): -+ sys.stdout.write(line) -+ sys.stdout.flush() -+ -+ status = await asyncio.wait_for(process.wait(), timeout=1) -+ exit(status) -+ -+ -+def clone_testbed( -+ source: Path, -+ target: Path, -+ framework: Path, -+ apps: list[Path], -+) -> None: -+ if target.exists(): -+ print(f"{target} already exists; aborting without creating project.") -+ sys.exit(10) -+ -+ if framework is None: -+ if not ( -+ source / "Python.xcframework/ios-arm64_x86_64-simulator/bin" -+ ).is_dir(): -+ print( -+ f"The testbed being cloned ({source}) does not contain " -+ f"a simulator framework. Re-run with --framework" -+ ) -+ sys.exit(11) -+ else: -+ if not framework.is_dir(): -+ print(f"{framework} does not exist.") -+ sys.exit(12) -+ elif not ( -+ framework.suffix == ".xcframework" -+ or (framework / "Python.framework").is_dir() -+ ): -+ print( -+ f"{framework} is not an XCframework, " -+ f"or a simulator slice of a framework build." -+ ) -+ sys.exit(13) -+ -+ print("Cloning testbed project:") -+ print(f" Cloning {source}...", end="", flush=True) -+ shutil.copytree(source, target, symlinks=True) -+ print(" done") -+ -+ if framework is not None: -+ if framework.suffix == ".xcframework": -+ print(" Installing XCFramework...", end="", flush=True) -+ xc_framework_path = (target / "Python.xcframework").resolve() -+ if xc_framework_path.is_dir(): -+ shutil.rmtree(xc_framework_path) -+ else: -+ xc_framework_path.unlink() -+ xc_framework_path.symlink_to( -+ framework.relative_to(xc_framework_path.parent, walk_up=True) -+ ) -+ print(" done") -+ else: -+ print(" Installing simulator framework...", end="", flush=True) -+ sim_framework_path = ( -+ target / "Python.xcframework" / "ios-arm64_x86_64-simulator" -+ ).resolve() -+ if sim_framework_path.is_dir(): -+ shutil.rmtree(sim_framework_path) -+ else: -+ sim_framework_path.unlink() -+ sim_framework_path.symlink_to( -+ framework.relative_to(sim_framework_path.parent, walk_up=True) -+ ) -+ print(" done") -+ else: -+ print(" Using pre-existing iOS framework.") -+ -+ for app_src in apps: -+ print(f" Installing app {app_src.name!r}...", end="", flush=True) -+ app_target = target / f"iOSTestbed/app/{app_src.name}" -+ if app_target.is_dir(): -+ shutil.rmtree(app_target) -+ shutil.copytree(app_src, app_target) -+ print(" done") -+ -+ print(f"Successfully cloned testbed: {target.resolve()}") -+ -+ -+def update_plist(testbed_path, args): -+ # Add the test runner arguments to the testbed's Info.plist file. -+ info_plist = testbed_path / "iOSTestbed" / "iOSTestbed-Info.plist" -+ with info_plist.open("rb") as f: -+ info = plistlib.load(f) -+ -+ info["TestArgs"] = args -+ -+ with info_plist.open("wb") as f: -+ plistlib.dump(info, f) -+ -+ -+async def run_testbed(simulator: str, args: list[str], verbose: bool=False): -+ location = Path(__file__).parent -+ print("Updating plist...", end="", flush=True) -+ update_plist(location, args) -+ print(" done.") -+ -+ # Get the list of devices that are booted at the start of the test run. -+ # The simulator started by the test suite will be detected as the new -+ # entry that appears on the device list. -+ initial_devices = await list_devices() -+ -+ try: -+ async with asyncio.TaskGroup() as tg: -+ tg.create_task(log_stream_task(initial_devices)) -+ tg.create_task(xcode_test(location, simulator=simulator, verbose=verbose)) -+ except* MySystemExit as e: -+ raise SystemExit(*e.exceptions[0].args) from None -+ except* subprocess.CalledProcessError as e: -+ # Extract it from the ExceptionGroup so it can be handled by `main`. -+ raise e.exceptions[0] -+ -+ -+def main(): -+ parser = argparse.ArgumentParser( -+ description=( -+ "Manages the process of testing a Python project in the iOS simulator." -+ ), -+ ) -+ -+ subcommands = parser.add_subparsers(dest="subcommand") -+ -+ clone = subcommands.add_parser( -+ "clone", -+ description=( -+ "Clone the testbed project, copying in an iOS Python framework and" -+ "any specified application code." -+ ), -+ help="Clone a testbed project to a new location.", -+ ) -+ clone.add_argument( -+ "--framework", -+ help=( -+ "The location of the XCFramework (or simulator-only slice of an " -+ "XCFramework) to use when running the testbed" -+ ), -+ ) -+ clone.add_argument( -+ "--app", -+ dest="apps", -+ action="append", -+ default=[], -+ help="The location of any code to include in the testbed project", -+ ) -+ clone.add_argument( -+ "location", -+ help="The path where the testbed will be cloned.", -+ ) -+ -+ run = subcommands.add_parser( -+ "run", -+ usage="%(prog)s [-h] [--simulator SIMULATOR] -- [ ...]", -+ description=( -+ "Run a testbed project. The arguments provided after `--` will be " -+ "passed to the running iOS process as if they were arguments to " -+ "`python -m`." -+ ), -+ help="Run a testbed project", -+ ) -+ run.add_argument( -+ "--simulator", -+ default="iPhone SE (3rd Generation)", -+ help="The name of the simulator to use (default: 'iPhone SE (3rd Generation)')", -+ ) -+ run.add_argument( -+ "-v", "--verbose", -+ action="store_true", -+ help="Enable verbose output", -+ ) -+ -+ try: -+ pos = sys.argv.index("--") -+ testbed_args = sys.argv[1:pos] -+ test_args = sys.argv[pos + 1 :] -+ except ValueError: -+ testbed_args = sys.argv[1:] -+ test_args = [] -+ -+ context = parser.parse_args(testbed_args) -+ -+ if context.subcommand == "clone": -+ clone_testbed( -+ source=Path(__file__).parent, -+ target=Path(context.location), -+ framework=Path(context.framework).resolve() if context.framework else None, -+ apps=[Path(app) for app in context.apps], -+ ) -+ elif context.subcommand == "run": -+ if test_args: -+ if not ( -+ Path(__file__).parent / "Python.xcframework/ios-arm64_x86_64-simulator/bin" -+ ).is_dir(): -+ print( -+ f"Testbed does not contain a compiled iOS framework. Use " -+ f"`python {sys.argv[0]} clone ...` to create a runnable " -+ f"clone of this testbed." -+ ) -+ sys.exit(20) -+ -+ asyncio.run( -+ run_testbed( -+ simulator=context.simulator, -+ verbose=context.verbose, -+ args=test_args, -+ ) -+ ) -+ else: -+ print(f"Must specify test arguments (e.g., {sys.argv[0]} run -- test)") -+ print() -+ parser.print_help(sys.stderr) -+ sys.exit(21) -+ else: -+ parser.print_help(sys.stderr) -+ sys.exit(1) -+ -+ -+if __name__ == "__main__": -+ main() -diff --git a/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj b/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj -index 6819ac0eeed..c7d63909ee2 100644 ---- a/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj -+++ b/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj -@@ -263,6 +263,7 @@ - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "set -e\n\nmkdir -p \"$CODESIGNING_FOLDER_PATH/python/lib\"\nif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-iphonesimulator\" ]; then\n echo \"Installing Python modules for iOS Simulator\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64_x86_64-simulator/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nelse\n echo \"Installing Python modules for iOS Device\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nfi\n"; -+ showEnvVarsInLog = 0; - }; - 607A66562B0F06200010BFC8 /* Prepare Python Binary Modules */ = { - isa = PBXShellScriptBuildPhase; -@@ -282,6 +283,7 @@ - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "set -e\n\ninstall_dylib () {\n INSTALL_BASE=$1\n FULL_EXT=$2\n\n # The name of the extension file\n EXT=$(basename \"$FULL_EXT\")\n # The location of the extension file, relative to the bundle\n RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/} \n # The path to the extension file, relative to the install base\n PYTHON_EXT=${RELATIVE_EXT/$INSTALL_BASE/}\n # The full dotted name of the extension module, constructed from the file path.\n FULL_MODULE_NAME=$(echo $PYTHON_EXT | cut -d \".\" -f 1 | tr \"/\" \".\"); \n # A bundle identifier; not actually used, but required by Xcode framework packaging\n FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr \"_\" \"-\")\n # The name of the framework folder.\n FRAMEWORK_FOLDER=\"Frameworks/$FULL_MODULE_NAME.framework\"\n\n # If the framework folder doesn't exist, create it.\n if [ ! -d \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\" ]; then\n echo \"Creating framework for $RELATIVE_EXT\" \n mkdir -p \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\"\n cp \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleExecutable -string \"$FULL_MODULE_NAME\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleIdentifier -string \"$FRAMEWORK_BUNDLE_ID\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n fi\n \n echo \"Installing binary for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" \n mv \"$FULL_EXT\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\"\n # Create a placeholder .fwork file where the .so was\n echo \"$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" > ${FULL_EXT%.so}.fwork\n # Create a back reference to the .so file location in the framework\n echo \"${RELATIVE_EXT%.so}.fwork\" > \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin\" \n}\n\nPYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/python/lib\")\necho \"Install Python $PYTHON_VER standard library extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib python/lib/$PYTHON_VER/lib-dynload/ \"$FULL_EXT\"\ndone\necho \"Install app package extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app_packages\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app_packages/ \"$FULL_EXT\"\ndone\necho \"Install app extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app/ \"$FULL_EXT\"\ndone\n\n# Clean up dylib template \nrm -f \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\"\necho \"Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)...\"\nfind \"$CODESIGNING_FOLDER_PATH/Frameworks\" -name \"*.framework\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der \"{}\" \\; \n"; -+ showEnvVarsInLog = 0; - }; - /* End PBXShellScriptBuildPhase section */ - -diff --git a/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m b/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m -index db00d43da85..6db38253396 100644 ---- a/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m -+++ b/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m -@@ -24,8 +24,11 @@ - - NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; - -- // Disable all color, as the Xcode log can't display color -+ // Set some other common environment indicators to disable color, as the -+ // Xcode log can't display color. Stdout will report that it is *not* a -+ // TTY. - setenv("NO_COLOR", "1", true); -+ setenv("PY_COLORS", "0", true); - - // Arguments to pass into the test suite runner. - // argv[0] must identify the process; any subsequent arg -@@ -50,6 +53,8 @@ - // Enforce UTF-8 encoding for stderr, stdout, file-system encoding and locale. - // See https://docs.python.org/3/library/os.html#python-utf-8-mode. - preconfig.utf8_mode = 1; -+ // Use the system logger for stdout/err -+ config.use_system_logger = 1; - // Don't buffer stdio. We want output to appears in the log immediately - config.buffered_stdio = 0; - // Don't write bytecode; we can't modify the app bundle --- /dev/null +++ b/tvOS/README.rst @@ -0,0 +1,108 @@ From 42a4a135fba53e0a657ef3fcf4cc9c5a4b3cf41a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 22 Dec 2024 20:56:00 +0000 Subject: [PATCH 03/26] Bump actions/upload-artifact from 4.4.3 to 4.5.0 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.4.3 to 4.5.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.4.3...v4.5.0) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2a78d775..e600ed36 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -121,7 +121,7 @@ jobs: make ${{ matrix.target }} BUILD_NUMBER=${{ needs.config.outputs.BUILD_NUMBER }} - name: Upload build artefacts - uses: actions/upload-artifact@v4.4.3 + uses: actions/upload-artifact@v4.5.0 with: name: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz path: dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz From dad62c3fd5945523c4af72ec9a61598c66326f95 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 07:10:22 +0800 Subject: [PATCH 04/26] Bump actions/upload-artifact from 4.5.0 to 4.6.0 (#244) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.5.0 to 4.6.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.5.0...v4.6.0) --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e600ed36..263d5771 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -121,7 +121,7 @@ jobs: make ${{ matrix.target }} BUILD_NUMBER=${{ needs.config.outputs.BUILD_NUMBER }} - name: Upload build artefacts - uses: actions/upload-artifact@v4.5.0 + uses: actions/upload-artifact@v4.6.0 with: name: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz path: dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz From 6d5096d3869a6d61cee965bd2f7b4e725e57784b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 07:26:14 +0800 Subject: [PATCH 05/26] Bump ncipollo/release-action from 1.14.0 to 1.15.0 (#243) Bumps [ncipollo/release-action](https://github.com/ncipollo/release-action) from 1.14.0 to 1.15.0. - [Release notes](https://github.com/ncipollo/release-action/releases) - [Commits](https://github.com/ncipollo/release-action/compare/v1.14.0...v1.15.0) --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 5e5ef751..4c0a16a4 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -47,7 +47,7 @@ jobs: merge-multiple: true - name: Create Release - uses: ncipollo/release-action@v1.14.0 + uses: ncipollo/release-action@v1.15.0 with: name: ${{ needs.ci.outputs.PYTHON_VER }}-${{ needs.config.outputs.BUILD_NUMBER }} tag: ${{ needs.ci.outputs.PYTHON_VER }}-${{ needs.config.outputs.BUILD_NUMBER }} From a3ffcad67280310770611e68c2eee5c269158545 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 3 Feb 2025 09:22:33 +0800 Subject: [PATCH 06/26] Update patch to 3.14.0a4. --- Makefile | 2 +- patch/Python/Python.patch | 192 +++++++++++++++++++------------------- 2 files changed, 97 insertions(+), 97 deletions(-) diff --git a/Makefile b/Makefile index 720461ea..0aa9c0d2 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ BUILD_NUMBER=custom # of a release cycle, as official binaries won't be published. # PYTHON_MICRO_VERSION is the full version number, without any alpha/beta/rc suffix. (e.g., 3.10.0) # PYTHON_VER is the major/minor version (e.g., 3.10) -PYTHON_VERSION=3.14.0a3 +PYTHON_VERSION=3.14.0a4 PYTHON_PKG_VERSION=$(PYTHON_VERSION) PYTHON_MICRO_VERSION=$(shell echo $(PYTHON_VERSION) | grep -Eo "\d+\.\d+\.\d+") PYTHON_PKG_MICRO_VERSION=$(shell echo $(PYTHON_PKG_VERSION) | grep -Eo "\d+\.\d+\.\d+") diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 19768c8e..6137a65d 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -113,10 +113,10 @@ index 1f6baed66d3..235dd98c60a 100644 macos_release = mac_ver()[0] if macos_release: diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py -index ed7b6a335d0..322e6cf1eee 100644 +index 7a4a8f65a5e..04ceb922249 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py -@@ -690,6 +690,14 @@ +@@ -691,6 +691,14 @@ release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0") osname = sys.platform machine = sys.implementation._multiarch @@ -163,10 +163,10 @@ index ec0857a4a99..2350e9dc821 100644 # elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX PLATFORM_TRIPLET=darwin diff --git a/configure b/configure -index 57be576e3ca..6d4ef3d0e01 100755 +index 70581e11b60..249a67228b0 100755 --- a/configure +++ b/configure -@@ -980,6 +980,8 @@ +@@ -974,6 +974,8 @@ CFLAGS CC HAS_XCRUN @@ -175,7 +175,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 IPHONEOS_DEPLOYMENT_TARGET EXPORT_MACOSX_DEPLOYMENT_TARGET CONFIGURE_MACOSX_DEPLOYMENT_TARGET -@@ -4052,6 +4054,12 @@ +@@ -4097,6 +4099,12 @@ *-apple-ios*) ac_sys_system=iOS ;; @@ -185,10 +185,10 @@ index 57be576e3ca..6d4ef3d0e01 100755 + *-apple-watchos*) + ac_sys_system=watchOS + ;; - *-*-vxworks*) - ac_sys_system=VxWorks - ;; -@@ -4129,7 +4137,7 @@ + *-*-darwin*) + ac_sys_system=Darwin + ;; +@@ -4177,7 +4185,7 @@ # On cross-compile builds, configure will look for a host-specific compiler by # prepending the user-provided host triple to the required binary name. # @@ -197,7 +197,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 # which isn't a binary that exists, and isn't very convenient, as it contains the # iOS version. As the default cross-compiler name won't exist, configure falls # back to gcc, which *definitely* won't work. We're providing wrapper scripts for -@@ -4144,6 +4152,14 @@ +@@ -4192,6 +4200,14 @@ aarch64-apple-ios*-simulator) AR=arm64-apple-ios-simulator-ar ;; aarch64-apple-ios*) AR=arm64-apple-ios-ar ;; x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;; @@ -212,7 +212,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 *) esac fi -@@ -4152,6 +4168,14 @@ +@@ -4200,6 +4216,14 @@ aarch64-apple-ios*-simulator) CC=arm64-apple-ios-simulator-clang ;; aarch64-apple-ios*) CC=arm64-apple-ios-clang ;; x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;; @@ -227,7 +227,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 *) esac fi -@@ -4160,6 +4184,14 @@ +@@ -4208,6 +4232,14 @@ aarch64-apple-ios*-simulator) CPP=arm64-apple-ios-simulator-cpp ;; aarch64-apple-ios*) CPP=arm64-apple-ios-cpp ;; x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;; @@ -242,7 +242,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 *) esac fi -@@ -4168,6 +4200,14 @@ +@@ -4216,6 +4248,14 @@ aarch64-apple-ios*-simulator) CXX=arm64-apple-ios-simulator-clang++ ;; aarch64-apple-ios*) CXX=arm64-apple-ios-clang++ ;; x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;; @@ -257,7 +257,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 *) esac fi -@@ -4288,8 +4328,10 @@ +@@ -4338,8 +4378,10 @@ case $enableval in yes) case $ac_sys_system in @@ -270,7 +270,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 esac esac -@@ -4298,6 +4340,8 @@ +@@ -4348,6 +4390,8 @@ no) case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -279,7 +279,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4404,6 +4448,36 @@ +@@ -4454,6 +4498,36 @@ ac_config_files="$ac_config_files iOS/Resources/Info.plist" @@ -316,8 +316,8 @@ index 57be576e3ca..6d4ef3d0e01 100755 ;; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 -@@ -4415,6 +4489,8 @@ - +@@ -4465,6 +4539,8 @@ + e) case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; + tvOS) as_fn_error $? "tvOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -325,7 +325,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4468,8 +4544,8 @@ +@@ -4519,8 +4595,8 @@ case "$withval" in yes) case $ac_sys_system in @@ -336,9 +336,9 @@ index 57be576e3ca..6d4ef3d0e01 100755 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" ;; *) as_fn_error $? "no default app store compliance patch available for $ac_sys_system" "$LINENO" 5 ;; -@@ -4487,8 +4563,8 @@ - else $as_nop - +@@ -4538,8 +4614,8 @@ + else case e in #( + e) case $ac_sys_system in - iOS) - # Always apply the compliance patch on iOS; we can use the macOS patch @@ -347,7 +347,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: applying default app store compliance patch" >&5 printf "%s\n" "applying default app store compliance patch" >&6; } -@@ -4542,6 +4618,50 @@ +@@ -4594,6 +4670,50 @@ ;; esac ;; @@ -395,10 +395,10 @@ index 57be576e3ca..6d4ef3d0e01 100755 + ;; + esac + ;; - *-*-vxworks*) - _host_ident=$host_cpu - ;; -@@ -4620,9 +4740,13 @@ + *-*-darwin*) + case "$host_cpu" in + arm*) +@@ -4684,9 +4804,13 @@ define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; @@ -413,7 +413,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -4685,7 +4809,10 @@ +@@ -4749,7 +4873,10 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' @@ -425,7 +425,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 # checks for alternative programs -@@ -4726,6 +4853,16 @@ +@@ -4790,6 +4917,16 @@ as_fn_append CFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" as_fn_append LDFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" ;; #( @@ -442,7 +442,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 *) : ;; esac -@@ -7030,6 +7167,10 @@ +@@ -7159,6 +7296,10 @@ MULTIARCH="" ;; #( iOS) : MULTIARCH="" ;; #( @@ -453,7 +453,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -7050,7 +7191,7 @@ +@@ -7179,7 +7320,7 @@ printf "%s\n" "$MULTIARCH" >&6; } case $ac_sys_system in #( @@ -462,7 +462,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2` ;; #( *) : SOABI_PLATFORM=$PLATFORM_TRIPLET -@@ -7101,6 +7242,14 @@ +@@ -7230,6 +7371,14 @@ PY_SUPPORT_TIER=3 ;; #( aarch64-apple-ios*/clang) : PY_SUPPORT_TIER=3 ;; #( @@ -477,7 +477,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 aarch64-*-linux-android/clang) : PY_SUPPORT_TIER=3 ;; #( x86_64-*-linux-android/clang) : -@@ -7530,7 +7679,7 @@ +@@ -7666,7 +7815,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; @@ -486,7 +486,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5;; -@@ -7596,7 +7745,7 @@ +@@ -7732,7 +7881,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -495,7 +495,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -13160,7 +13309,7 @@ +@@ -13540,7 +13689,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -504,7 +504,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -13293,7 +13442,7 @@ +@@ -13673,7 +13822,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -513,7 +513,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -13317,7 +13466,7 @@ +@@ -13697,7 +13846,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -522,7 +522,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -14769,7 +14918,7 @@ +@@ -15282,7 +15431,7 @@ ctypes_malloc_closure=yes ;; #( @@ -531,7 +531,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 ctypes_malloc_closure=yes ;; #( -@@ -18272,12 +18421,6 @@ +@@ -19031,12 +19180,6 @@ then : printf "%s\n" "#define HAVE_DUP3 1" >>confdefs.h @@ -544,7 +544,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 fi ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes -@@ -18338,18 +18481,6 @@ +@@ -19097,18 +19240,6 @@ then : printf "%s\n" "#define HAVE_FEXECVE 1" >>confdefs.h @@ -563,7 +563,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 fi ac_fn_c_check_func "$LINENO" "fpathconf" "ac_cv_func_fpathconf" if test "x$ac_cv_func_fpathconf" = xyes -@@ -18776,24 +18907,6 @@ +@@ -19535,24 +19666,6 @@ then : printf "%s\n" "#define HAVE_POSIX_OPENPT 1" >>confdefs.h @@ -588,7 +588,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 fi ac_fn_c_check_func "$LINENO" "pread" "ac_cv_func_pread" if test "x$ac_cv_func_pread" = xyes -@@ -19094,12 +19207,6 @@ +@@ -19853,12 +19966,6 @@ then : printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h @@ -601,7 +601,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 fi ac_fn_c_check_func "$LINENO" "sigfillset" "ac_cv_func_sigfillset" if test "x$ac_cv_func_sigfillset" = xyes -@@ -19368,11 +19475,11 @@ +@@ -20127,11 +20234,11 @@ fi @@ -615,7 +615,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" if test "x$ac_cv_func_getentropy" = xyes then : -@@ -19394,6 +19501,53 @@ +@@ -20153,6 +20260,53 @@ fi @@ -669,7 +669,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5 printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; } if test ${ac_cv_c_undeclared_builtin_options+y} -@@ -22269,7 +22423,8 @@ +@@ -23235,7 +23389,8 @@ # check for openpty, login_tty, and forkpty @@ -679,7 +679,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 for ac_func in openpty do : -@@ -22365,7 +22520,7 @@ +@@ -23349,7 +23504,7 @@ fi done @@ -688,7 +688,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 printf %s "checking for library containing login_tty... " >&6; } if test ${ac_cv_search_login_tty+y} then : -@@ -22522,6 +22677,7 @@ +@@ -23532,6 +23687,7 @@ fi done @@ -696,7 +696,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 # check for long file support functions ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" -@@ -22768,10 +22924,10 @@ +@@ -23797,10 +23953,10 @@ done @@ -709,7 +709,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 then for ac_func in clock_settime -@@ -24999,8 +25155,8 @@ +@@ -26139,8 +26295,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -720,7 +720,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -27752,7 +27908,7 @@ +@@ -29010,7 +29166,7 @@ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for device files" >&5 printf "%s\n" "$as_me: checking for device files" >&6;} @@ -729,7 +729,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -28184,7 +28340,7 @@ +@@ -29458,7 +29614,7 @@ with_ensurepip=no ;; #( WASI) : with_ensurepip=no ;; #( @@ -738,7 +738,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 with_ensurepip=no ;; #( *) : with_ensurepip=upgrade -@@ -29130,7 +29286,7 @@ +@@ -30438,7 +30594,7 @@ ;; #( Darwin) : ;; #( @@ -747,7 +747,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 -@@ -33028,6 +33184,8 @@ +@@ -34441,6 +34597,8 @@ "Mac/Resources/framework/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/framework/Info.plist" ;; "Mac/Resources/app/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/app/Info.plist" ;; "iOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES iOS/Resources/Info.plist" ;; @@ -757,7 +757,7 @@ index 57be576e3ca..6d4ef3d0e01 100755 "Misc/python.pc") CONFIG_FILES="$CONFIG_FILES Misc/python.pc" ;; "Misc/python-embed.pc") CONFIG_FILES="$CONFIG_FILES Misc/python-embed.pc" ;; diff --git a/configure.ac b/configure.ac -index bd0221481c5..6dca265f3cc 100644 +index d7c3920d049..6027da5752a 100644 --- a/configure.ac +++ b/configure.ac @@ -330,6 +330,12 @@ @@ -770,10 +770,10 @@ index bd0221481c5..6dca265f3cc 100644 + *-apple-watchos*) + ac_sys_system=watchOS + ;; - *-*-vxworks*) - ac_sys_system=VxWorks - ;; -@@ -401,7 +407,7 @@ + *-*-darwin*) + ac_sys_system=Darwin + ;; +@@ -404,7 +410,7 @@ # On cross-compile builds, configure will look for a host-specific compiler by # prepending the user-provided host triple to the required binary name. # @@ -782,7 +782,7 @@ index bd0221481c5..6dca265f3cc 100644 # which isn't a binary that exists, and isn't very convenient, as it contains the # iOS version. As the default cross-compiler name won't exist, configure falls # back to gcc, which *definitely* won't work. We're providing wrapper scripts for -@@ -416,6 +422,14 @@ +@@ -419,6 +425,14 @@ aarch64-apple-ios*-simulator) AR=arm64-apple-ios-simulator-ar ;; aarch64-apple-ios*) AR=arm64-apple-ios-ar ;; x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;; @@ -797,7 +797,7 @@ index bd0221481c5..6dca265f3cc 100644 *) esac fi -@@ -424,6 +438,14 @@ +@@ -427,6 +441,14 @@ aarch64-apple-ios*-simulator) CC=arm64-apple-ios-simulator-clang ;; aarch64-apple-ios*) CC=arm64-apple-ios-clang ;; x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;; @@ -812,7 +812,7 @@ index bd0221481c5..6dca265f3cc 100644 *) esac fi -@@ -432,6 +454,14 @@ +@@ -435,6 +457,14 @@ aarch64-apple-ios*-simulator) CPP=arm64-apple-ios-simulator-cpp ;; aarch64-apple-ios*) CPP=arm64-apple-ios-cpp ;; x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;; @@ -827,7 +827,7 @@ index bd0221481c5..6dca265f3cc 100644 *) esac fi -@@ -440,6 +470,14 @@ +@@ -443,6 +473,14 @@ aarch64-apple-ios*-simulator) CXX=arm64-apple-ios-simulator-clang++ ;; aarch64-apple-ios*) CXX=arm64-apple-ios-clang++ ;; x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;; @@ -842,7 +842,7 @@ index bd0221481c5..6dca265f3cc 100644 *) esac fi -@@ -554,8 +592,10 @@ +@@ -557,8 +595,10 @@ case $enableval in yes) case $ac_sys_system in @@ -855,7 +855,7 @@ index bd0221481c5..6dca265f3cc 100644 *) AC_MSG_ERROR([Unknown platform for framework build]) esac esac -@@ -564,6 +604,8 @@ +@@ -567,6 +607,8 @@ no) case $ac_sys_system in iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;; @@ -864,7 +864,7 @@ index bd0221481c5..6dca265f3cc 100644 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -666,6 +708,34 @@ +@@ -669,6 +711,34 @@ AC_CONFIG_FILES([iOS/Resources/Info.plist]) ;; @@ -899,7 +899,7 @@ index bd0221481c5..6dca265f3cc 100644 *) AC_MSG_ERROR([Unknown platform for framework build]) ;; -@@ -674,6 +744,8 @@ +@@ -677,6 +747,8 @@ ],[ case $ac_sys_system in iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;; @@ -908,7 +908,7 @@ index bd0221481c5..6dca265f3cc 100644 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -726,8 +798,8 @@ +@@ -729,8 +801,8 @@ case "$withval" in yes) case $ac_sys_system in @@ -919,7 +919,7 @@ index bd0221481c5..6dca265f3cc 100644 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" ;; *) AC_MSG_ERROR([no default app store compliance patch available for $ac_sys_system]) ;; -@@ -741,8 +813,8 @@ +@@ -744,8 +816,8 @@ esac ],[ case $ac_sys_system in @@ -930,7 +930,7 @@ index bd0221481c5..6dca265f3cc 100644 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" AC_MSG_RESULT([applying default app store compliance patch]) ;; -@@ -790,6 +862,46 @@ +@@ -793,6 +865,46 @@ ;; esac ;; @@ -974,10 +974,10 @@ index bd0221481c5..6dca265f3cc 100644 + ;; + esac + ;; - *-*-vxworks*) - _host_ident=$host_cpu - ;; -@@ -867,9 +979,13 @@ + *-*-darwin*) + case "$host_cpu" in + arm*) +@@ -882,9 +994,13 @@ define_xopen_source=no;; Darwin/@<:@[12]@:>@@<:@0-9@:>@.*) define_xopen_source=no;; @@ -992,7 +992,7 @@ index bd0221481c5..6dca265f3cc 100644 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -928,8 +1044,11 @@ +@@ -943,8 +1059,11 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' @@ -1005,7 +1005,7 @@ index bd0221481c5..6dca265f3cc 100644 # checks for alternative programs -@@ -963,11 +1082,17 @@ +@@ -978,11 +1097,17 @@ ], ) @@ -1024,7 +1024,7 @@ index bd0221481c5..6dca265f3cc 100644 ], ) -@@ -1156,6 +1281,8 @@ +@@ -1171,6 +1296,8 @@ AS_CASE([$ac_sys_system], [Darwin*], [MULTIARCH=""], [iOS], [MULTIARCH=""], @@ -1033,7 +1033,7 @@ index bd0221481c5..6dca265f3cc 100644 [FreeBSD*], [MULTIARCH=""], [MULTIARCH=$($CC --print-multiarch 2>/dev/null)] ) -@@ -1177,7 +1304,7 @@ +@@ -1192,7 +1319,7 @@ dnl use a single "fat" binary at runtime. SOABI_PLATFORM is the component of dnl the PLATFORM_TRIPLET that will be used in binary module extensions. AS_CASE([$ac_sys_system], @@ -1042,7 +1042,7 @@ index bd0221481c5..6dca265f3cc 100644 [SOABI_PLATFORM=$PLATFORM_TRIPLET] ) -@@ -1211,6 +1338,10 @@ +@@ -1226,6 +1353,10 @@ [x86_64-*-freebsd*/clang], [PY_SUPPORT_TIER=3], dnl FreeBSD on AMD64 [aarch64-apple-ios*-simulator/clang], [PY_SUPPORT_TIER=3], dnl iOS Simulator on arm64 [aarch64-apple-ios*/clang], [PY_SUPPORT_TIER=3], dnl iOS on ARM64 @@ -1053,7 +1053,7 @@ index bd0221481c5..6dca265f3cc 100644 [aarch64-*-linux-android/clang], [PY_SUPPORT_TIER=3], dnl Android on ARM64 [x86_64-*-linux-android/clang], [PY_SUPPORT_TIER=3], dnl Android on AMD64 -@@ -1520,7 +1651,7 @@ +@@ -1535,7 +1666,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; @@ -1062,7 +1062,7 @@ index bd0221481c5..6dca265f3cc 100644 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) AC_MSG_ERROR([Unknown platform for framework build]);; -@@ -1585,7 +1716,7 @@ +@@ -1600,7 +1731,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -1071,7 +1071,7 @@ index bd0221481c5..6dca265f3cc 100644 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -3412,7 +3543,7 @@ +@@ -3455,7 +3586,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -1080,7 +1080,7 @@ index bd0221481c5..6dca265f3cc 100644 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -3536,7 +3667,7 @@ +@@ -3579,7 +3710,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -1089,7 +1089,7 @@ index bd0221481c5..6dca265f3cc 100644 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -3560,7 +3691,7 @@ +@@ -3603,7 +3734,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -1098,7 +1098,7 @@ index bd0221481c5..6dca265f3cc 100644 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -3980,7 +4111,7 @@ +@@ -4023,7 +4154,7 @@ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], @@ -1107,19 +1107,19 @@ index bd0221481c5..6dca265f3cc 100644 ctypes_malloc_closure=yes ], [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] -@@ -5098,9 +5229,9 @@ +@@ -5140,9 +5271,9 @@ # checks for library functions AC_CHECK_FUNCS([ \ accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ -- copy_file_range ctermid dup dup3 execv explicit_bzero explicit_memset \ -+ copy_file_range ctermid dup dup3 explicit_bzero explicit_memset \ +- copy_file_range ctermid dladdr dup dup3 execv explicit_bzero explicit_memset \ ++ copy_file_range ctermid dladdr dup dup3 explicit_bzero explicit_memset \ faccessat fchmod fchmodat fchown fchownat fdopendir fdwalk fexecve \ - fork fork1 fpathconf fstatat ftime ftruncate futimens futimes futimesat \ + fpathconf fstatat ftime ftruncate futimens futimes futimesat \ gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \ getgrnam_r getgrouplist gethostname getitimer getloadavg getlogin \ getpeername getpgid getpid getppid getpriority _getpty \ -@@ -5108,8 +5239,7 @@ +@@ -5150,8 +5281,7 @@ getspnam getuid getwd grantpt if_nameindex initgroups kill killpg lchown linkat \ lockf lstat lutimes madvise mbrtowc memrchr mkdirat mkfifo mkfifoat \ mknod mknodat mktime mmap mremap nice openat opendir pathconf pause pipe \ @@ -1129,7 +1129,7 @@ index bd0221481c5..6dca265f3cc 100644 pread preadv preadv2 process_vm_readv \ pthread_cond_timedwait_relative_np pthread_condattr_setclock pthread_init \ pthread_kill pthread_getname_np pthread_setname_np \ -@@ -5118,7 +5248,7 @@ +@@ -5160,7 +5290,7 @@ sched_setparam sched_setscheduler sem_clockwait sem_getvalue sem_open \ sem_timedwait sem_unlink sendfile setegid seteuid setgid sethostname \ setitimer setlocale setpgid setpgrp setpriority setregid setresgid \ @@ -1138,7 +1138,7 @@ index bd0221481c5..6dca265f3cc 100644 sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ -@@ -5133,12 +5263,20 @@ +@@ -5175,12 +5305,20 @@ AC_CHECK_FUNCS([lchmod]) fi @@ -1162,7 +1162,7 @@ index bd0221481c5..6dca265f3cc 100644 fi AC_CHECK_DECL([dirfd], -@@ -5392,20 +5530,22 @@ +@@ -5434,20 +5572,22 @@ ]) # check for openpty, login_tty, and forkpty @@ -1199,7 +1199,7 @@ index bd0221481c5..6dca265f3cc 100644 # check for long file support functions AC_CHECK_FUNCS([fseek64 fseeko fstatvfs ftell64 ftello statvfs]) -@@ -5444,10 +5584,10 @@ +@@ -5486,10 +5626,10 @@ ]) ]) @@ -1212,7 +1212,7 @@ index bd0221481c5..6dca265f3cc 100644 then AC_CHECK_FUNCS([clock_settime], [], [ AC_CHECK_LIB([rt], [clock_settime], [ -@@ -6198,8 +6338,8 @@ +@@ -6240,8 +6380,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -1223,7 +1223,7 @@ index bd0221481c5..6dca265f3cc 100644 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -6863,7 +7003,7 @@ +@@ -6900,7 +7040,7 @@ dnl NOTE: Inform user how to proceed with files when cross compiling. dnl Some cross-compile builds are predictable; they won't ever dnl have /dev/ptmx or /dev/ptc, so we can set them explicitly. @@ -1232,7 +1232,7 @@ index bd0221481c5..6dca265f3cc 100644 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -7119,7 +7259,7 @@ +@@ -7156,7 +7296,7 @@ AS_CASE([$ac_sys_system], [Emscripten], [with_ensurepip=no], [WASI], [with_ensurepip=no], @@ -1241,7 +1241,7 @@ index bd0221481c5..6dca265f3cc 100644 [with_ensurepip=upgrade] ) ]) -@@ -7529,7 +7669,7 @@ +@@ -7567,7 +7707,7 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], From e6682a41b2310b820cf554f3e660eff43d115459 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 10:42:14 +0800 Subject: [PATCH 07/26] Bump actions/setup-python from 5.3.0 to 5.4.0 (#245) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.3.0 to 5.4.0. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5.3.0...v5.4.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yaml | 2 +- .github/workflows/publish.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 263d5771..44609a7a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -109,7 +109,7 @@ jobs: - uses: actions/checkout@v4.1.7 - name: Set up Python - uses: actions/setup-python@v5.3.0 + uses: actions/setup-python@v5.4.0 with: # Appending -dev ensures that we can always build the dev release. # It's a no-op for versions that have been published. diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index a9f69ed4..5d2ce753 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python environment - uses: actions/setup-python@v5.3.0 + uses: actions/setup-python@v5.4.0 with: python-version: "3.X" From a2c04c37393d47947d467e6ad234b17435b64fbc Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Thu, 13 Feb 2025 15:01:46 +0800 Subject: [PATCH 08/26] Bump patch to Python3.14.0a5. (#247) Update Python to 3.14.0a5. Also includes: * python/cpython#130026 testbed symlink handling * LibFFI 3.4.7 * OpenSSL 3.0.16 * XZ 5.6.4 --- Makefile | 8 +- patch/Python/Python.patch | 248 ++++++++++++++++++++++++++------------ 2 files changed, 174 insertions(+), 82 deletions(-) diff --git a/Makefile b/Makefile index 0aa9c0d2..b34d7e05 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ BUILD_NUMBER=custom # of a release cycle, as official binaries won't be published. # PYTHON_MICRO_VERSION is the full version number, without any alpha/beta/rc suffix. (e.g., 3.10.0) # PYTHON_VER is the major/minor version (e.g., 3.10) -PYTHON_VERSION=3.14.0a4 +PYTHON_VERSION=3.14.0a5 PYTHON_PKG_VERSION=$(PYTHON_VERSION) PYTHON_MICRO_VERSION=$(shell echo $(PYTHON_VERSION) | grep -Eo "\d+\.\d+\.\d+") PYTHON_PKG_MICRO_VERSION=$(shell echo $(PYTHON_PKG_VERSION) | grep -Eo "\d+\.\d+\.\d+") @@ -27,10 +27,10 @@ PYTHON_VER=$(basename $(PYTHON_VERSION)) # The binary releases of dependencies, published at: # https://github.com/beeware/cpython-apple-source-deps/releases BZIP2_VERSION=1.0.8-1 -LIBFFI_VERSION=3.4.6-1 +LIBFFI_VERSION=3.4.7-1 MPDECIMAL_VERSION=4.0.0-1 -OPENSSL_VERSION=3.0.15-1 -XZ_VERSION=5.6.2-1 +OPENSSL_VERSION=3.0.16-1 +XZ_VERSION=5.6.4-1 # Supported OS OS_LIST=macOS iOS tvOS watchOS diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 6137a65d..910f33c2 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -113,10 +113,10 @@ index 1f6baed66d3..235dd98c60a 100644 macos_release = mac_ver()[0] if macos_release: diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py -index 7a4a8f65a5e..04ceb922249 100644 +index 69f72452c40..34ce643340b 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py -@@ -691,6 +691,14 @@ +@@ -719,6 +719,14 @@ release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0") osname = sys.platform machine = sys.implementation._multiarch @@ -163,7 +163,7 @@ index ec0857a4a99..2350e9dc821 100644 # elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX PLATFORM_TRIPLET=darwin diff --git a/configure b/configure -index 70581e11b60..249a67228b0 100755 +index d46bc563a67..d5cd81d16a8 100755 --- a/configure +++ b/configure @@ -974,6 +974,8 @@ @@ -175,7 +175,7 @@ index 70581e11b60..249a67228b0 100755 IPHONEOS_DEPLOYMENT_TARGET EXPORT_MACOSX_DEPLOYMENT_TARGET CONFIGURE_MACOSX_DEPLOYMENT_TARGET -@@ -4097,6 +4099,12 @@ +@@ -4100,6 +4102,12 @@ *-apple-ios*) ac_sys_system=iOS ;; @@ -188,7 +188,7 @@ index 70581e11b60..249a67228b0 100755 *-*-darwin*) ac_sys_system=Darwin ;; -@@ -4177,7 +4185,7 @@ +@@ -4181,7 +4189,7 @@ # On cross-compile builds, configure will look for a host-specific compiler by # prepending the user-provided host triple to the required binary name. # @@ -197,7 +197,7 @@ index 70581e11b60..249a67228b0 100755 # which isn't a binary that exists, and isn't very convenient, as it contains the # iOS version. As the default cross-compiler name won't exist, configure falls # back to gcc, which *definitely* won't work. We're providing wrapper scripts for -@@ -4192,6 +4200,14 @@ +@@ -4196,6 +4204,14 @@ aarch64-apple-ios*-simulator) AR=arm64-apple-ios-simulator-ar ;; aarch64-apple-ios*) AR=arm64-apple-ios-ar ;; x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;; @@ -212,7 +212,7 @@ index 70581e11b60..249a67228b0 100755 *) esac fi -@@ -4200,6 +4216,14 @@ +@@ -4204,6 +4220,14 @@ aarch64-apple-ios*-simulator) CC=arm64-apple-ios-simulator-clang ;; aarch64-apple-ios*) CC=arm64-apple-ios-clang ;; x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;; @@ -227,7 +227,7 @@ index 70581e11b60..249a67228b0 100755 *) esac fi -@@ -4208,6 +4232,14 @@ +@@ -4212,6 +4236,14 @@ aarch64-apple-ios*-simulator) CPP=arm64-apple-ios-simulator-cpp ;; aarch64-apple-ios*) CPP=arm64-apple-ios-cpp ;; x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;; @@ -242,7 +242,7 @@ index 70581e11b60..249a67228b0 100755 *) esac fi -@@ -4216,6 +4248,14 @@ +@@ -4220,6 +4252,14 @@ aarch64-apple-ios*-simulator) CXX=arm64-apple-ios-simulator-clang++ ;; aarch64-apple-ios*) CXX=arm64-apple-ios-clang++ ;; x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;; @@ -257,7 +257,7 @@ index 70581e11b60..249a67228b0 100755 *) esac fi -@@ -4338,8 +4378,10 @@ +@@ -4342,8 +4382,10 @@ case $enableval in yes) case $ac_sys_system in @@ -270,7 +270,7 @@ index 70581e11b60..249a67228b0 100755 *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 esac esac -@@ -4348,6 +4390,8 @@ +@@ -4352,6 +4394,8 @@ no) case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -279,7 +279,7 @@ index 70581e11b60..249a67228b0 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4454,6 +4498,36 @@ +@@ -4458,6 +4502,36 @@ ac_config_files="$ac_config_files iOS/Resources/Info.plist" @@ -316,7 +316,7 @@ index 70581e11b60..249a67228b0 100755 ;; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 -@@ -4465,6 +4539,8 @@ +@@ -4469,6 +4543,8 @@ e) case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -325,7 +325,7 @@ index 70581e11b60..249a67228b0 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4519,8 +4595,8 @@ +@@ -4523,8 +4599,8 @@ case "$withval" in yes) case $ac_sys_system in @@ -336,7 +336,7 @@ index 70581e11b60..249a67228b0 100755 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" ;; *) as_fn_error $? "no default app store compliance patch available for $ac_sys_system" "$LINENO" 5 ;; -@@ -4538,8 +4614,8 @@ +@@ -4542,8 +4618,8 @@ else case e in #( e) case $ac_sys_system in @@ -347,7 +347,7 @@ index 70581e11b60..249a67228b0 100755 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: applying default app store compliance patch" >&5 printf "%s\n" "applying default app store compliance patch" >&6; } -@@ -4594,6 +4670,50 @@ +@@ -4598,6 +4674,50 @@ ;; esac ;; @@ -398,7 +398,7 @@ index 70581e11b60..249a67228b0 100755 *-*-darwin*) case "$host_cpu" in arm*) -@@ -4684,9 +4804,13 @@ +@@ -4688,9 +4808,13 @@ define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; @@ -413,7 +413,7 @@ index 70581e11b60..249a67228b0 100755 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -4749,7 +4873,10 @@ +@@ -4753,7 +4877,10 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' @@ -425,7 +425,7 @@ index 70581e11b60..249a67228b0 100755 # checks for alternative programs -@@ -4790,6 +4917,16 @@ +@@ -4794,6 +4921,16 @@ as_fn_append CFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" as_fn_append LDFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" ;; #( @@ -442,7 +442,7 @@ index 70581e11b60..249a67228b0 100755 *) : ;; esac -@@ -7159,6 +7296,10 @@ +@@ -7163,6 +7300,10 @@ MULTIARCH="" ;; #( iOS) : MULTIARCH="" ;; #( @@ -453,7 +453,7 @@ index 70581e11b60..249a67228b0 100755 FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -7179,7 +7320,7 @@ +@@ -7183,7 +7324,7 @@ printf "%s\n" "$MULTIARCH" >&6; } case $ac_sys_system in #( @@ -462,7 +462,7 @@ index 70581e11b60..249a67228b0 100755 SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2` ;; #( *) : SOABI_PLATFORM=$PLATFORM_TRIPLET -@@ -7230,6 +7371,14 @@ +@@ -7234,6 +7375,14 @@ PY_SUPPORT_TIER=3 ;; #( aarch64-apple-ios*/clang) : PY_SUPPORT_TIER=3 ;; #( @@ -477,7 +477,7 @@ index 70581e11b60..249a67228b0 100755 aarch64-*-linux-android/clang) : PY_SUPPORT_TIER=3 ;; #( x86_64-*-linux-android/clang) : -@@ -7666,7 +7815,7 @@ +@@ -7670,7 +7819,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; @@ -486,7 +486,7 @@ index 70581e11b60..249a67228b0 100755 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5;; -@@ -7732,7 +7881,7 @@ +@@ -7736,7 +7885,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -495,7 +495,7 @@ index 70581e11b60..249a67228b0 100755 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -13540,7 +13689,7 @@ +@@ -13544,7 +13693,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -504,7 +504,7 @@ index 70581e11b60..249a67228b0 100755 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -13673,7 +13822,7 @@ +@@ -13677,7 +13826,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -513,7 +513,7 @@ index 70581e11b60..249a67228b0 100755 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -13697,7 +13846,7 @@ +@@ -13701,7 +13850,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -522,7 +522,7 @@ index 70581e11b60..249a67228b0 100755 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -15282,7 +15431,7 @@ +@@ -15286,7 +15435,7 @@ ctypes_malloc_closure=yes ;; #( @@ -531,7 +531,7 @@ index 70581e11b60..249a67228b0 100755 ctypes_malloc_closure=yes ;; #( -@@ -19031,12 +19180,6 @@ +@@ -19038,12 +19187,6 @@ then : printf "%s\n" "#define HAVE_DUP3 1" >>confdefs.h @@ -544,7 +544,7 @@ index 70581e11b60..249a67228b0 100755 fi ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes -@@ -19097,18 +19240,6 @@ +@@ -19104,18 +19247,6 @@ then : printf "%s\n" "#define HAVE_FEXECVE 1" >>confdefs.h @@ -563,7 +563,7 @@ index 70581e11b60..249a67228b0 100755 fi ac_fn_c_check_func "$LINENO" "fpathconf" "ac_cv_func_fpathconf" if test "x$ac_cv_func_fpathconf" = xyes -@@ -19535,24 +19666,6 @@ +@@ -19542,24 +19673,6 @@ then : printf "%s\n" "#define HAVE_POSIX_OPENPT 1" >>confdefs.h @@ -588,7 +588,7 @@ index 70581e11b60..249a67228b0 100755 fi ac_fn_c_check_func "$LINENO" "pread" "ac_cv_func_pread" if test "x$ac_cv_func_pread" = xyes -@@ -19853,12 +19966,6 @@ +@@ -19860,12 +19973,6 @@ then : printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h @@ -601,7 +601,7 @@ index 70581e11b60..249a67228b0 100755 fi ac_fn_c_check_func "$LINENO" "sigfillset" "ac_cv_func_sigfillset" if test "x$ac_cv_func_sigfillset" = xyes -@@ -20127,11 +20234,11 @@ +@@ -20134,11 +20241,11 @@ fi @@ -615,7 +615,7 @@ index 70581e11b60..249a67228b0 100755 ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" if test "x$ac_cv_func_getentropy" = xyes then : -@@ -20153,6 +20260,53 @@ +@@ -20160,6 +20267,53 @@ fi @@ -669,7 +669,7 @@ index 70581e11b60..249a67228b0 100755 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5 printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; } if test ${ac_cv_c_undeclared_builtin_options+y} -@@ -23235,7 +23389,8 @@ +@@ -23242,7 +23396,8 @@ # check for openpty, login_tty, and forkpty @@ -679,7 +679,7 @@ index 70581e11b60..249a67228b0 100755 for ac_func in openpty do : -@@ -23349,7 +23504,7 @@ +@@ -23356,7 +23511,7 @@ fi done @@ -688,7 +688,7 @@ index 70581e11b60..249a67228b0 100755 printf %s "checking for library containing login_tty... " >&6; } if test ${ac_cv_search_login_tty+y} then : -@@ -23532,6 +23687,7 @@ +@@ -23539,6 +23694,7 @@ fi done @@ -696,7 +696,7 @@ index 70581e11b60..249a67228b0 100755 # check for long file support functions ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" -@@ -23797,10 +23953,10 @@ +@@ -23804,10 +23960,10 @@ done @@ -709,7 +709,7 @@ index 70581e11b60..249a67228b0 100755 then for ac_func in clock_settime -@@ -26139,8 +26295,8 @@ +@@ -26146,8 +26302,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -720,7 +720,7 @@ index 70581e11b60..249a67228b0 100755 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -29010,7 +29166,7 @@ +@@ -29017,7 +29173,7 @@ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for device files" >&5 printf "%s\n" "$as_me: checking for device files" >&6;} @@ -729,7 +729,7 @@ index 70581e11b60..249a67228b0 100755 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -29458,7 +29614,7 @@ +@@ -29510,7 +29666,7 @@ with_ensurepip=no ;; #( WASI) : with_ensurepip=no ;; #( @@ -738,7 +738,7 @@ index 70581e11b60..249a67228b0 100755 with_ensurepip=no ;; #( *) : with_ensurepip=upgrade -@@ -30438,7 +30594,7 @@ +@@ -30490,7 +30646,7 @@ ;; #( Darwin) : ;; #( @@ -747,7 +747,7 @@ index 70581e11b60..249a67228b0 100755 -@@ -34441,6 +34597,8 @@ +@@ -34493,6 +34649,8 @@ "Mac/Resources/framework/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/framework/Info.plist" ;; "Mac/Resources/app/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/app/Info.plist" ;; "iOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES iOS/Resources/Info.plist" ;; @@ -757,7 +757,7 @@ index 70581e11b60..249a67228b0 100755 "Misc/python.pc") CONFIG_FILES="$CONFIG_FILES Misc/python.pc" ;; "Misc/python-embed.pc") CONFIG_FILES="$CONFIG_FILES Misc/python-embed.pc" ;; diff --git a/configure.ac b/configure.ac -index d7c3920d049..6027da5752a 100644 +index faa89095303..9bd51f7da97 100644 --- a/configure.ac +++ b/configure.ac @@ -330,6 +330,12 @@ @@ -773,7 +773,7 @@ index d7c3920d049..6027da5752a 100644 *-*-darwin*) ac_sys_system=Darwin ;; -@@ -404,7 +410,7 @@ +@@ -405,7 +411,7 @@ # On cross-compile builds, configure will look for a host-specific compiler by # prepending the user-provided host triple to the required binary name. # @@ -782,7 +782,7 @@ index d7c3920d049..6027da5752a 100644 # which isn't a binary that exists, and isn't very convenient, as it contains the # iOS version. As the default cross-compiler name won't exist, configure falls # back to gcc, which *definitely* won't work. We're providing wrapper scripts for -@@ -419,6 +425,14 @@ +@@ -420,6 +426,14 @@ aarch64-apple-ios*-simulator) AR=arm64-apple-ios-simulator-ar ;; aarch64-apple-ios*) AR=arm64-apple-ios-ar ;; x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;; @@ -797,7 +797,7 @@ index d7c3920d049..6027da5752a 100644 *) esac fi -@@ -427,6 +441,14 @@ +@@ -428,6 +442,14 @@ aarch64-apple-ios*-simulator) CC=arm64-apple-ios-simulator-clang ;; aarch64-apple-ios*) CC=arm64-apple-ios-clang ;; x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;; @@ -812,7 +812,7 @@ index d7c3920d049..6027da5752a 100644 *) esac fi -@@ -435,6 +457,14 @@ +@@ -436,6 +458,14 @@ aarch64-apple-ios*-simulator) CPP=arm64-apple-ios-simulator-cpp ;; aarch64-apple-ios*) CPP=arm64-apple-ios-cpp ;; x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;; @@ -827,7 +827,7 @@ index d7c3920d049..6027da5752a 100644 *) esac fi -@@ -443,6 +473,14 @@ +@@ -444,6 +474,14 @@ aarch64-apple-ios*-simulator) CXX=arm64-apple-ios-simulator-clang++ ;; aarch64-apple-ios*) CXX=arm64-apple-ios-clang++ ;; x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;; @@ -842,7 +842,7 @@ index d7c3920d049..6027da5752a 100644 *) esac fi -@@ -557,8 +595,10 @@ +@@ -558,8 +596,10 @@ case $enableval in yes) case $ac_sys_system in @@ -855,7 +855,7 @@ index d7c3920d049..6027da5752a 100644 *) AC_MSG_ERROR([Unknown platform for framework build]) esac esac -@@ -567,6 +607,8 @@ +@@ -568,6 +608,8 @@ no) case $ac_sys_system in iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;; @@ -864,7 +864,7 @@ index d7c3920d049..6027da5752a 100644 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -669,6 +711,34 @@ +@@ -670,6 +712,34 @@ AC_CONFIG_FILES([iOS/Resources/Info.plist]) ;; @@ -899,7 +899,7 @@ index d7c3920d049..6027da5752a 100644 *) AC_MSG_ERROR([Unknown platform for framework build]) ;; -@@ -677,6 +747,8 @@ +@@ -678,6 +748,8 @@ ],[ case $ac_sys_system in iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;; @@ -908,7 +908,7 @@ index d7c3920d049..6027da5752a 100644 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -729,8 +801,8 @@ +@@ -730,8 +802,8 @@ case "$withval" in yes) case $ac_sys_system in @@ -919,7 +919,7 @@ index d7c3920d049..6027da5752a 100644 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" ;; *) AC_MSG_ERROR([no default app store compliance patch available for $ac_sys_system]) ;; -@@ -744,8 +816,8 @@ +@@ -745,8 +817,8 @@ esac ],[ case $ac_sys_system in @@ -930,7 +930,7 @@ index d7c3920d049..6027da5752a 100644 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" AC_MSG_RESULT([applying default app store compliance patch]) ;; -@@ -793,6 +865,46 @@ +@@ -794,6 +866,46 @@ ;; esac ;; @@ -977,7 +977,7 @@ index d7c3920d049..6027da5752a 100644 *-*-darwin*) case "$host_cpu" in arm*) -@@ -882,9 +994,13 @@ +@@ -883,9 +995,13 @@ define_xopen_source=no;; Darwin/@<:@[12]@:>@@<:@0-9@:>@.*) define_xopen_source=no;; @@ -992,7 +992,7 @@ index d7c3920d049..6027da5752a 100644 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -943,8 +1059,11 @@ +@@ -944,8 +1060,11 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' @@ -1005,7 +1005,7 @@ index d7c3920d049..6027da5752a 100644 # checks for alternative programs -@@ -978,11 +1097,17 @@ +@@ -979,11 +1098,17 @@ ], ) @@ -1024,7 +1024,7 @@ index d7c3920d049..6027da5752a 100644 ], ) -@@ -1171,6 +1296,8 @@ +@@ -1172,6 +1297,8 @@ AS_CASE([$ac_sys_system], [Darwin*], [MULTIARCH=""], [iOS], [MULTIARCH=""], @@ -1033,7 +1033,7 @@ index d7c3920d049..6027da5752a 100644 [FreeBSD*], [MULTIARCH=""], [MULTIARCH=$($CC --print-multiarch 2>/dev/null)] ) -@@ -1192,7 +1319,7 @@ +@@ -1193,7 +1320,7 @@ dnl use a single "fat" binary at runtime. SOABI_PLATFORM is the component of dnl the PLATFORM_TRIPLET that will be used in binary module extensions. AS_CASE([$ac_sys_system], @@ -1042,7 +1042,7 @@ index d7c3920d049..6027da5752a 100644 [SOABI_PLATFORM=$PLATFORM_TRIPLET] ) -@@ -1226,6 +1353,10 @@ +@@ -1227,6 +1354,10 @@ [x86_64-*-freebsd*/clang], [PY_SUPPORT_TIER=3], dnl FreeBSD on AMD64 [aarch64-apple-ios*-simulator/clang], [PY_SUPPORT_TIER=3], dnl iOS Simulator on arm64 [aarch64-apple-ios*/clang], [PY_SUPPORT_TIER=3], dnl iOS on ARM64 @@ -1053,7 +1053,7 @@ index d7c3920d049..6027da5752a 100644 [aarch64-*-linux-android/clang], [PY_SUPPORT_TIER=3], dnl Android on ARM64 [x86_64-*-linux-android/clang], [PY_SUPPORT_TIER=3], dnl Android on AMD64 -@@ -1535,7 +1666,7 @@ +@@ -1536,7 +1667,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; @@ -1062,7 +1062,7 @@ index d7c3920d049..6027da5752a 100644 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) AC_MSG_ERROR([Unknown platform for framework build]);; -@@ -1600,7 +1731,7 @@ +@@ -1601,7 +1732,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -1071,7 +1071,7 @@ index d7c3920d049..6027da5752a 100644 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -3455,7 +3586,7 @@ +@@ -3456,7 +3587,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -1080,7 +1080,7 @@ index d7c3920d049..6027da5752a 100644 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -3579,7 +3710,7 @@ +@@ -3580,7 +3711,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -1089,7 +1089,7 @@ index d7c3920d049..6027da5752a 100644 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -3603,7 +3734,7 @@ +@@ -3604,7 +3735,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -1098,7 +1098,7 @@ index d7c3920d049..6027da5752a 100644 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -4023,7 +4154,7 @@ +@@ -4024,7 +4155,7 @@ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], @@ -1107,7 +1107,7 @@ index d7c3920d049..6027da5752a 100644 ctypes_malloc_closure=yes ], [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] -@@ -5140,9 +5271,9 @@ +@@ -5133,9 +5264,9 @@ # checks for library functions AC_CHECK_FUNCS([ \ accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ @@ -1119,7 +1119,7 @@ index d7c3920d049..6027da5752a 100644 gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \ getgrnam_r getgrouplist gethostname getitimer getloadavg getlogin \ getpeername getpgid getpid getppid getpriority _getpty \ -@@ -5150,8 +5281,7 @@ +@@ -5143,8 +5274,7 @@ getspnam getuid getwd grantpt if_nameindex initgroups kill killpg lchown linkat \ lockf lstat lutimes madvise mbrtowc memrchr mkdirat mkfifo mkfifoat \ mknod mknodat mktime mmap mremap nice openat opendir pathconf pause pipe \ @@ -1129,7 +1129,7 @@ index d7c3920d049..6027da5752a 100644 pread preadv preadv2 process_vm_readv \ pthread_cond_timedwait_relative_np pthread_condattr_setclock pthread_init \ pthread_kill pthread_getname_np pthread_setname_np \ -@@ -5160,7 +5290,7 @@ +@@ -5153,7 +5283,7 @@ sched_setparam sched_setscheduler sem_clockwait sem_getvalue sem_open \ sem_timedwait sem_unlink sendfile setegid seteuid setgid sethostname \ setitimer setlocale setpgid setpgrp setpriority setregid setresgid \ @@ -1138,7 +1138,7 @@ index d7c3920d049..6027da5752a 100644 sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ -@@ -5175,12 +5305,20 @@ +@@ -5168,12 +5298,20 @@ AC_CHECK_FUNCS([lchmod]) fi @@ -1162,7 +1162,7 @@ index d7c3920d049..6027da5752a 100644 fi AC_CHECK_DECL([dirfd], -@@ -5434,20 +5572,22 @@ +@@ -5427,20 +5565,22 @@ ]) # check for openpty, login_tty, and forkpty @@ -1199,7 +1199,7 @@ index d7c3920d049..6027da5752a 100644 # check for long file support functions AC_CHECK_FUNCS([fseek64 fseeko fstatvfs ftell64 ftello statvfs]) -@@ -5486,10 +5626,10 @@ +@@ -5479,10 +5619,10 @@ ]) ]) @@ -1212,7 +1212,7 @@ index d7c3920d049..6027da5752a 100644 then AC_CHECK_FUNCS([clock_settime], [], [ AC_CHECK_LIB([rt], [clock_settime], [ -@@ -6240,8 +6380,8 @@ +@@ -6233,8 +6373,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -1223,7 +1223,7 @@ index d7c3920d049..6027da5752a 100644 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -6900,7 +7040,7 @@ +@@ -6893,7 +7033,7 @@ dnl NOTE: Inform user how to proceed with files when cross compiling. dnl Some cross-compile builds are predictable; they won't ever dnl have /dev/ptmx or /dev/ptc, so we can set them explicitly. @@ -1232,7 +1232,7 @@ index d7c3920d049..6027da5752a 100644 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -7156,7 +7296,7 @@ +@@ -7187,7 +7327,7 @@ AS_CASE([$ac_sys_system], [Emscripten], [with_ensurepip=no], [WASI], [with_ensurepip=no], @@ -1241,7 +1241,7 @@ index d7c3920d049..6027da5752a 100644 [with_ensurepip=upgrade] ) ]) -@@ -7567,7 +7707,7 @@ +@@ -7598,7 +7738,7 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], @@ -1270,6 +1270,98 @@ index c3e261ecd9e..26ef7a95de4 100644 CFBundleSupportedPlatforms iPhoneOS +diff --git a/iOS/testbed/__main__.py b/iOS/testbed/__main__.py +index b4499f5ac17..08fbe90a1c6 100644 +--- a/iOS/testbed/__main__.py ++++ b/iOS/testbed/__main__.py +@@ -230,33 +230,69 @@ + shutil.copytree(source, target, symlinks=True) + print(" done") + ++ xc_framework_path = target / "Python.xcframework" ++ sim_framework_path = xc_framework_path / "ios-arm64_x86_64-simulator" + if framework is not None: + if framework.suffix == ".xcframework": + print(" Installing XCFramework...", end="", flush=True) +- xc_framework_path = (target / "Python.xcframework").resolve() + if xc_framework_path.is_dir(): + shutil.rmtree(xc_framework_path) + else: +- xc_framework_path.unlink() ++ xc_framework_path.unlink(missing_ok=True) + xc_framework_path.symlink_to( + framework.relative_to(xc_framework_path.parent, walk_up=True) + ) + print(" done") + else: + print(" Installing simulator framework...", end="", flush=True) +- sim_framework_path = ( +- target / "Python.xcframework" / "ios-arm64_x86_64-simulator" +- ).resolve() + if sim_framework_path.is_dir(): + shutil.rmtree(sim_framework_path) + else: +- sim_framework_path.unlink() ++ sim_framework_path.unlink(missing_ok=True) + sim_framework_path.symlink_to( + framework.relative_to(sim_framework_path.parent, walk_up=True) + ) + print(" done") + else: +- print(" Using pre-existing iOS framework.") ++ if ( ++ xc_framework_path.is_symlink() ++ and not xc_framework_path.readlink().is_absolute() ++ ): ++ # XCFramework is a relative symlink. Rewrite the symlink relative ++ # to the new location. ++ print(" Rewriting symlink to XCframework...", end="", flush=True) ++ orig_xc_framework_path = ( ++ source ++ / xc_framework_path.readlink() ++ ).resolve() ++ xc_framework_path.unlink() ++ xc_framework_path.symlink_to( ++ orig_xc_framework_path.relative_to( ++ xc_framework_path.parent, walk_up=True ++ ) ++ ) ++ print(" done") ++ elif ( ++ sim_framework_path.is_symlink() ++ and not sim_framework_path.readlink().is_absolute() ++ ): ++ print(" Rewriting symlink to simulator framework...", end="", flush=True) ++ # Simulator framework is a relative symlink. Rewrite the symlink ++ # relative to the new location. ++ orig_sim_framework_path = ( ++ source ++ / "Python.XCframework" ++ / sim_framework_path.readlink() ++ ).resolve() ++ sim_framework_path.unlink() ++ sim_framework_path.symlink_to( ++ orig_sim_framework_path.relative_to( ++ sim_framework_path.parent, walk_up=True ++ ) ++ ) ++ print(" done") ++ else: ++ print(" Using pre-existing iOS framework.") + + for app_src in apps: + print(f" Installing app {app_src.name!r}...", end="", flush=True) +@@ -372,8 +408,8 @@ + + if context.subcommand == "clone": + clone_testbed( +- source=Path(__file__).parent, +- target=Path(context.location), ++ source=Path(__file__).parent.resolve(), ++ target=Path(context.location).resolve(), + framework=Path(context.framework).resolve() if context.framework else None, + apps=[Path(app) for app in context.apps], + ) --- /dev/null +++ b/tvOS/README.rst @@ -0,0 +1,108 @@ From c82aa70a26a9b6180089e13f9010b5127d83fc44 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 14 Feb 2025 10:19:08 +0800 Subject: [PATCH 09/26] Modify handling of platform site to allow for venv usage. (#246) Moves the platform site handling into the framework, and adds a mechanism to convert a macOS virtual environment into a cross-platform iOS build environment. --- Makefile | 60 +++++++---- patch/Python/_cross_target.py.tmpl | 71 +++++++++++++ patch/Python/_cross_venv.py | 105 +++++++++++++++++++ patch/Python/make_cross_venv.py | 144 ++++++++++++++++++++++++++ patch/Python/sitecustomize.iOS.py | 114 -------------------- patch/Python/sitecustomize.macOS.py | 14 --- patch/Python/sitecustomize.py.tmpl | 22 ++++ patch/Python/sitecustomize.tvOS.py | 99 ------------------ patch/Python/sitecustomize.watchOS.py | 99 ------------------ 9 files changed, 383 insertions(+), 345 deletions(-) create mode 100644 patch/Python/_cross_target.py.tmpl create mode 100644 patch/Python/_cross_venv.py create mode 100644 patch/Python/make_cross_venv.py delete mode 100644 patch/Python/sitecustomize.iOS.py delete mode 100644 patch/Python/sitecustomize.macOS.py create mode 100644 patch/Python/sitecustomize.py.tmpl delete mode 100644 patch/Python/sitecustomize.tvOS.py delete mode 100644 patch/Python/sitecustomize.watchOS.py diff --git a/Makefile b/Makefile index b34d7e05..765bf39f 100644 --- a/Makefile +++ b/Makefile @@ -129,10 +129,10 @@ ARCH-$(target)=$$(subst .,,$$(suffix $(target))) ifneq ($(os),macOS) ifeq ($$(findstring simulator,$$(SDK-$(target))),) TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(OS_LOWER-$(target))$$(VERSION_MIN-$(os)) -IS_SIMULATOR-$(target)="False" +IS_SIMULATOR-$(target)=False else TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(OS_LOWER-$(target))$$(VERSION_MIN-$(os))-simulator -IS_SIMULATOR-$(target)="True" +IS_SIMULATOR-$(target)=True endif endif @@ -261,6 +261,9 @@ PYTHON_LIB-$(target)=$$(PYTHON_FRAMEWORK-$(target))/Python PYTHON_BIN-$(target)=$$(PYTHON_INSTALL-$(target))/bin PYTHON_INCLUDE-$(target)=$$(PYTHON_FRAMEWORK-$(target))/Headers PYTHON_STDLIB-$(target)=$$(PYTHON_INSTALL-$(target))/lib/python$(PYTHON_VER) +PYTHON_PLATFORM_CONFIG-$(target)=$$(PYTHON_INSTALL-$(target))/platform-config/$$(ARCH-$(target))-$$(SDK-$(target)) +PYTHON_PLATFORM_SITECUSTOMIZE-$(target)=$$(PYTHON_PLATFORM_CONFIG-$(target))/sitecustomize.py + $$(PYTHON_SRCDIR-$(target))/configure: \ downloads/Python-$(PYTHON_VERSION).tar.gz \ @@ -319,23 +322,35 @@ $$(PYTHON_LIB-$(target)): $$(PYTHON_SRCDIR-$(target))/python.exe # Remove any .orig files produced by the compliance patching process find $$(PYTHON_INSTALL-$(target)) -name "*.orig" -exec rm {} \; -endif - -PYTHON_SITECUSTOMIZE-$(target)=$(PROJECT_DIR)/support/$(PYTHON_VER)/$(os)/platform-site/$(target)/sitecustomize.py -$$(PYTHON_SITECUSTOMIZE-$(target)): - @echo ">>> Create cross-platform sitecustomize.py for $(target)" - mkdir -p $$(dir $$(PYTHON_SITECUSTOMIZE-$(target))) - cat $(PROJECT_DIR)/patch/Python/sitecustomize.$(os).py \ +$$(PYTHON_PLATFORM_SITECUSTOMIZE-$(target)): + @echo ">>> Create cross-plaform config for $(target)" + mkdir -p $$(PYTHON_PLATFORM_CONFIG-$(target)) + # Create the cross-platform site definition + echo "import _cross_$$(ARCH-$(target))_$$(SDK-$(target)); import _cross_venv;" \ + > $$(PYTHON_PLATFORM_CONFIG-$(target))/_cross_venv.pth + cp $(PROJECT_DIR)/patch/Python/make_cross_venv.py \ + $$(PYTHON_PLATFORM_CONFIG-$(target))/make_cross_venv.py + cp $(PROJECT_DIR)/patch/Python/_cross_venv.py \ + $$(PYTHON_PLATFORM_CONFIG-$(target))/_cross_venv.py + cp $$(PYTHON_STDLIB-$(target))/_sysconfig* \ + $$(PYTHON_PLATFORM_CONFIG-$(target)) + cat $(PROJECT_DIR)/patch/Python/_cross_target.py.tmpl \ | sed -e "s/{{os}}/$(os)/g" \ + | sed -e "s/{{platform}}/$$(OS_LOWER-$(target))/g" \ | sed -e "s/{{arch}}/$$(ARCH-$(target))/g" \ + | sed -e "s/{{sdk}}/$$(SDK-$(target))/g" \ | sed -e "s/{{version_min}}/$$(VERSION_MIN-$(os))/g" \ | sed -e "s/{{is_simulator}}/$$(IS_SIMULATOR-$(target))/g" \ - | sed -e "s/{{multiarch}}/$$(ARCH-$(target))-$$(SDK-$(target))/g" \ - | sed -e "s/{{tag}}/$$(OS_LOWER-$(target))-$$(VERSION_MIN-$(os))-$$(ARCH-$(target))-$$(SDK-$(target))/g" \ - > $$(PYTHON_SITECUSTOMIZE-$(target)) + > $$(PYTHON_PLATFORM_CONFIG-$(target))/_cross_$$(ARCH-$(target))_$$(SDK-$(target)).py + cat $(PROJECT_DIR)/patch/Python/sitecustomize.py.tmpl \ + | sed -e "s/{{arch}}/$$(ARCH-$(target))/g" \ + | sed -e "s/{{sdk}}/$$(SDK-$(target))/g" \ + > $$(PYTHON_PLATFORM_SITECUSTOMIZE-$(target)) -$(target): $$(PYTHON_SITECUSTOMIZE-$(target)) $$(PYTHON_LIB-$(target)) +endif + +$(target): $$(PYTHON_PLATFORM_SITECUSTOMIZE-$(target)) $$(PYTHON_LIB-$(target)) ########################################################################### # Target: Debug @@ -364,6 +379,8 @@ vars-$(target): @echo "PYTHON_BIN-$(target): $$(PYTHON_BIN-$(target))" @echo "PYTHON_INCLUDE-$(target): $$(PYTHON_INCLUDE-$(target))" @echo "PYTHON_STDLIB-$(target): $$(PYTHON_STDLIB-$(target))" + @echo "PYTHON_PLATFORM_CONFIG-$(target): $$(PYTHON_PLATFORM_CONFIG-$(target))" + @echo "PYTHON_PLATFORM_SITECUSTOMIZE-$(target): $$(PYTHON_PLATFORM_SITECUSTOMIZE-$(target))" @echo endef # build-target @@ -424,6 +441,7 @@ PYTHON_LIB-$(sdk)=$$(PYTHON_FRAMEWORK-$(sdk))/Python PYTHON_BIN-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/bin PYTHON_INCLUDE-$(sdk)=$$(PYTHON_FRAMEWORK-$(sdk))/Headers PYTHON_STDLIB-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/lib/python$(PYTHON_VER) +PYTHON_PLATFORM_CONFIG-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/platform-config $$(PYTHON_LIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_LIB-$$(target))) @echo ">>> Build Python fat library for the $(sdk) SDK" @@ -459,7 +477,7 @@ $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h: $$(PYTHON_LIB-$(sdk)) cp $$(PYTHON_SRCDIR-$$(firstword $$(SDK_TARGETS-$(sdk))))/$(os)/Resources/pyconfig.h $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h -$$(PYTHON_STDLIB-$(sdk))/LICENSE.TXT: $$(PYTHON_LIB-$(sdk)) $$(PYTHON_FRAMEWORK-$(sdk))/Info.plist $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h +$$(PYTHON_STDLIB-$(sdk))/LICENSE.TXT: $$(PYTHON_LIB-$(sdk)) $$(PYTHON_FRAMEWORK-$(sdk))/Info.plist $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_PLATFORM_SITECUSTOMIZE-$$(target))) @echo ">>> Build Python stdlib for the $(sdk) SDK" mkdir -p $$(PYTHON_STDLIB-$(sdk))/lib-dynload # Copy stdlib from the first target associated with the $(sdk) SDK @@ -468,11 +486,17 @@ $$(PYTHON_STDLIB-$(sdk))/LICENSE.TXT: $$(PYTHON_LIB-$(sdk)) $$(PYTHON_FRAMEWORK- # Delete the single-SDK parts of the standard library rm -rf \ $$(PYTHON_STDLIB-$(sdk))/_sysconfigdata__*.py \ + $$(PYTHON_STDLIB-$(sdk))/_sysconfig_vars__*.json \ $$(PYTHON_STDLIB-$(sdk))/config-* \ $$(PYTHON_STDLIB-$(sdk))/lib-dynload/* # Copy the individual _sysconfigdata modules into names that include the architecture $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_STDLIB-$$(target))/_sysconfigdata_* $$(PYTHON_STDLIB-$(sdk))/; ) + $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_STDLIB-$$(target))/_sysconfig_vars_* $$(PYTHON_STDLIB-$(sdk))/; ) + + # Copy the platform site folders for each architecture + mkdir -p $$(PYTHON_PLATFORM_CONFIG-$(sdk)) + $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp -r $$(PYTHON_PLATFORM_CONFIG-$$(target)) $$(PYTHON_PLATFORM_CONFIG-$(sdk)); ) # Merge the binary modules from each target in the $(sdk) SDK into a single binary $$(foreach module,$$(wildcard $$(PYTHON_STDLIB-$$(firstword $$(SDK_TARGETS-$(sdk))))/lib-dynload/*),lipo -create -output $$(PYTHON_STDLIB-$(sdk))/lib-dynload/$$(notdir $$(module)) $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_STDLIB-$$(target))/lib-dynload/$$(notdir $$(module))); ) @@ -581,7 +605,7 @@ support/$(PYTHON_VER)/macOS/VERSIONS: dist/Python-$(PYTHON_VER)-macOS-support.$(BUILD_NUMBER).tar.gz: \ $$(PYTHON_XCFRAMEWORK-macOS)/Info.plist \ support/$(PYTHON_VER)/macOS/VERSIONS \ - $$(foreach target,$$(TARGETS-macOS), $$(PYTHON_SITECUSTOMIZE-$$(target))) + $$(foreach target,$$(TARGETS-macOS), $$(PYTHON_PLATFORM_SITECUSTOMIZE-$$(target))) @echo ">>> Create final distribution artefact for macOS" mkdir -p dist @@ -604,9 +628,7 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/include $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/bin $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/lib $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) - - @echo ">>> Create helper links in XCframework for $(os)" - $$(foreach sdk,$$(SDKS-$(os)),ln -si $$(SDK_SLICE-$$(sdk)) $$(PYTHON_XCFRAMEWORK-$(os))/$$(sdk); ) + $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/platform-config $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) ifeq ($(os),iOS) @echo ">>> Clone testbed project for $(os)" @@ -626,7 +648,7 @@ endif dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz: \ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist \ - $$(foreach target,$$(TARGETS-$(os)), $$(PYTHON_SITECUSTOMIZE-$$(target))) + $$(foreach target,$$(TARGETS-$(os)), $$(PYTHON_PLATFORM_SITECUSTOMIZE-$$(target))) @echo ">>> Create final distribution artefact for $(os)" mkdir -p dist diff --git a/patch/Python/_cross_target.py.tmpl b/patch/Python/_cross_target.py.tmpl new file mode 100644 index 00000000..9f75af53 --- /dev/null +++ b/patch/Python/_cross_target.py.tmpl @@ -0,0 +1,71 @@ +# A site package that turns a macOS virtual environment +# into an {{arch}} {{sdk}} cross-platform virtual environment +import platform +import subprocess +import sys +import sysconfig + +########################################################################### +# sys module patches +########################################################################### +sys.cross_compiling = True +sys.platform = "{{platform}}" +sys.implementation._multiarch = "{{arch}}-{{sdk}}" + +########################################################################### +# subprocess module patches +########################################################################### +subprocess._can_fork_exec = True + + +########################################################################### +# platform module patches +########################################################################### + +def cross_system(): + return "{{os}}" + + +def cross_uname(): + return platform.uname_result( + system="{{os}}", + node="build", + release="{{version_min}}", + version="", + machine="{{arch}}", + ) + + +def cross_ios_ver(system="", release="", model="", is_simulator=False): + if system == "": + system = "{{os}}" + if release == "": + release = "{{version_min}}" + if model == "": + model = "{{sdk}}" + + return platform.IOSVersionInfo(system, release, model, {{is_simulator}}) + + +platform.system = cross_system +platform.uname = cross_uname +platform.ios_ver = cross_ios_ver + + +########################################################################### +# sysconfig module patches +########################################################################### + +def cross_get_platform(): + return "{{platform}}-{{version_min}}-{{arch}}-{{sdk}}" + + +def cross_get_sysconfigdata_name(): + return "_sysconfigdata__{{platform}}_{{arch}}-{{sdk}}" + + +sysconfig.get_platform = cross_get_platform +sysconfig._get_sysconfigdata_name = cross_get_sysconfigdata_name + +# Force sysconfig data to be loaded (and cached). +sysconfig._init_config_vars() diff --git a/patch/Python/_cross_venv.py b/patch/Python/_cross_venv.py new file mode 100644 index 00000000..9caddf60 --- /dev/null +++ b/patch/Python/_cross_venv.py @@ -0,0 +1,105 @@ +import shutil +import sys +import sysconfig +from pathlib import Path + +SITE_PACKAGE_PATH = Path(__file__).parent + +########################################################################### +# importlib module patches +########################################################################### + + +def patch_env_create(env): + """ + Patch the process of creating virtual environments to ensure that the cross + environment modification files are also copied as part of environment + creation. + """ + old_pip_env_create = env._PipBackend.create + + def pip_env_create(self, path, *args, **kwargs): + result = old_pip_env_create(self, path, *args, **kwargs) + # Copy any _cross_*.pth or _cross_*.py file, plus the cross-platform + # sysconfigdata module and sysconfig_vars JSON to the new environment. + data_name = sysconfig._get_sysconfigdata_name() + json_name = data_name.replace("_sysconfigdata", "_sysconfig_vars") + for filename in [ + "_cross_venv.pth", + "_cross_venv.py", + f"_cross_{sys.implementation._multiarch.replace('-', '_')}.py", + f"{data_name}.py", + f"{json_name}.json", + ]: + src = SITE_PACKAGE_PATH / filename + target = Path(path) / src.relative_to( + SITE_PACKAGE_PATH.parent.parent.parent + ) + if not target.exists(): + shutil.copy(src, target) + return result + + env._PipBackend.create = pip_env_create + + +# Import hook that patches the creation of virtual environments by `build` +# +# The approach used here is the same as the one used by virtualenv to patch +# distutils (but without support for the older load_module API). +# https://docs.python.org/3/library/importlib.html#setting-up-an-importer +_BUILD_PATCH = ("build.env",) + + +class _Finder: + """A meta path finder that allows patching the imported build modules.""" + + fullname = None + + # lock[0] is threading.Lock(), but initialized lazily to avoid importing + # threading very early at startup, because there are gevent-based + # applications that need to be first to import threading by themselves. + # See https://github.com/pypa/virtualenv/issues/1895 for details. + lock = [] # noqa: RUF012 + + def find_spec(self, fullname, path, target=None): + if fullname in _BUILD_PATCH and self.fullname is None: + # initialize lock[0] lazily + if len(self.lock) == 0: + import threading + + lock = threading.Lock() + # there is possibility that two threads T1 and T2 are + # simultaneously running into find_spec, observing .lock as + # empty, and further going into hereby initialization. However + # due to the GIL, list.append() operation is atomic and this + # way only one of the threads will "win" to put the lock + # - that every thread will use - into .lock[0]. + # https://docs.python.org/3/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe + self.lock.append(lock) + + from functools import partial + from importlib.util import find_spec + + with self.lock[0]: + self.fullname = fullname + try: + spec = find_spec(fullname, path) + if spec is not None: + # https://www.python.org/dev/peps/pep-0451/#how-loading-will-work + old = spec.loader.exec_module + func = self.exec_module + if old is not func: + spec.loader.exec_module = partial(func, old) + return spec + finally: + self.fullname = None + return None + + @staticmethod + def exec_module(old, module): + old(module) + if module.__name__ in _BUILD_PATCH: + patch_env_create(module) + + +sys.meta_path.insert(0, _Finder()) diff --git a/patch/Python/make_cross_venv.py b/patch/Python/make_cross_venv.py new file mode 100644 index 00000000..f8dad270 --- /dev/null +++ b/patch/Python/make_cross_venv.py @@ -0,0 +1,144 @@ +import json +import pprint +import shutil +import sys +from pathlib import Path +from importlib import util as importlib_util + + +def localized_vars(orig_vars, slice_path): + """Update (where possible) any references to build-time variables with the + best guess of the installed location. + """ + # The host's sysconfigdata will include references to build-time variables. + # Update these to refer to the current known install location. + orig_prefix = orig_vars["prefix"] + localized_vars = {} + for key, value in orig_vars.items(): + final = value + if isinstance(value, str): + # Replace any reference to the build installation prefix + final = final.replace(orig_prefix, str(slice_path)) + # Replace any reference to the build-time Framework location + final = final.replace("-F .", f"-F {slice_path}") + localized_vars[key] = final + + return localized_vars + + +def localize_sysconfigdata(platform_config_path, venv_site_packages): + """Localize a sysconfigdata python module. + + :param platform_config_path: The platform config that contains the + sysconfigdata module to localize. + :param venv_site_packages: The site packages folder where the localized + sysconfigdata module should be output. + """ + # Find the "_sysconfigdata_*.py" file in the platform config + sysconfigdata_path = next(platform_config_path.glob("_sysconfigdata_*.py")) + + # Import the sysconfigdata module + spec = importlib_util.spec_from_file_location( + sysconfigdata_path.stem, + sysconfigdata_path + ) + if spec is None: + msg = f"Unable to load spec for {sysconfigdata_path}" + raise ValueError(msg) + if spec.loader is None: + msg = f"Spec for {sysconfigdata_path} does not define a loader" + raise ValueError(msg) + sysconfigdata = importlib_util.module_from_spec(spec) + spec.loader.exec_module(sysconfigdata) + + # Write the updated sysconfigdata module into the cross-platform site. + slice_path = sysconfigdata_path.parent.parent.parent + with (venv_site_packages / sysconfigdata_path.name).open("w") as f: + f.write(f"# Generated from {sysconfigdata_path}\n") + f.write("build_time_vars = ") + pprint.pprint( + localized_vars(sysconfigdata.build_time_vars, slice_path), + stream=f, + compact=True + ) + + +def localize_sysconfig_vars(platform_config_path, venv_site_packages): + """Localize a sysconfig_vars.json file. + + :param platform_config_path: The platform config that contains the + sysconfigdata module to localize. + :param venv_site_packages: The site-packages folder where the localized + sysconfig_vars.json file should be output. + """ + # Find the "_sysconfig_vars_*.json" file in the platform config + sysconfig_vars_path = next(platform_config_path.glob("_sysconfig_vars_*.json")) + + with sysconfig_vars_path.open("rb") as f: + build_time_vars = json.load(f) + + slice_path = sysconfig_vars_path.parent.parent.parent + with (venv_site_packages / sysconfig_vars_path.name).open("w") as f: + json.dump(localized_vars(build_time_vars, slice_path), f, indent=2) + + +def make_cross_venv(venv_path: Path, platform_config_path: Path): + """Convert a virtual environment into a cross-platform environment. + + :param venv_path: The path to the root of the venv. + :param platform_config_path: The path containing the platform config. + """ + if not venv_path.exists(): + raise ValueError(f"Virtual environment {venv_path} does not exist.") + if not (venv_path / "bin/python3").exists(): + raise ValueError(f"{venv_path} does not appear to be a virtual environment.") + + print( + f"Converting {venv_path} into a {platform_config_path.name} environment... ", + end="", + ) + + LIB_PATH = f"lib/python{sys.version_info[0]}.{sys.version_info[1]}" + + # Update path references in the sysconfigdata to reflect local conditions. + venv_site_packages = venv_path / LIB_PATH / "site-packages" + localize_sysconfigdata(platform_config_path, venv_site_packages) + localize_sysconfig_vars(platform_config_path, venv_site_packages) + + # Copy in the site-package environment modifications. + cross_multiarch = f"_cross_{platform_config_path.name.replace('-', '_')}" + shutil.copy( + platform_config_path / f"{cross_multiarch}.py", + venv_site_packages / f"{cross_multiarch}.py", + ) + shutil.copy( + platform_config_path / "_cross_venv.py", + venv_site_packages / "_cross_venv.py", + ) + # Write the .pth file that will enable the cross-env modifications + (venv_site_packages / "_cross_venv.pth").write_text( + f"import {cross_multiarch}; import _cross_venv\n" + ) + + print("done.") + + +if __name__ == "__main__": + try: + platform_config_path = Path(sys.argv[2]).resolve() + except IndexError: + platform_config_path = Path(__file__).parent + + try: + venv_path = Path(sys.argv[1]).resolve() + make_cross_venv(venv_path, platform_config_path) + except IndexError: + print(""" +Convert a virtual environment in to a cross-platform environment. + +Usage: + make_cross_venv () + +If an explicit platform config isn't provided, it is assumed the directory +containing the make_cross_venv script *is* a platform config. +""") diff --git a/patch/Python/sitecustomize.iOS.py b/patch/Python/sitecustomize.iOS.py deleted file mode 100644 index ccc291f3..00000000 --- a/patch/Python/sitecustomize.iOS.py +++ /dev/null @@ -1,114 +0,0 @@ -# A site customization that can be used to trick pip into installing -# packages cross-platform. If the folder containing this file is on -# your PYTHONPATH when you invoke pip, pip will behave as if it were -# running on {{os}}. -import collections -import distutils.ccompiler -import distutils.unixccompiler -import os -import platform -import sys -import sysconfig -import types - -# Make platform.system() return "{{os}}" -def custom_system(): - return "{{os}}" - -platform.system = custom_system - -# Make platform.ios_ver() return an appropriate namedtuple -IOSVersionInfo = collections.namedtuple( - "IOSVersionInfo", - ["system", "release", "model", "is_simulator"] -) - -def custom_ios_ver(system="", release="", model="", is_simulator=False): - return IOSVersionInfo("{{os}}", "{{version_min}}", "iPhone", {{is_simulator}}) - -platform.ios_ver = custom_ios_ver - -# Make sys.implementation._multiarch return the multiarch description -sys.implementation._multiarch = "{{multiarch}}" - -# Make sysconfig.get_platform() return the platform tag -def custom_get_platform(): - return "{{tag}}" - -sysconfig.get_platform = custom_get_platform - -# Make distutils raise errors if you try to use it to build modules. -DISABLED_COMPILER_ERROR = "Cannot compile native modules" - -distutils.ccompiler.get_default_compiler = lambda *args, **kwargs: "disabled" -distutils.ccompiler.compiler_class["disabled"] = ( - "disabledcompiler", - "DisabledCompiler", - "Compiler disabled ({})".format(DISABLED_COMPILER_ERROR), -) - - -def disabled_compiler(prefix): - # No need to give any more advice here: that will come from the higher-level code in pip. - from distutils.errors import DistutilsPlatformError - - raise DistutilsPlatformError("{}: {}".format(prefix, DISABLED_COMPILER_ERROR)) - - -class DisabledCompiler(distutils.ccompiler.CCompiler): - compiler_type = "disabled" - - def preprocess(*args, **kwargs): - disabled_compiler("CCompiler.preprocess") - - def compile(*args, **kwargs): - disabled_compiler("CCompiler.compile") - - def create_static_lib(*args, **kwargs): - disabled_compiler("CCompiler.create_static_lib") - - def link(*args, **kwargs): - disabled_compiler("CCompiler.link") - - -# To maximize the chance of the build getting as far as actually calling compile(), make -# sure the class has all of the expected attributes. -for name in [ - "src_extensions", - "obj_extension", - "static_lib_extension", - "shared_lib_extension", - "static_lib_format", - "shared_lib_format", - "exe_extension", -]: - setattr( - DisabledCompiler, name, getattr(distutils.unixccompiler.UnixCCompiler, name) - ) - -DisabledCompiler.executables = { - name: [DISABLED_COMPILER_ERROR.replace(" ", "_")] - for name in distutils.unixccompiler.UnixCCompiler.executables -} - -disabled_mod = types.ModuleType("distutils.disabledcompiler") -disabled_mod.DisabledCompiler = DisabledCompiler -sys.modules["distutils.disabledcompiler"] = disabled_mod - - -# Try to disable native builds for packages which don't use the distutils native build -# system at all (e.g. uwsgi), or only use it to wrap an external build script (e.g. pynacl). -for tool in ["ar", "as", "cc", "cxx", "ld"]: - os.environ[tool.upper()] = DISABLED_COMPILER_ERROR.replace(" ", "_") - - -# Call the next sitecustomize script if there is one -# (https://nedbatchelder.com/blog/201001/running_code_at_python_startup.html). -del sys.modules["sitecustomize"] -this_dir = os.path.dirname(__file__) -path_index = sys.path.index(this_dir) -del sys.path[path_index] -try: - import sitecustomize # noqa: F401 -finally: - sys.path.insert(path_index, this_dir) diff --git a/patch/Python/sitecustomize.macOS.py b/patch/Python/sitecustomize.macOS.py deleted file mode 100644 index 500714da..00000000 --- a/patch/Python/sitecustomize.macOS.py +++ /dev/null @@ -1,14 +0,0 @@ -# A site customization that can be used to trick pip into installing -# packages cross-platform. If the folder containing this file is on -# your PYTHONPATH when you invoke pip, pip will behave as if it were -# running on {{arch}}. -import platform - -# Make platform.mac_ver() return {{arch}} -orig_mac_ver = platform.mac_ver - -def custom_mac_ver(): - orig = orig_mac_ver() - return orig[0], orig[1], "{{arch}}" - -platform.mac_ver = custom_mac_ver diff --git a/patch/Python/sitecustomize.py.tmpl b/patch/Python/sitecustomize.py.tmpl new file mode 100644 index 00000000..0330575a --- /dev/null +++ b/patch/Python/sitecustomize.py.tmpl @@ -0,0 +1,22 @@ +# A site customization that can be used to trick pip into installing packages +# cross-platform. If the folder containing this file is on your PYTHONPATH when +# you invoke python, the interpreter will behave as if it were running on +# {{arch}} {{sdk}}. +import sys +import os + +# Apply the cross-platform patch +import _cross_{{arch}}_{{sdk}} +import _cross_venv + + +# Call the next sitecustomize script if there is one +# (https://nedbatchelder.com/blog/201001/running_code_at_python_startup.html). +del sys.modules["sitecustomize"] +this_dir = os.path.dirname(__file__) +path_index = sys.path.index(this_dir) +del sys.path[path_index] +try: + import sitecustomize # noqa: F401 +finally: + sys.path.insert(path_index, this_dir) diff --git a/patch/Python/sitecustomize.tvOS.py b/patch/Python/sitecustomize.tvOS.py deleted file mode 100644 index d7d86e36..00000000 --- a/patch/Python/sitecustomize.tvOS.py +++ /dev/null @@ -1,99 +0,0 @@ -# A site customization that can be used to trick pip into installing -# packages cross-platform. If the folder containing this file is on -# your PYTHONPATH when you invoke pip, pip will behave as if it were -# running on {{os}}. -import distutils.ccompiler -import distutils.unixccompiler -import os -import platform -import sys -import sysconfig -import types - -# Make platform.system() return "{{os}}" -def custom_system(): - return "{{os}}" - -platform.system = custom_system - -# Make sysconfig.get_platform() return "{{tag}}" -def custom_get_platform(): - return "{{tag}}" - -sysconfig.get_platform = custom_get_platform - -# Make distutils raise errors if you try to use it to build modules. -DISABLED_COMPILER_ERROR = "Cannot compile native modules" - -distutils.ccompiler.get_default_compiler = lambda *args, **kwargs: "disabled" -distutils.ccompiler.compiler_class["disabled"] = ( - "disabledcompiler", - "DisabledCompiler", - "Compiler disabled ({})".format(DISABLED_COMPILER_ERROR), -) - - -def disabled_compiler(prefix): - # No need to give any more advice here: that will come from the higher-level code in pip. - from distutils.errors import DistutilsPlatformError - - raise DistutilsPlatformError("{}: {}".format(prefix, DISABLED_COMPILER_ERROR)) - - -class DisabledCompiler(distutils.ccompiler.CCompiler): - compiler_type = "disabled" - - def preprocess(*args, **kwargs): - disabled_compiler("CCompiler.preprocess") - - def compile(*args, **kwargs): - disabled_compiler("CCompiler.compile") - - def create_static_lib(*args, **kwargs): - disabled_compiler("CCompiler.create_static_lib") - - def link(*args, **kwargs): - disabled_compiler("CCompiler.link") - - -# To maximize the chance of the build getting as far as actually calling compile(), make -# sure the class has all of the expected attributes. -for name in [ - "src_extensions", - "obj_extension", - "static_lib_extension", - "shared_lib_extension", - "static_lib_format", - "shared_lib_format", - "exe_extension", -]: - setattr( - DisabledCompiler, name, getattr(distutils.unixccompiler.UnixCCompiler, name) - ) - -DisabledCompiler.executables = { - name: [DISABLED_COMPILER_ERROR.replace(" ", "_")] - for name in distutils.unixccompiler.UnixCCompiler.executables -} - -disabled_mod = types.ModuleType("distutils.disabledcompiler") -disabled_mod.DisabledCompiler = DisabledCompiler -sys.modules["distutils.disabledcompiler"] = disabled_mod - - -# Try to disable native builds for packages which don't use the distutils native build -# system at all (e.g. uwsgi), or only use it to wrap an external build script (e.g. pynacl). -for tool in ["ar", "as", "cc", "cxx", "ld"]: - os.environ[tool.upper()] = DISABLED_COMPILER_ERROR.replace(" ", "_") - - -# Call the next sitecustomize script if there is one -# (https://nedbatchelder.com/blog/201001/running_code_at_python_startup.html). -del sys.modules["sitecustomize"] -this_dir = os.path.dirname(__file__) -path_index = sys.path.index(this_dir) -del sys.path[path_index] -try: - import sitecustomize # noqa: F401 -finally: - sys.path.insert(path_index, this_dir) diff --git a/patch/Python/sitecustomize.watchOS.py b/patch/Python/sitecustomize.watchOS.py deleted file mode 100644 index d7d86e36..00000000 --- a/patch/Python/sitecustomize.watchOS.py +++ /dev/null @@ -1,99 +0,0 @@ -# A site customization that can be used to trick pip into installing -# packages cross-platform. If the folder containing this file is on -# your PYTHONPATH when you invoke pip, pip will behave as if it were -# running on {{os}}. -import distutils.ccompiler -import distutils.unixccompiler -import os -import platform -import sys -import sysconfig -import types - -# Make platform.system() return "{{os}}" -def custom_system(): - return "{{os}}" - -platform.system = custom_system - -# Make sysconfig.get_platform() return "{{tag}}" -def custom_get_platform(): - return "{{tag}}" - -sysconfig.get_platform = custom_get_platform - -# Make distutils raise errors if you try to use it to build modules. -DISABLED_COMPILER_ERROR = "Cannot compile native modules" - -distutils.ccompiler.get_default_compiler = lambda *args, **kwargs: "disabled" -distutils.ccompiler.compiler_class["disabled"] = ( - "disabledcompiler", - "DisabledCompiler", - "Compiler disabled ({})".format(DISABLED_COMPILER_ERROR), -) - - -def disabled_compiler(prefix): - # No need to give any more advice here: that will come from the higher-level code in pip. - from distutils.errors import DistutilsPlatformError - - raise DistutilsPlatformError("{}: {}".format(prefix, DISABLED_COMPILER_ERROR)) - - -class DisabledCompiler(distutils.ccompiler.CCompiler): - compiler_type = "disabled" - - def preprocess(*args, **kwargs): - disabled_compiler("CCompiler.preprocess") - - def compile(*args, **kwargs): - disabled_compiler("CCompiler.compile") - - def create_static_lib(*args, **kwargs): - disabled_compiler("CCompiler.create_static_lib") - - def link(*args, **kwargs): - disabled_compiler("CCompiler.link") - - -# To maximize the chance of the build getting as far as actually calling compile(), make -# sure the class has all of the expected attributes. -for name in [ - "src_extensions", - "obj_extension", - "static_lib_extension", - "shared_lib_extension", - "static_lib_format", - "shared_lib_format", - "exe_extension", -]: - setattr( - DisabledCompiler, name, getattr(distutils.unixccompiler.UnixCCompiler, name) - ) - -DisabledCompiler.executables = { - name: [DISABLED_COMPILER_ERROR.replace(" ", "_")] - for name in distutils.unixccompiler.UnixCCompiler.executables -} - -disabled_mod = types.ModuleType("distutils.disabledcompiler") -disabled_mod.DisabledCompiler = DisabledCompiler -sys.modules["distutils.disabledcompiler"] = disabled_mod - - -# Try to disable native builds for packages which don't use the distutils native build -# system at all (e.g. uwsgi), or only use it to wrap an external build script (e.g. pynacl). -for tool in ["ar", "as", "cc", "cxx", "ld"]: - os.environ[tool.upper()] = DISABLED_COMPILER_ERROR.replace(" ", "_") - - -# Call the next sitecustomize script if there is one -# (https://nedbatchelder.com/blog/201001/running_code_at_python_startup.html). -del sys.modules["sitecustomize"] -this_dir = os.path.dirname(__file__) -path_index = sys.path.index(this_dir) -del sys.path[path_index] -try: - import sitecustomize # noqa: F401 -finally: - sys.path.insert(path_index, this_dir) From 5cd3512edbcf817ea9adbcfb89473e83981cd2d8 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 14 Feb 2025 16:36:48 +0800 Subject: [PATCH 10/26] Improve support for PythonKit, and update usage guide (#248) * Add a Swift modulemap to the generated frameworks. * Updates usage information to reflect current package structure. --- Makefile | 6 ++++++ USAGE.md | 36 +++++++++++++++-------------------- patch/Python/module.modulemap | 5 +++++ 3 files changed, 26 insertions(+), 21 deletions(-) create mode 100644 patch/Python/module.modulemap diff --git a/Makefile b/Makefile index 765bf39f..b0ba7299 100644 --- a/Makefile +++ b/Makefile @@ -466,6 +466,9 @@ $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h: $$(PYTHON_LIB-$(sdk)) # Copy headers as-is from the first target in the $(sdk) SDK cp -r $$(PYTHON_INCLUDE-$$(firstword $$(SDK_TARGETS-$(sdk)))) $$(PYTHON_INCLUDE-$(sdk)) + # Copy in the modulemap file + cp -r patch/Python/module.modulemap $$(PYTHON_INCLUDE-$(sdk)) + # Link the PYTHONHOME version of the headers mkdir -p $$(PYTHON_INSTALL-$(sdk))/include ln -si ../Python.framework/Headers $$(PYTHON_INSTALL-$(sdk))/include/python$(PYTHON_VER) @@ -582,6 +585,9 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ # Rewrite the framework to make it standalone patch/make-relocatable.sh $$(PYTHON_INSTALL_VERSION-macosx) 2>&1 > /dev/null + # Copy in the modulemap file + cp -r patch/Python/module.modulemap $$(PYTHON_FRAMEWORK-macosx)/Headers + # Re-apply the signature on the binaries. codesign -s - --preserve-metadata=identifier,entitlements,flags,runtime -f $$(PYTHON_LIB-macosx) \ 2>&1 | tee $$(PYTHON_INSTALL-macosx)/python-$(os).codesign.log diff --git a/USAGE.md b/USAGE.md index 12bc1dc7..40bbcf80 100644 --- a/USAGE.md +++ b/USAGE.md @@ -20,7 +20,7 @@ what Briefcase is doing). The steps required are documented in the CPython usage guides: * [macOS](https://docs.python.org/3/using/mac.html) -* [iOS](https://docs.python.org/3.14/using/ios.html) +* [iOS](https://docs.python.org/3/using/ios.html#adding-python-to-an-ios-project) For tvOS and watchOS, you should be able to broadly follow the instructions in the iOS guide. @@ -29,7 +29,7 @@ the iOS guide. There are 2 ways to access the Python runtime in your project code. -### Embedded C API. +### Embedded C API You can use the [Python Embedded C API](https://docs.python.org/3/extending/embedding.html) to instantiate a Python @@ -43,37 +43,31 @@ An alternate approach is to use [PythonKit](https://github.com/pvieito/PythonKit). PythonKit is a package that provides a Swift API to running Python code. -To use PythonKit in your project: +To use PythonKit in your project, add the Python Apple Support package to your +project as described above; then: 1. Add PythonKit to your project using the Swift Package manager. See the PythonKit documentation for details. -2. Create a file called `module.modulemap` inside - `Python.xcframework/macos-arm64_x86_64/Headers/`, containing the following - code: -``` -module Python { - umbrella header "Python.h" - export * - link "Python" -} -``` - -3. In your Swift code, initialize the Python runtime. This should generally be +2. In your Swift code, initialize the Python runtime. This should generally be done as early as possible in the application's lifecycle, but definitely - needs to be done before you invoke Python code: + needs to be done before you invoke Python code. References to a specific + Python version should reflect the version of Python you are using: ```swift import Python -guard let stdLibPath = Bundle.main.path(forResource: "python-stdlib", ofType: nil) else { return } -guard let libDynloadPath = Bundle.main.path(forResource: "python-stdlib/lib-dynload", ofType: nil) else { return } -setenv("PYTHONHOME", stdLibPath, 1) -setenv("PYTHONPATH", "\(stdLibPath):\(libDynloadPath)", 1) +guard let pythonHome = Bundle.main.path(forResource: "python", ofType: nil) else { return } +guard let pythonPath = Bundle.main.path(forResource: "python/lib/python3.13", ofType: nil) else { return } +guard let libDynloadPath = Bundle.main.path(forResource: "python/lib/python3.13/lib-dynload", ofType: nil) else { return } +let appPath = Bundle.main.path(forResource: "app", ofType: nil) + +setenv("PYTHONHOME", pythonHome, 1) +setenv("PYTHONPATH", [pythonPath, libDynloadPath, appPath].compactMap { $0 }.joined(separator: ":"), 1) Py_Initialize() // we now have a Python interpreter ready to be used ``` -5. Invoke Python code in your app. For example: +3. Invoke Python code in your app. For example: ```swift import PythonKit diff --git a/patch/Python/module.modulemap b/patch/Python/module.modulemap new file mode 100644 index 00000000..9a4dcbb8 --- /dev/null +++ b/patch/Python/module.modulemap @@ -0,0 +1,5 @@ +module Python { + umbrella header "Python.h" + export * + link "Python" +} From 2f2f220c760f34028c775f028f8fe6cdd9228d95 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 17 Feb 2025 14:38:25 +0800 Subject: [PATCH 11/26] More updates to USAGE and README (#250) * Adds details on `python-config` and `testbed` * Adds a bare bones interpreter instantiation example * Clarifies the difference between instantiating the interpreter, and accessing the Python API with PythonKit * Adds header exclusions to the modulemap to silence build warnings. --- README.rst | 35 ++++++-- USAGE.md | 113 ++++++++++++++++-------- patch/Python/module.modulemap | 160 ++++++++++++++++++++++++++++++++++ 3 files changed, 265 insertions(+), 43 deletions(-) diff --git a/README.rst b/README.rst index 94c0ae94..2317b749 100644 --- a/README.rst +++ b/README.rst @@ -83,15 +83,6 @@ Each support package contains: * ``VERSIONS``, a text file describing the specific versions of code used to build the support package; -* ``platform-site``, a folder that contains site customization scripts that can be used - to make your local Python install look like it is an on-device install for each of the - underlying target architectures supported by the platform. This is needed because when - you run ``pip`` you'll be on a macOS machine with a specific architecture; if ``pip`` - tries to install a binary package, it will install a macOS binary wheel (which won't - work on iOS/tvOS/watchOS). However, if you add the ``platform-site`` folder to your - ``PYTHONPATH`` when invoking pip, the site customization will make your Python install - return ``platform`` and ``sysconfig`` responses consistent with on-device behavior, - which will cause ``pip`` to install platform-appropriate packages. * ``Python.xcframework``, a multi-architecture build of the Python runtime library On iOS/tvOS/watchOS, the ``Python.xcframework`` contains a @@ -105,6 +96,32 @@ needed to build packages. This is required because Xcode uses the ``xcrun`` alias to dynamically generate the name of binaries, but a lot of C tooling expects that ``CC`` will not contain spaces. +Each slice of an iOS/tvOS/watchOS XCframework also contains a +``platform-config`` folder with a subfolder for each supported architecture in +that slice. These subfolders can be used to make a macOS Python environment +behave as if it were on an iOS/tvOS/watchOS device. This works in one of two +ways: + +1. **A sitecustomize.py script**. If the ``platform-config`` subfolder is on + your ``PYTHONPATH`` when a Python interpreter is started, a site + customization will be applied that patches methods in ``sys``, ``sysconfig`` + and ``platform`` that are used to identify the system. + +2. **A make_cross_venv.py script**. If you call ``make_cross_venv.py``, + providing the location of a virtual environment, the script will add some + files to the ``site-packages`` folder of that environment that will + automatically apply the same set of patches as the ``sitecustomize.py`` + script whenever the environment is activated, without any need to modify + ``PYTHONPATH``. If you use ``build`` to create an isolated PEP 517 + environment to build a wheel, these patches will also be applied to the + isolated build environment that is created. + +iOS distributions also contain a copy of the iOS ``testbed`` project - an Xcode +project that can be used to run test suites of Python code. See the `CPython +documentation on testing packages +`__ for +details on how to use this testbed. + For a detailed instructions on using the support package in your own project, see the `usage guide <./USAGE.md>`__ diff --git a/USAGE.md b/USAGE.md index 40bbcf80..096f71b0 100644 --- a/USAGE.md +++ b/USAGE.md @@ -25,6 +25,75 @@ guides: For tvOS and watchOS, you should be able to broadly follow the instructions in the iOS guide. +### Using Objective C + +Once you've added the Python XCframework to your project, you'll need to +initialize the Python runtime in your Objective C code (This is step 10 of the +iOS guide linked above). This initialization should generally be done as early +as possible in the application's lifecycle, but definitely needs to be done +before you invoke Python code. + +As a *bare minimum*, you can do the following: + +1. Import the Python C API headers: + ```objc + #include + ``` + +2. Initialize the Python interpreter: + ```objc + NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; + NSString *pythonHome = [NSString stringWithFormat:@"%@/python", resourcePath, nil]; + NSString *pythonPath = [NSString stringWithFormat:@"%@/lib/python3.13", python_home, nil]; + NSString *libDynloadPath = [NSString stringWithFormat:@"%@/lib/python3.13/lib-dynload", python_home, nil]; + NSString *appPath = [NSString stringWithFormat:@"%@/app", resourcePath, nil]; + + setenv("PYTHONHOME", pythonHome, 1); + setenv("PYTHONPATH", [NSString stringWithFormat:@"%@:%@:%@", pythonpath, libDynloadPath, appPath, nil]); + + Py_Initialize(); + + // we now have a Python interpreter ready to be used + ``` + References to a specific Python version should reflect the version of + Python you are using. + +Again - this is the *bare minimum* initialization. In practice, you will likely +need to configure other aspects of the Python interpreter using the +`PyPreConfig` and `PyConfig` mechanisms. Consult the [Python documentation on +interpreter configuration](https://docs.python.org/3/c-api/init_config.html) for +more details on the configuration options that are available. You may find the +[bootstrap mainline code used by +Briefcase](https://github.com/beeware/briefcase-iOS-Xcode-template/blob/main/%7B%7B%20cookiecutter.format%20%7D%7D/%7B%7B%20cookiecutter.class_name%20%7D%7D/main.m) +a helpful point of comparison. + +### Using Swift + +If you want to use Swift instead of Objective C, the bare minimum initialization +code will look something like this: + +1. Import the Python framework: + ```swift + import Python + ``` + +2. Initialize the Python interpreter: + ```swift + guard let pythonHome = Bundle.main.path(forResource: "python", ofType: nil) else { return } + guard let pythonPath = Bundle.main.path(forResource: "python/lib/python3.13", ofType: nil) else { return } + guard let libDynloadPath = Bundle.main.path(forResource: "python/lib/python3.13/lib-dynload", ofType: nil) else { return } + let appPath = Bundle.main.path(forResource: "app", ofType: nil) + + setenv("PYTHONHOME", pythonHome, 1) + setenv("PYTHONPATH", [pythonPath, libDynloadPath, appPath].compactMap { $0 }.joined(separator: ":"), 1) + Py_Initialize() + // we now have a Python interpreter ready to be used + ``` + + Again, references to a specific Python version should reflect the version of + Python you are using; and you will likely need to use `PyPreConfig` and + `PreConfig` APIs. + ## Accessing the Python runtime There are 2 ways to access the Python runtime in your project code. @@ -32,53 +101,29 @@ There are 2 ways to access the Python runtime in your project code. ### Embedded C API You can use the [Python Embedded C -API](https://docs.python.org/3/extending/embedding.html) to instantiate a Python -interpreter. This is the approach taken by Briefcase; you may find the bootstrap -mainline code generated by Briefcase a helpful guide to what is needed to start -an interpreter and run Python code. +API](https://docs.python.org/3/extending/embedding.html) to invoke Python code +and interact with Python objects. This is a raw C API that is accesible to both +Objective C and Swift. ### PythonKit -An alternate approach is to use +If you're using Swift, an alternate approach is to use [PythonKit](https://github.com/pvieito/PythonKit). PythonKit is a package that provides a Swift API to running Python code. To use PythonKit in your project, add the Python Apple Support package to your -project as described above; then: - -1. Add PythonKit to your project using the Swift Package manager. See the - PythonKit documentation for details. +project and instantiate a Python interpreter as described above; then add +PythonKit to your project using the Swift Package manager (see the [PythonKit +documentation](https://github.com/pvieito/PythonKit) for details). -2. In your Swift code, initialize the Python runtime. This should generally be - done as early as possible in the application's lifecycle, but definitely - needs to be done before you invoke Python code. References to a specific - Python version should reflect the version of Python you are using: +Once you've done this, you can import PythonKit: ```swift -import Python - -guard let pythonHome = Bundle.main.path(forResource: "python", ofType: nil) else { return } -guard let pythonPath = Bundle.main.path(forResource: "python/lib/python3.13", ofType: nil) else { return } -guard let libDynloadPath = Bundle.main.path(forResource: "python/lib/python3.13/lib-dynload", ofType: nil) else { return } -let appPath = Bundle.main.path(forResource: "app", ofType: nil) - -setenv("PYTHONHOME", pythonHome, 1) -setenv("PYTHONPATH", [pythonPath, libDynloadPath, appPath].compactMap { $0 }.joined(separator: ":"), 1) -Py_Initialize() -// we now have a Python interpreter ready to be used +import PythonKit ``` - -3. Invoke Python code in your app. For example: +and use the PythonKit Swift API to interact with Python code: ```swift -import PythonKit - let sys = Python.import("sys") print("Python Version: \(sys.version_info.major).\(sys.version_info.minor)") print("Python Encoding: \(sys.getdefaultencoding().upper())") print("Python Path: \(sys.path)") - -_ = Python.import("math") // verifies `lib-dynload` is found and signed successfully ``` - -To integrate 3rd party python code and dependencies, you will need to make sure -`PYTHONPATH` contains their paths; once this has been done, you can run -`Python.import("")`. to import that module from inside swift. diff --git a/patch/Python/module.modulemap b/patch/Python/module.modulemap index 9a4dcbb8..bc3b19e9 100644 --- a/patch/Python/module.modulemap +++ b/patch/Python/module.modulemap @@ -2,4 +2,164 @@ module Python { umbrella header "Python.h" export * link "Python" + + exclude header "datetime.h" + exclude header "dynamic_annotations.h" + exclude header "errcode.h" + exclude header "frameobject.h" + exclude header "marshal.h" + exclude header "opcode_ids.h" + exclude header "opcode.h" + exclude header "osdefs.h" + exclude header "py_curses.h" + exclude header "pyconfig-arm64.h" + exclude header "pyconfig-x86_64.h" + exclude header "pyconfig-arm32_64.h" + exclude header "pydtrace.h" + exclude header "pyexpat.h" + exclude header "structmember.h" + + exclude header "cpython/frameobject.h" + exclude header "cpython/pthread_stubs.h" + exclude header "cpython/pyatomic_msc.h" + exclude header "cpython/pyatomic_std.h" + exclude header "cpython/pystats.h" + + exclude header "internal/mimalloc/mimalloc.h" + exclude header "internal/mimalloc/mimalloc/atomic.h" + exclude header "internal/mimalloc/mimalloc/internal.h" + exclude header "internal/mimalloc/mimalloc/prim.h" + exclude header "internal/mimalloc/mimalloc/track.h" + exclude header "internal/mimalloc/mimalloc/types.h" + + exclude header "internal/pycore_abstract.h" + exclude header "internal/pycore_asdl.h" + exclude header "internal/pycore_ast_state.h" + exclude header "internal/pycore_ast.h" + exclude header "internal/pycore_atexit.h" + exclude header "internal/pycore_audit.h" + exclude header "internal/pycore_backoff.h" + exclude header "internal/pycore_bitutils.h" + exclude header "internal/pycore_blocks_output_buffer.h" + exclude header "internal/pycore_brc.h" + exclude header "internal/pycore_bytes_methods.h" + exclude header "internal/pycore_bytesobject.h" + exclude header "internal/pycore_call.h" + exclude header "internal/pycore_capsule.h" + exclude header "internal/pycore_cell.h" + exclude header "internal/pycore_ceval_state.h" + exclude header "internal/pycore_ceval.h" + exclude header "internal/pycore_code.h" + exclude header "internal/pycore_codecs.h" + exclude header "internal/pycore_compile.h" + exclude header "internal/pycore_complexobject.h" + exclude header "internal/pycore_condvar.h" + exclude header "internal/pycore_context.h" + exclude header "internal/pycore_critical_section.h" + exclude header "internal/pycore_crossinterp_data_registry.h" + exclude header "internal/pycore_crossinterp.h" + exclude header "internal/pycore_debug_offsets.h" + exclude header "internal/pycore_descrobject.h" + exclude header "internal/pycore_dict_state.h" + exclude header "internal/pycore_dict.h" + exclude header "internal/pycore_dtoa.h" + exclude header "internal/pycore_emscripten_signal.h" + exclude header "internal/pycore_emscripten_trampoline.h" + exclude header "internal/pycore_exceptions.h" + exclude header "internal/pycore_faulthandler.h" + exclude header "internal/pycore_fileutils_windows.h" + exclude header "internal/pycore_fileutils.h" + exclude header "internal/pycore_floatobject.h" + exclude header "internal/pycore_flowgraph.h" + exclude header "internal/pycore_format.h" + exclude header "internal/pycore_frame.h" + exclude header "internal/pycore_freelist_state.h" + exclude header "internal/pycore_freelist.h" + exclude header "internal/pycore_function.h" + exclude header "internal/pycore_gc.h" + exclude header "internal/pycore_genobject.h" + exclude header "internal/pycore_getopt.h" + exclude header "internal/pycore_gil.h" + exclude header "internal/pycore_global_objects_fini_generated.h" + exclude header "internal/pycore_global_objects.h" + exclude header "internal/pycore_global_strings.h" + exclude header "internal/pycore_hamt.h" + exclude header "internal/pycore_hashtable.h" + exclude header "internal/pycore_identifier.h" + exclude header "internal/pycore_import.h" + exclude header "internal/pycore_importdl.h" + exclude header "internal/pycore_index_pool.h" + exclude header "internal/pycore_initconfig.h" + exclude header "internal/pycore_instruction_sequence.h" + exclude header "internal/pycore_instruments.h" + exclude header "internal/pycore_interp.h" + exclude header "internal/pycore_intrinsics.h" + exclude header "internal/pycore_jit.h" + exclude header "internal/pycore_list.h" + exclude header "internal/pycore_llist.h" + exclude header "internal/pycore_lock.h" + exclude header "internal/pycore_long.h" + exclude header "internal/pycore_magic_number.h" + exclude header "internal/pycore_memoryobject.h" + exclude header "internal/pycore_mimalloc.h" + exclude header "internal/pycore_modsupport.h" + exclude header "internal/pycore_moduleobject.h" + exclude header "internal/pycore_namespace.h" + exclude header "internal/pycore_object_alloc.h" + exclude header "internal/pycore_object_deferred.h" + exclude header "internal/pycore_object_stack.h" + exclude header "internal/pycore_object_state.h" + exclude header "internal/pycore_object.h" + exclude header "internal/pycore_obmalloc_init.h" + exclude header "internal/pycore_obmalloc.h" + exclude header "internal/pycore_opcode_metadata.h" + exclude header "internal/pycore_opcode_utils.h" + exclude header "internal/pycore_optimizer.h" + exclude header "internal/pycore_parking_lot.h" + exclude header "internal/pycore_parser.h" + exclude header "internal/pycore_pathconfig.h" + exclude header "internal/pycore_pyarena.h" + exclude header "internal/pycore_pyatomic_ft_wrappers.h" + exclude header "internal/pycore_pybuffer.h" + exclude header "internal/pycore_pyerrors.h" + exclude header "internal/pycore_pyhash.h" + exclude header "internal/pycore_pylifecycle.h" + exclude header "internal/pycore_pymath.h" + exclude header "internal/pycore_pymem_init.h" + exclude header "internal/pycore_pymem.h" + exclude header "internal/pycore_pystate.h" + exclude header "internal/pycore_pystats.h" + exclude header "internal/pycore_pythonrun.h" + exclude header "internal/pycore_pythread.h" + exclude header "internal/pycore_qsbr.h" + exclude header "internal/pycore_range.h" + exclude header "internal/pycore_runtime_init_generated.h" + exclude header "internal/pycore_runtime_init.h" + exclude header "internal/pycore_runtime.h" + exclude header "internal/pycore_semaphore.h" + exclude header "internal/pycore_setobject.h" + exclude header "internal/pycore_signal.h" + exclude header "internal/pycore_sliceobject.h" + exclude header "internal/pycore_stackref.h" + exclude header "internal/pycore_strhex.h" + exclude header "internal/pycore_structseq.h" + exclude header "internal/pycore_symtable.h" + exclude header "internal/pycore_sysmodule.h" + exclude header "internal/pycore_time.h" + exclude header "internal/pycore_token.h" + exclude header "internal/pycore_traceback.h" + exclude header "internal/pycore_tracemalloc.h" + exclude header "internal/pycore_tstate.h" + exclude header "internal/pycore_tuple.h" + exclude header "internal/pycore_typeobject.h" + exclude header "internal/pycore_typevarobject.h" + exclude header "internal/pycore_ucnhash.h" + exclude header "internal/pycore_unicodeobject_generated.h" + exclude header "internal/pycore_unicodeobject.h" + exclude header "internal/pycore_unionobject.h" + exclude header "internal/pycore_uniqueid.h" + exclude header "internal/pycore_uop_ids.h" + exclude header "internal/pycore_uop_metadata.h" + exclude header "internal/pycore_warnings.h" + exclude header "internal/pycore_weakref.h" } From 1640499e88d8dab555b24fe03602ec4dd8881fc0 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 18 Feb 2025 08:36:44 +0800 Subject: [PATCH 12/26] Bump patch to 3.13.2. --- Makefile | 2 +- patch/Python/Python.patch | 1392 ++++--------------------------------- 2 files changed, 146 insertions(+), 1248 deletions(-) diff --git a/Makefile b/Makefile index 06e2f168..66179ff2 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ BUILD_NUMBER=custom # of a release cycle, as official binaries won't be published. # PYTHON_MICRO_VERSION is the full version number, without any alpha/beta/rc suffix. (e.g., 3.10.0) # PYTHON_VER is the major/minor version (e.g., 3.10) -PYTHON_VERSION=3.13.1 +PYTHON_VERSION=3.13.2 PYTHON_PKG_VERSION=$(PYTHON_VERSION) PYTHON_MICRO_VERSION=$(shell echo $(PYTHON_VERSION) | grep -Eo "\d+\.\d+\.\d+") PYTHON_PKG_MICRO_VERSION=$(shell echo $(PYTHON_PKG_VERSION) | grep -Eo "\d+\.\d+\.\d+") diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index fa07bc1c..8db1f456 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1,184 +1,8 @@ -diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst -index 612aa2aa711..cd78fe18e35 100644 ---- a/Doc/c-api/init_config.rst -+++ b/Doc/c-api/init_config.rst -@@ -1271,6 +1271,17 @@ - - Default: ``1`` in Python config and ``0`` in isolated config. - -+ .. c:member:: int use_system_logger -+ -+ If non-zero, ``stdout`` and ``stderr`` will be redirected to the system -+ log. -+ -+ Only available on macOS 10.12 and later, and on iOS. -+ -+ Default: ``0`` (don't use system log). -+ -+ .. versionadded:: 3.13.2 -+ - .. c:member:: int user_site_directory - - If non-zero, add the user site directory to :data:`sys.path`. -diff --git a/Doc/using/ios.rst b/Doc/using/ios.rst -index 4d4eb2031ee..aa43f75ec35 100644 ---- a/Doc/using/ios.rst -+++ b/Doc/using/ios.rst -@@ -292,10 +292,12 @@ - 10. Add Objective C code to initialize and use a Python interpreter in embedded - mode. You should ensure that: - -- * :c:member:`UTF-8 mode ` is *enabled*; -- * :c:member:`Buffered stdio ` is *disabled*; -- * :c:member:`Writing bytecode ` is *disabled*; -- * :c:member:`Signal handlers ` are *enabled*; -+ * UTF-8 mode (:c:member:`PyPreConfig.utf8_mode`) is *enabled*; -+ * Buffered stdio (:c:member:`PyConfig.buffered_stdio`) is *disabled*; -+ * Writing bytecode (:c:member:`PyConfig.write_bytecode`) is *disabled*; -+ * Signal handlers (:c:member:`PyConfig.install_signal_handlers`) are *enabled*; -+ * System logging (:c:member:`PyConfig.use_system_logger`) is *enabled* -+ (optional, but strongly recommended); - * ``PYTHONHOME`` for the interpreter is configured to point at the - ``python`` subfolder of your app's bundle; and - * The ``PYTHONPATH`` for the interpreter includes: -@@ -324,6 +326,49 @@ - * If you're using a separate folder for third-party packages, ensure that folder - is included as part of the ``PYTHONPATH`` configuration in step 10. - -+Testing a Python package -+------------------------ -+ -+The CPython source tree contains :source:`a testbed project ` that -+is used to run the CPython test suite on the iOS simulator. This testbed can also -+be used as a testbed project for running your Python library's test suite on iOS. -+ -+After building or obtaining an iOS XCFramework (See :source:`iOS/README.rst` -+for details), create a clone of the Python iOS testbed project by running: -+ -+.. code-block:: bash -+ -+ $ python iOS/testbed clone --framework --app --app app-testbed -+ -+You will need to modify the ``iOS/testbed`` reference to point to that -+directory in the CPython source tree; any folders specified with the ``--app`` -+flag will be copied into the cloned testbed project. The resulting testbed will -+be created in the ``app-testbed`` folder. In this example, the ``module1`` and -+``module2`` would be importable modules at runtime. If your project has -+additional dependencies, they can be installed into the -+``app-testbed/iOSTestbed/app_packages`` folder (using ``pip install --target -+app-testbed/iOSTestbed/app_packages`` or similar). -+ -+You can then use the ``app-testbed`` folder to run the test suite for your app, -+For example, if ``module1.tests`` was the entry point to your test suite, you -+could run: -+ -+.. code-block:: bash -+ -+ $ python app-testbed run -- module1.tests -+ -+This is the equivalent of running ``python -m module1.tests`` on a desktop -+Python build. Any arguments after the ``--`` will be passed to the testbed as -+if they were arguments to ``python -m`` on a desktop machine. -+ -+You can also open the testbed project in Xcode by running: -+ -+.. code-block:: bash -+ -+ $ open app-testbed/iOSTestbed.xcodeproj -+ -+This will allow you to use the full Xcode suite of tools for debugging. -+ - App Store Compliance - ==================== - -diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h -index 5da5ef9e543..20f5c9ad9bb 100644 ---- a/Include/cpython/initconfig.h -+++ b/Include/cpython/initconfig.h -@@ -179,6 +179,9 @@ - int use_frozen_modules; - int safe_path; - int int_max_str_digits; -+#ifdef __APPLE__ -+ int use_system_logger; -+#endif - - int cpu_count; - #ifdef Py_GIL_DISABLED ---- /dev/null -+++ b/Lib/_apple_support.py -@@ -0,0 +1,66 @@ -+import io -+import sys -+ -+ -+def init_streams(log_write, stdout_level, stderr_level): -+ # Redirect stdout and stderr to the Apple system log. This method is -+ # invoked by init_apple_streams() (initconfig.c) if config->use_system_logger -+ # is enabled. -+ sys.stdout = SystemLog(log_write, stdout_level, errors=sys.stderr.errors) -+ sys.stderr = SystemLog(log_write, stderr_level, errors=sys.stderr.errors) -+ -+ -+class SystemLog(io.TextIOWrapper): -+ def __init__(self, log_write, level, **kwargs): -+ kwargs.setdefault("encoding", "UTF-8") -+ kwargs.setdefault("line_buffering", True) -+ super().__init__(LogStream(log_write, level), **kwargs) -+ -+ def __repr__(self): -+ return f"" -+ -+ def write(self, s): -+ if not isinstance(s, str): -+ raise TypeError( -+ f"write() argument must be str, not {type(s).__name__}") -+ -+ # In case `s` is a str subclass that writes itself to stdout or stderr -+ # when we call its methods, convert it to an actual str. -+ s = str.__str__(s) -+ -+ # We want to emit one log message per line, so split -+ # the string before sending it to the superclass. -+ for line in s.splitlines(keepends=True): -+ super().write(line) -+ -+ return len(s) -+ -+ -+class LogStream(io.RawIOBase): -+ def __init__(self, log_write, level): -+ self.log_write = log_write -+ self.level = level -+ -+ def __repr__(self): -+ return f"" -+ -+ def writable(self): -+ return True -+ -+ def write(self, b): -+ if type(b) is not bytes: -+ try: -+ b = bytes(memoryview(b)) -+ except TypeError: -+ raise TypeError( -+ f"write() argument must be bytes-like, not {type(b).__name__}" -+ ) from None -+ -+ # Writing an empty string to the stream should have no effect. -+ if b: -+ # Encode null bytes using "modified UTF-8" to avoid truncating the -+ # message. This should not affect the return value, as the caller -+ # may be expecting it to match the length of the input. -+ self.log_write(self.level, b.replace(b"\x00", b"\xc0\x80")) -+ -+ return len(b) diff --git a/Lib/platform.py b/Lib/platform.py -index 5958382276e..5db5eb276a2 100755 +index 8895177e326..eab586011ed 100755 --- a/Lib/platform.py +++ b/Lib/platform.py -@@ -521,6 +521,54 @@ +@@ -522,6 +522,54 @@ return IOSVersionInfo(system, release, model, is_simulator) @@ -233,7 +57,7 @@ index 5958382276e..5db5eb276a2 100755 def _java_getprop(name, default): """This private helper is deprecated in 3.13 and will be removed in 3.15""" from java.lang import System -@@ -884,14 +932,25 @@ +@@ -885,14 +933,25 @@ csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0) return 'Alpha' if cpu_number >= 128 else 'VAX' @@ -262,7 +86,7 @@ index 5958382276e..5db5eb276a2 100755 def from_subprocess(): """ Fall back to `uname -p` -@@ -1051,9 +1110,13 @@ +@@ -1052,9 +1111,13 @@ system = 'Android' release = android_ver().release @@ -277,7 +101,7 @@ index 5958382276e..5db5eb276a2 100755 vals = system, node, release, version, machine # Replace 'unknown' values with the more portable '' -@@ -1343,6 +1406,10 @@ +@@ -1344,6 +1407,10 @@ # macOS and iOS both report as a "Darwin" kernel if sys.platform == "ios": system, release, _, _ = ios_ver() @@ -289,10 +113,10 @@ index 5958382276e..5db5eb276a2 100755 macos_release = mac_ver()[0] if macos_release: diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py -index ec3b638f007..58d6ac7f108 100644 +index 7bcb737ff2c..9cac5d7d807 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py -@@ -668,6 +668,14 @@ +@@ -669,6 +669,14 @@ release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0") osname = sys.platform machine = sys.implementation._multiarch @@ -307,223 +131,6 @@ index ec3b638f007..58d6ac7f108 100644 else: import _osx_support osname, release, machine = _osx_support.get_platform_osx( ---- /dev/null -+++ b/Lib/test/test_apple.py -@@ -0,0 +1,155 @@ -+import unittest -+from _apple_support import SystemLog -+from test.support import is_apple -+from unittest.mock import Mock, call -+ -+if not is_apple: -+ raise unittest.SkipTest("Apple-specific") -+ -+ -+# Test redirection of stdout and stderr to the Apple system log. -+class TestAppleSystemLogOutput(unittest.TestCase): -+ maxDiff = None -+ -+ def assert_writes(self, output): -+ self.assertEqual( -+ self.log_write.mock_calls, -+ [ -+ call(self.log_level, line) -+ for line in output -+ ] -+ ) -+ -+ self.log_write.reset_mock() -+ -+ def setUp(self): -+ self.log_write = Mock() -+ self.log_level = 42 -+ self.log = SystemLog(self.log_write, self.log_level, errors="replace") -+ -+ def test_repr(self): -+ self.assertEqual(repr(self.log), "") -+ self.assertEqual(repr(self.log.buffer), "") -+ -+ def test_log_config(self): -+ self.assertIs(self.log.writable(), True) -+ self.assertIs(self.log.readable(), False) -+ -+ self.assertEqual("UTF-8", self.log.encoding) -+ self.assertEqual("replace", self.log.errors) -+ -+ self.assertIs(self.log.line_buffering, True) -+ self.assertIs(self.log.write_through, False) -+ -+ def test_empty_str(self): -+ self.log.write("") -+ self.log.flush() -+ -+ self.assert_writes([]) -+ -+ def test_simple_str(self): -+ self.log.write("hello world\n") -+ -+ self.assert_writes([b"hello world\n"]) -+ -+ def test_buffered_str(self): -+ self.log.write("h") -+ self.log.write("ello") -+ self.log.write(" ") -+ self.log.write("world\n") -+ self.log.write("goodbye.") -+ self.log.flush() -+ -+ self.assert_writes([b"hello world\n", b"goodbye."]) -+ -+ def test_manual_flush(self): -+ self.log.write("Hello") -+ -+ self.assert_writes([]) -+ -+ self.log.write(" world\nHere for a while...\nGoodbye") -+ self.assert_writes([b"Hello world\n", b"Here for a while...\n"]) -+ -+ self.log.write(" world\nHello again") -+ self.assert_writes([b"Goodbye world\n"]) -+ -+ self.log.flush() -+ self.assert_writes([b"Hello again"]) -+ -+ def test_non_ascii(self): -+ # Spanish -+ self.log.write("ol\u00e9\n") -+ self.assert_writes([b"ol\xc3\xa9\n"]) -+ -+ # Chinese -+ self.log.write("\u4e2d\u6587\n") -+ self.assert_writes([b"\xe4\xb8\xad\xe6\x96\x87\n"]) -+ -+ # Printing Non-BMP emoji -+ self.log.write("\U0001f600\n") -+ self.assert_writes([b"\xf0\x9f\x98\x80\n"]) -+ -+ # Non-encodable surrogates are replaced -+ self.log.write("\ud800\udc00\n") -+ self.assert_writes([b"??\n"]) -+ -+ def test_modified_null(self): -+ # Null characters are logged using "modified UTF-8". -+ self.log.write("\u0000\n") -+ self.assert_writes([b"\xc0\x80\n"]) -+ self.log.write("a\u0000\n") -+ self.assert_writes([b"a\xc0\x80\n"]) -+ self.log.write("\u0000b\n") -+ self.assert_writes([b"\xc0\x80b\n"]) -+ self.log.write("a\u0000b\n") -+ self.assert_writes([b"a\xc0\x80b\n"]) -+ -+ def test_nonstandard_str(self): -+ # String subclasses are accepted, but they should be converted -+ # to a standard str without calling any of their methods. -+ class CustomStr(str): -+ def splitlines(self, *args, **kwargs): -+ raise AssertionError() -+ -+ def __len__(self): -+ raise AssertionError() -+ -+ def __str__(self): -+ raise AssertionError() -+ -+ self.log.write(CustomStr("custom\n")) -+ self.assert_writes([b"custom\n"]) -+ -+ def test_non_str(self): -+ # Non-string classes are not accepted. -+ for obj in [b"", b"hello", None, 42]: -+ with self.subTest(obj=obj): -+ with self.assertRaisesRegex( -+ TypeError, -+ fr"write\(\) argument must be str, not " -+ fr"{type(obj).__name__}" -+ ): -+ self.log.write(obj) -+ -+ def test_byteslike_in_buffer(self): -+ # The underlying buffer *can* accept bytes-like objects -+ self.log.buffer.write(bytearray(b"hello")) -+ self.log.flush() -+ -+ self.log.buffer.write(b"") -+ self.log.flush() -+ -+ self.log.buffer.write(b"goodbye") -+ self.log.flush() -+ -+ self.assert_writes([b"hello", b"goodbye"]) -+ -+ def test_non_byteslike_in_buffer(self): -+ for obj in ["hello", None, 42]: -+ with self.subTest(obj=obj): -+ with self.assertRaisesRegex( -+ TypeError, -+ fr"write\(\) argument must be bytes-like, not " -+ fr"{type(obj).__name__}" -+ ): -+ self.log.buffer.write(obj) -diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py -index 3b43e422f82..5f70632182e 100644 ---- a/Lib/test/test_embed.py -+++ b/Lib/test/test_embed.py -@@ -627,6 +627,8 @@ - CONFIG_COMPAT.update({ - 'legacy_windows_stdio': 0, - }) -+ if support.is_apple: -+ CONFIG_COMPAT['use_system_logger'] = False - - CONFIG_PYTHON = dict(CONFIG_COMPAT, - _config_init=API_PYTHON, -diff --git a/Makefile.pre.in b/Makefile.pre.in -index 03ca4cb635b..46a37ded970 100644 ---- a/Makefile.pre.in -+++ b/Makefile.pre.in -@@ -2061,7 +2061,6 @@ - # This must be run *after* a `make install` has completed the build. The - # `--with-framework-name` argument *cannot* be used when configuring the build. - XCFOLDER:=iOSTestbed.$(MULTIARCH).$(shell date +%s) --XCRESULT=$(XCFOLDER)/$(MULTIARCH).xcresult - .PHONY: testios - testios: - @if test "$(MACHDEP)" != "ios"; then \ -@@ -2080,29 +2079,12 @@ - echo "Cannot find a finalized iOS Python.framework. Have you run 'make install' to finalize the framework build?"; \ - exit 1;\ - fi -- # Copy the testbed project into the build folder -- cp -r $(srcdir)/iOS/testbed $(XCFOLDER) -- # Copy the framework from the install location to the testbed project. -- cp -r $(PYTHONFRAMEWORKPREFIX)/* $(XCFOLDER)/Python.xcframework/ios-arm64_x86_64-simulator -- -- # Run the test suite for the Xcode project, targeting the iOS simulator. -- # If the suite fails, touch a file in the test folder as a marker -- if ! xcodebuild test -project $(XCFOLDER)/iOSTestbed.xcodeproj -scheme "iOSTestbed" -destination "platform=iOS Simulator,name=iPhone SE (3rd Generation)" -resultBundlePath $(XCRESULT) -derivedDataPath $(XCFOLDER)/DerivedData ; then \ -- touch $(XCFOLDER)/failed; \ -- fi - -- # Regardless of success or failure, extract and print the test output -- xcrun xcresulttool get --path $(XCRESULT) \ -- --id $$( \ -- xcrun xcresulttool get --path $(XCRESULT) --format json | \ -- $(PYTHON_FOR_BUILD) -c "import sys, json; result = json.load(sys.stdin); print(result['actions']['_values'][0]['actionResult']['logRef']['id']['_value'])" \ -- ) \ -- --format json | \ -- $(PYTHON_FOR_BUILD) -c "import sys, json; result = json.load(sys.stdin); print(result['subsections']['_values'][1]['subsections']['_values'][0]['emittedOutput']['_value'])" -+ # Clone the testbed project into the XCFOLDER -+ $(PYTHON_FOR_BUILD) $(srcdir)/iOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER)" - -- @if test -e $(XCFOLDER)/failed ; then \ -- exit 1; \ -- fi -+ # Run the testbed project -+ $(PYTHON_FOR_BUILD) "$(XCFOLDER)" run --verbose -- test -uall --single-process --rerun -W - - # Like test, but using --slow-ci which enables all test resources and use - # longer timeout. Run an optional pybuildbot.identify script to include diff --git a/Misc/platform_triplet.c b/Misc/platform_triplet.c index ec0857a4a99..2350e9dc821 100644 --- a/Misc/platform_triplet.c @@ -555,193 +162,11 @@ index ec0857a4a99..2350e9dc821 100644 // Older macOS SDKs do not define TARGET_OS_OSX # elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX PLATFORM_TRIPLET=darwin -diff --git a/Python/initconfig.c b/Python/initconfig.c -index 84717b4e3c9..5746416c826 100644 ---- a/Python/initconfig.c -+++ b/Python/initconfig.c -@@ -129,6 +129,10 @@ - #ifdef Py_DEBUG - SPEC(run_presite, WSTR_OPT), - #endif -+#ifdef __APPLE__ -+ SPEC(use_system_logger, BOOL), -+#endif -+ - {NULL, 0, 0}, - }; - -@@ -744,6 +748,9 @@ - assert(config->cpu_count != 0); - // config->use_frozen_modules is initialized later - // by _PyConfig_InitImportConfig(). -+#ifdef __APPLE__ -+ assert(config->use_system_logger >= 0); -+#endif - #ifdef Py_STATS - assert(config->_pystats >= 0); - #endif -@@ -846,6 +853,9 @@ - config->_is_python_build = 0; - config->code_debug_ranges = 1; - config->cpu_count = -1; -+#ifdef __APPLE__ -+ config->use_system_logger = 0; -+#endif - #ifdef Py_GIL_DISABLED - config->enable_gil = _PyConfig_GIL_DEFAULT; - #endif -@@ -874,6 +884,9 @@ - #ifdef MS_WINDOWS - config->legacy_windows_stdio = 0; - #endif -+#ifdef __APPLE__ -+ config->use_system_logger = 0; -+#endif - } - - -@@ -909,6 +922,9 @@ - #ifdef MS_WINDOWS - config->legacy_windows_stdio = 0; - #endif -+#ifdef __APPLE__ -+ config->use_system_logger = 0; -+#endif - } - - -diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c -index 8fe5bb8b300..d23cfc62d0f 100644 ---- a/Python/pylifecycle.c -+++ b/Python/pylifecycle.c -@@ -43,7 +43,9 @@ - #endif - - #if defined(__APPLE__) -+# include - # include -+# include - #endif - - #ifdef HAVE_SIGNAL_H -@@ -73,6 +75,9 @@ - #ifdef __ANDROID__ - static PyStatus init_android_streams(PyThreadState *tstate); - #endif -+#if defined(__APPLE__) -+static PyStatus init_apple_streams(PyThreadState *tstate); -+#endif - static void wait_for_thread_shutdown(PyThreadState *tstate); - static void finalize_subinterpreters(void); - static void call_ll_exitfuncs(_PyRuntimeState *runtime); -@@ -1253,6 +1258,14 @@ - return status; - } - #endif -+#if defined(__APPLE__) -+ if (config->use_system_logger) { -+ status = init_apple_streams(tstate); -+ if (_PyStatus_EXCEPTION(status)) { -+ return status; -+ } -+ } -+#endif - - #ifdef Py_DEBUG - run_presite(tstate); -@@ -2920,6 +2933,75 @@ - - #endif // __ANDROID__ - -+#if defined(__APPLE__) -+ -+static PyObject * -+apple_log_write_impl(PyObject *self, PyObject *args) -+{ -+ int logtype = 0; -+ const char *text = NULL; -+ if (!PyArg_ParseTuple(args, "iy", &logtype, &text)) { -+ return NULL; -+ } -+ -+ // Call the underlying Apple logging API. The os_log unified logging APIs -+ // were introduced in macOS 10.12, iOS 10.0, tvOS 10.0, and watchOS 3.0; -+ // this call is a no-op on older versions. -+ #if TARGET_OS_IPHONE || (TARGET_OS_OSX && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) -+ // Pass the user-provided text through explicit %s formatting -+ // to avoid % literals being interpreted as a formatting directive. -+ os_log_with_type(OS_LOG_DEFAULT, logtype, "%s", text); -+ #endif -+ Py_RETURN_NONE; -+} -+ -+ -+static PyMethodDef apple_log_write_method = { -+ "apple_log_write", apple_log_write_impl, METH_VARARGS -+}; -+ -+ -+static PyStatus -+init_apple_streams(PyThreadState *tstate) -+{ -+ PyStatus status = _PyStatus_OK(); -+ PyObject *_apple_support = NULL; -+ PyObject *apple_log_write = NULL; -+ PyObject *result = NULL; -+ -+ _apple_support = PyImport_ImportModule("_apple_support"); -+ if (_apple_support == NULL) { -+ goto error; -+ } -+ -+ apple_log_write = PyCFunction_New(&apple_log_write_method, NULL); -+ if (apple_log_write == NULL) { -+ goto error; -+ } -+ -+ // Initialize the logging streams, sending stdout -> Default; stderr -> Error -+ result = PyObject_CallMethod( -+ _apple_support, "init_streams", "Oii", -+ apple_log_write, OS_LOG_TYPE_DEFAULT, OS_LOG_TYPE_ERROR); -+ if (result == NULL) { -+ goto error; -+ } -+ -+ goto done; -+ -+error: -+ _PyErr_Print(tstate); -+ status = _PyStatus_ERR("failed to initialize Apple log streams"); -+ -+done: -+ Py_XDECREF(result); -+ Py_XDECREF(apple_log_write); -+ Py_XDECREF(_apple_support); -+ return status; -+} -+ -+#endif // __APPLE__ -+ - - static void - _Py_FatalError_DumpTracebacks(int fd, PyInterpreterState *interp, -diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h -index faeed0b7125..dfe0fa2acd8 100644 ---- a/Python/stdlib_module_names.h -+++ b/Python/stdlib_module_names.h -@@ -6,6 +6,7 @@ - "_abc", - "_aix_support", - "_android_support", -+"_apple_support", - "_ast", - "_asyncio", - "_bisect", diff --git a/configure b/configure -index ae70f02f70e..af518f926d8 100755 +index 1cd1f690f7b..34922ae651e 100755 --- a/configure +++ b/configure -@@ -978,6 +978,8 @@ +@@ -979,6 +979,8 @@ CFLAGS CC HAS_XCRUN @@ -750,7 +175,7 @@ index ae70f02f70e..af518f926d8 100755 IPHONEOS_DEPLOYMENT_TARGET EXPORT_MACOSX_DEPLOYMENT_TARGET CONFIGURE_MACOSX_DEPLOYMENT_TARGET -@@ -4054,6 +4056,12 @@ +@@ -4058,6 +4060,12 @@ *-apple-ios*) ac_sys_system=iOS ;; @@ -763,7 +188,7 @@ index ae70f02f70e..af518f926d8 100755 *-*-vxworks*) ac_sys_system=VxWorks ;; -@@ -4108,7 +4116,7 @@ +@@ -4112,7 +4120,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. # @@ -772,7 +197,7 @@ index ae70f02f70e..af518f926d8 100755 # which isn't a binary that exists, and isn't very convenient, as it contains the # iOS version. As the default cross-compiler name won't exist, configure falls # back to gcc, which *definitely* won't work. We're providing wrapper scripts for -@@ -4123,6 +4131,14 @@ +@@ -4127,6 +4135,14 @@ aarch64-apple-ios*-simulator) AR=arm64-apple-ios-simulator-ar ;; aarch64-apple-ios*) AR=arm64-apple-ios-ar ;; x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;; @@ -787,7 +212,7 @@ index ae70f02f70e..af518f926d8 100755 *) esac fi -@@ -4131,6 +4147,14 @@ +@@ -4135,6 +4151,14 @@ aarch64-apple-ios*-simulator) CC=arm64-apple-ios-simulator-clang ;; aarch64-apple-ios*) CC=arm64-apple-ios-clang ;; x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;; @@ -802,7 +227,7 @@ index ae70f02f70e..af518f926d8 100755 *) esac fi -@@ -4139,6 +4163,14 @@ +@@ -4143,6 +4167,14 @@ aarch64-apple-ios*-simulator) CPP=arm64-apple-ios-simulator-cpp ;; aarch64-apple-ios*) CPP=arm64-apple-ios-cpp ;; x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;; @@ -817,7 +242,7 @@ index ae70f02f70e..af518f926d8 100755 *) esac fi -@@ -4147,6 +4179,14 @@ +@@ -4151,6 +4183,14 @@ aarch64-apple-ios*-simulator) CXX=arm64-apple-ios-simulator-clang++ ;; aarch64-apple-ios*) CXX=arm64-apple-ios-clang++ ;; x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;; @@ -832,7 +257,7 @@ index ae70f02f70e..af518f926d8 100755 *) esac fi -@@ -4267,8 +4307,10 @@ +@@ -4271,8 +4311,10 @@ case $enableval in yes) case $ac_sys_system in @@ -845,7 +270,7 @@ index ae70f02f70e..af518f926d8 100755 *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 esac esac -@@ -4277,6 +4319,8 @@ +@@ -4281,6 +4323,8 @@ no) case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -854,7 +279,7 @@ index ae70f02f70e..af518f926d8 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4383,6 +4427,36 @@ +@@ -4387,6 +4431,36 @@ ac_config_files="$ac_config_files iOS/Resources/Info.plist" @@ -891,7 +316,7 @@ index ae70f02f70e..af518f926d8 100755 ;; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 -@@ -4394,6 +4468,8 @@ +@@ -4398,6 +4472,8 @@ case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -900,7 +325,7 @@ index ae70f02f70e..af518f926d8 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4447,8 +4523,8 @@ +@@ -4451,8 +4527,8 @@ case "$withval" in yes) case $ac_sys_system in @@ -911,7 +336,7 @@ index ae70f02f70e..af518f926d8 100755 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" ;; *) as_fn_error $? "no default app store compliance patch available for $ac_sys_system" "$LINENO" 5 ;; -@@ -4466,8 +4542,8 @@ +@@ -4470,8 +4546,8 @@ else $as_nop case $ac_sys_system in @@ -922,7 +347,7 @@ index ae70f02f70e..af518f926d8 100755 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: applying default app store compliance patch" >&5 printf "%s\n" "applying default app store compliance patch" >&6; } -@@ -4521,6 +4597,50 @@ +@@ -4525,6 +4601,50 @@ ;; esac ;; @@ -973,7 +398,7 @@ index ae70f02f70e..af518f926d8 100755 *-*-vxworks*) _host_ident=$host_cpu ;; -@@ -4599,9 +4719,13 @@ +@@ -4603,9 +4723,13 @@ define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; @@ -988,7 +413,7 @@ index ae70f02f70e..af518f926d8 100755 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -4664,7 +4788,10 @@ +@@ -4668,7 +4792,10 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' @@ -1000,7 +425,7 @@ index ae70f02f70e..af518f926d8 100755 # checks for alternative programs -@@ -4705,6 +4832,16 @@ +@@ -4709,6 +4836,16 @@ as_fn_append CFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" as_fn_append LDFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" ;; #( @@ -1017,7 +442,7 @@ index ae70f02f70e..af518f926d8 100755 *) : ;; esac -@@ -7006,6 +7143,10 @@ +@@ -7010,6 +7147,10 @@ MULTIARCH="" ;; #( iOS) : MULTIARCH="" ;; #( @@ -1028,7 +453,7 @@ index ae70f02f70e..af518f926d8 100755 FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -7026,7 +7167,7 @@ +@@ -7030,7 +7171,7 @@ printf "%s\n" "$MULTIARCH" >&6; } case $ac_sys_system in #( @@ -1037,7 +462,7 @@ index ae70f02f70e..af518f926d8 100755 SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2` ;; #( *) : SOABI_PLATFORM=$PLATFORM_TRIPLET -@@ -7077,6 +7218,14 @@ +@@ -7081,6 +7222,14 @@ PY_SUPPORT_TIER=3 ;; #( aarch64-apple-ios*/clang) : PY_SUPPORT_TIER=3 ;; #( @@ -1052,7 +477,7 @@ index ae70f02f70e..af518f926d8 100755 aarch64-*-linux-android/clang) : PY_SUPPORT_TIER=3 ;; #( x86_64-*-linux-android/clang) : -@@ -7550,7 +7699,7 @@ +@@ -7554,7 +7703,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; @@ -1061,7 +486,7 @@ index ae70f02f70e..af518f926d8 100755 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5;; -@@ -7616,7 +7765,7 @@ +@@ -7620,7 +7769,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -1070,7 +495,7 @@ index ae70f02f70e..af518f926d8 100755 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -12955,7 +13104,7 @@ +@@ -12975,7 +13124,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -1079,7 +504,7 @@ index ae70f02f70e..af518f926d8 100755 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -13088,7 +13237,7 @@ +@@ -13108,7 +13257,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -1088,7 +513,7 @@ index ae70f02f70e..af518f926d8 100755 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -13112,7 +13261,7 @@ +@@ -13132,7 +13281,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -1097,7 +522,7 @@ index ae70f02f70e..af518f926d8 100755 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -14511,7 +14660,7 @@ +@@ -14531,7 +14680,7 @@ ctypes_malloc_closure=yes ;; #( @@ -1106,7 +531,7 @@ index ae70f02f70e..af518f926d8 100755 ctypes_malloc_closure=yes ;; #( -@@ -17962,12 +18111,6 @@ +@@ -17982,12 +18131,6 @@ then : printf "%s\n" "#define HAVE_DUP3 1" >>confdefs.h @@ -1119,7 +544,7 @@ index ae70f02f70e..af518f926d8 100755 fi ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes -@@ -18028,18 +18171,6 @@ +@@ -18048,18 +18191,6 @@ then : printf "%s\n" "#define HAVE_FEXECVE 1" >>confdefs.h @@ -1138,7 +563,7 @@ index ae70f02f70e..af518f926d8 100755 fi ac_fn_c_check_func "$LINENO" "fpathconf" "ac_cv_func_fpathconf" if test "x$ac_cv_func_fpathconf" = xyes -@@ -18466,24 +18597,6 @@ +@@ -18486,24 +18617,6 @@ then : printf "%s\n" "#define HAVE_POSIX_OPENPT 1" >>confdefs.h @@ -1163,7 +588,7 @@ index ae70f02f70e..af518f926d8 100755 fi ac_fn_c_check_func "$LINENO" "pread" "ac_cv_func_pread" if test "x$ac_cv_func_pread" = xyes -@@ -18772,12 +18885,6 @@ +@@ -18792,12 +18905,6 @@ then : printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h @@ -1176,7 +601,7 @@ index ae70f02f70e..af518f926d8 100755 fi ac_fn_c_check_func "$LINENO" "sigfillset" "ac_cv_func_sigfillset" if test "x$ac_cv_func_sigfillset" = xyes -@@ -19046,11 +19153,11 @@ +@@ -19066,11 +19173,11 @@ fi @@ -1190,7 +615,7 @@ index ae70f02f70e..af518f926d8 100755 ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" if test "x$ac_cv_func_getentropy" = xyes then : -@@ -19072,6 +19179,53 @@ +@@ -19092,6 +19199,53 @@ fi @@ -1244,7 +669,7 @@ index ae70f02f70e..af518f926d8 100755 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5 printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; } if test ${ac_cv_c_undeclared_builtin_options+y} -@@ -21868,7 +22022,8 @@ +@@ -21888,7 +22042,8 @@ # check for openpty, login_tty, and forkpty @@ -1254,7 +679,7 @@ index ae70f02f70e..af518f926d8 100755 for ac_func in openpty do : -@@ -21964,7 +22119,7 @@ +@@ -21984,7 +22139,7 @@ fi done @@ -1263,7 +688,7 @@ index ae70f02f70e..af518f926d8 100755 printf %s "checking for library containing login_tty... " >&6; } if test ${ac_cv_search_login_tty+y} then : -@@ -22121,6 +22276,7 @@ +@@ -22141,6 +22296,7 @@ fi done @@ -1271,7 +696,7 @@ index ae70f02f70e..af518f926d8 100755 # check for long file support functions ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" -@@ -22367,10 +22523,10 @@ +@@ -22387,10 +22543,10 @@ done @@ -1284,7 +709,7 @@ index ae70f02f70e..af518f926d8 100755 then for ac_func in clock_settime -@@ -24602,8 +24758,8 @@ +@@ -24622,8 +24778,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -1295,7 +720,7 @@ index ae70f02f70e..af518f926d8 100755 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -27251,7 +27407,7 @@ +@@ -27271,7 +27427,7 @@ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for device files" >&5 printf "%s\n" "$as_me: checking for device files" >&6;} @@ -1304,7 +729,7 @@ index ae70f02f70e..af518f926d8 100755 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -27684,7 +27840,7 @@ +@@ -27704,7 +27860,7 @@ with_ensurepip=no ;; #( WASI) : with_ensurepip=no ;; #( @@ -1313,7 +738,7 @@ index ae70f02f70e..af518f926d8 100755 with_ensurepip=no ;; #( *) : with_ensurepip=upgrade -@@ -28703,7 +28859,7 @@ +@@ -28723,7 +28879,7 @@ ;; #( Darwin) : ;; #( @@ -1322,7 +747,7 @@ index ae70f02f70e..af518f926d8 100755 -@@ -32468,6 +32624,8 @@ +@@ -32488,6 +32644,8 @@ "Mac/Resources/framework/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/framework/Info.plist" ;; "Mac/Resources/app/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/app/Info.plist" ;; "iOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES iOS/Resources/Info.plist" ;; @@ -1332,7 +757,7 @@ index ae70f02f70e..af518f926d8 100755 "Misc/python.pc") CONFIG_FILES="$CONFIG_FILES Misc/python.pc" ;; "Misc/python-embed.pc") CONFIG_FILES="$CONFIG_FILES Misc/python-embed.pc" ;; diff --git a/configure.ac b/configure.ac -index a764028e49f..be1b9e784db 100644 +index 3fcb18922c5..3a4167c96ee 100644 --- a/configure.ac +++ b/configure.ac @@ -330,6 +330,12 @@ @@ -1646,7 +1071,7 @@ index a764028e49f..be1b9e784db 100644 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -3465,7 +3596,7 @@ +@@ -3486,7 +3617,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -1655,7 +1080,7 @@ index a764028e49f..be1b9e784db 100644 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -3589,7 +3720,7 @@ +@@ -3610,7 +3741,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -1664,7 +1089,7 @@ index a764028e49f..be1b9e784db 100644 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -3613,7 +3744,7 @@ +@@ -3634,7 +3765,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -1673,7 +1098,7 @@ index a764028e49f..be1b9e784db 100644 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -3997,7 +4128,7 @@ +@@ -4018,7 +4149,7 @@ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], @@ -1682,7 +1107,7 @@ index a764028e49f..be1b9e784db 100644 ctypes_malloc_closure=yes ], [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] -@@ -5091,9 +5222,9 @@ +@@ -5112,9 +5243,9 @@ # checks for library functions AC_CHECK_FUNCS([ \ accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ @@ -1694,7 +1119,7 @@ index a764028e49f..be1b9e784db 100644 gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \ getgrnam_r getgrouplist gethostname getitimer getloadavg getlogin \ getpeername getpgid getpid getppid getpriority _getpty \ -@@ -5101,15 +5232,14 @@ +@@ -5122,15 +5253,14 @@ getspnam getuid getwd grantpt if_nameindex initgroups kill killpg lchown linkat \ lockf lstat lutimes madvise mbrtowc memrchr mkdirat mkfifo mkfifoat \ mknod mknodat mktime mmap mremap nice openat opendir pathconf pause pipe \ @@ -1712,7 +1137,7 @@ index a764028e49f..be1b9e784db 100644 sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ -@@ -5124,12 +5254,20 @@ +@@ -5145,12 +5275,20 @@ AC_CHECK_FUNCS([lchmod]) fi @@ -1736,7 +1161,7 @@ index a764028e49f..be1b9e784db 100644 fi AC_CHECK_DECL([dirfd], -@@ -5380,20 +5518,22 @@ +@@ -5401,20 +5539,22 @@ ]) # check for openpty, login_tty, and forkpty @@ -1773,7 +1198,7 @@ index a764028e49f..be1b9e784db 100644 # check for long file support functions AC_CHECK_FUNCS([fseek64 fseeko fstatvfs ftell64 ftello statvfs]) -@@ -5432,10 +5572,10 @@ +@@ -5453,10 +5593,10 @@ ]) ]) @@ -1786,7 +1211,7 @@ index a764028e49f..be1b9e784db 100644 then AC_CHECK_FUNCS([clock_settime], [], [ AC_CHECK_LIB([rt], [clock_settime], [ -@@ -6184,8 +6324,8 @@ +@@ -6205,8 +6345,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -1797,7 +1222,7 @@ index a764028e49f..be1b9e784db 100644 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -6793,7 +6933,7 @@ +@@ -6814,7 +6954,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. @@ -1806,7 +1231,7 @@ index a764028e49f..be1b9e784db 100644 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -7050,7 +7190,7 @@ +@@ -7071,7 +7211,7 @@ AS_CASE([$ac_sys_system], [Emscripten], [with_ensurepip=no], [WASI], [with_ensurepip=no], @@ -1815,7 +1240,7 @@ index a764028e49f..be1b9e784db 100644 [with_ensurepip=upgrade] ) ]) -@@ -7458,7 +7598,7 @@ +@@ -7479,7 +7619,7 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], @@ -1824,85 +1249,6 @@ index a764028e49f..be1b9e784db 100644 dnl subprocess and multiprocessing are not supported (no fork syscall). dnl curses and tkinter user interface are not available. dnl gdbm and nis aren't available -diff --git a/iOS/README.rst b/iOS/README.rst -index e33455eef8f..13b88514493 100644 ---- a/iOS/README.rst -+++ b/iOS/README.rst -@@ -285,52 +285,42 @@ - * Install the Python iOS framework into the copy of the testbed project; and - * Run the test suite on an "iPhone SE (3rd generation)" simulator. - --While the test suite is running, Xcode does not display any console output. --After showing some Xcode build commands, the console output will print ``Testing --started``, and then appear to stop. It will remain in this state until the test --suite completes. On a 2022 M1 MacBook Pro, the test suite takes approximately 12 --minutes to run; a couple of extra minutes is required to boot and prepare the --iOS simulator. -- - On success, the test suite will exit and report successful completion of the --test suite. No output of the Python test suite will be displayed. -- --On failure, the output of the Python test suite *will* be displayed. This will --show the details of the tests that failed. -+test suite. On a 2022 M1 MacBook Pro, the test suite takes approximately 15 -+minutes to run; a couple of extra minutes is required to compile the testbed -+project, and then boot and prepare the iOS simulator. - - Debugging test failures - ----------------------- - --The easiest way to diagnose a single test failure is to open the testbed project --in Xcode and run the tests from there using the "Product > Test" menu item. -- --To test in Xcode, you must ensure the testbed project has a copy of a compiled --framework. If you've configured your build with the default install location of --``iOS/Frameworks``, you can copy from that location into the test project. To --test on an ARM64 simulator, run:: -- -- $ rm -rf iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator/* -- $ cp -r iOS/Frameworks/arm64-iphonesimulator/* iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator -+Running ``make test`` generates a standalone version of the ``iOS/testbed`` -+project, and runs the full test suite. It does this using ``iOS/testbed`` -+itself - the folder is an executable module that can be used to create and run -+a clone of the testbed project. - --To test on an x86-64 simulator, run:: -+You can generate your own standalone testbed instance by running:: - -- $ rm -rf iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator/* -- $ cp -r iOS/Frameworks/x86_64-iphonesimulator/* iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator -+ $ python iOS/testbed clone --framework iOS/Frameworks/arm64-iphonesimulator my-testbed - --To test on a physical device:: -+This invocation assumes that ``iOS/Frameworks/arm64-iphonesimulator`` is the -+path to the iOS simulator framework for your platform (ARM64 in this case); -+``my-testbed`` is the name of the folder for the new testbed clone. - -- $ rm -rf iOS/testbed/Python.xcframework/ios-arm64/* -- $ cp -r iOS/Frameworks/arm64-iphoneos/* iOS/testbed/Python.xcframework/ios-arm64 -+You can then use the ``my-testbed`` folder to run the Python test suite, -+passing in any command line arguments you may require. For example, if you're -+trying to diagnose a failure in the ``os`` module, you might run:: - --Alternatively, you can configure your build to install directly into the --testbed project. For a simulator, use:: -+ $ python my-testbed run -- test -W test_os - -- --enable-framework=$(pwd)/iOS/testbed/Python.xcframework/ios-arm64_x86_64-simulator -+This is the equivalent of running ``python -m test -W test_os`` on a desktop -+Python build. Any arguments after the ``--`` will be passed to testbed as if -+they were arguments to ``python -m`` on a desktop machine. - --For a physical device, use:: -+You can also open the testbed project in Xcode by running:: - -- --enable-framework=$(pwd)/iOS/testbed/Python.xcframework/ios-arm64 -+ $ open my-testbed/iOSTestbed.xcodeproj - -+This will allow you to use the full Xcode suite of tools for debugging. - - Testing on an iOS device - ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/iOS/Resources/Info.plist.in b/iOS/Resources/Info.plist.in index c3e261ecd9e..26ef7a95de4 100644 --- a/iOS/Resources/Info.plist.in @@ -1923,546 +1269,98 @@ index c3e261ecd9e..26ef7a95de4 100644 CFBundleSupportedPlatforms iPhoneOS -diff --git a/iOS/Resources/bin/arm64-apple-ios-ar b/iOS/Resources/bin/arm64-apple-ios-ar -index 8122332b9c1..3cf3eb21874 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-ar -+++ b/iOS/Resources/bin/arm64-apple-ios-ar -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphoneos${IOS_SDK_VERSION} ar $@ -+xcrun --sdk iphoneos${IOS_SDK_VERSION} ar "$@" -diff --git a/iOS/Resources/bin/arm64-apple-ios-clang b/iOS/Resources/bin/arm64-apple-ios-clang -index 4d525751eba..c39519cd1f8 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-clang -+++ b/iOS/Resources/bin/arm64-apple-ios-clang -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphoneos${IOS_SDK_VERSION} clang -target arm64-apple-ios $@ -+xcrun --sdk iphoneos${IOS_SDK_VERSION} clang -target arm64-apple-ios "$@" -diff --git a/iOS/Resources/bin/arm64-apple-ios-clang++ b/iOS/Resources/bin/arm64-apple-ios-clang++ -index f24bec11268..d9b12925f38 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-clang++ -+++ b/iOS/Resources/bin/arm64-apple-ios-clang++ -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphoneos${IOS_SDK_VERSION} clang++ -target arm64-apple-ios $@ -+xcrun --sdk iphoneos${IOS_SDK_VERSION} clang++ -target arm64-apple-ios "$@" -diff --git a/iOS/Resources/bin/arm64-apple-ios-cpp b/iOS/Resources/bin/arm64-apple-ios-cpp -index 891bb25bb43..24da23d3448 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-cpp -+++ b/iOS/Resources/bin/arm64-apple-ios-cpp -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphoneos${IOS_SDK_VERSION} clang -target arm64-apple-ios -E $@ -+xcrun --sdk iphoneos${IOS_SDK_VERSION} clang -target arm64-apple-ios -E "$@" -diff --git a/iOS/Resources/bin/arm64-apple-ios-simulator-ar b/iOS/Resources/bin/arm64-apple-ios-simulator-ar -index 74ed3bc6df1..b836b6db902 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-simulator-ar -+++ b/iOS/Resources/bin/arm64-apple-ios-simulator-ar -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} ar $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} ar "$@" -diff --git a/iOS/Resources/bin/arm64-apple-ios-simulator-clang b/iOS/Resources/bin/arm64-apple-ios-simulator-clang -index 32574cad284..92e8d853d6e 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-simulator-clang -+++ b/iOS/Resources/bin/arm64-apple-ios-simulator-clang -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target arm64-apple-ios-simulator $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target arm64-apple-ios-simulator "$@" -diff --git a/iOS/Resources/bin/arm64-apple-ios-simulator-clang++ b/iOS/Resources/bin/arm64-apple-ios-simulator-clang++ -index ef37d05b512..076469cc70c 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-simulator-clang++ -+++ b/iOS/Resources/bin/arm64-apple-ios-simulator-clang++ -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang++ -target arm64-apple-ios-simulator $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang++ -target arm64-apple-ios-simulator "$@" -diff --git a/iOS/Resources/bin/arm64-apple-ios-simulator-cpp b/iOS/Resources/bin/arm64-apple-ios-simulator-cpp -index 6aaf6fbe188..c57f28cee5b 100755 ---- a/iOS/Resources/bin/arm64-apple-ios-simulator-cpp -+++ b/iOS/Resources/bin/arm64-apple-ios-simulator-cpp -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target arm64-apple-ios-simulator -E $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target arm64-apple-ios-simulator -E "$@" -diff --git a/iOS/Resources/bin/x86_64-apple-ios-simulator-ar b/iOS/Resources/bin/x86_64-apple-ios-simulator-ar -index 74ed3bc6df1..b836b6db902 100755 ---- a/iOS/Resources/bin/x86_64-apple-ios-simulator-ar -+++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-ar -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} ar $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} ar "$@" -diff --git a/iOS/Resources/bin/x86_64-apple-ios-simulator-clang b/iOS/Resources/bin/x86_64-apple-ios-simulator-clang -index bcbe91f6061..17cbe0c8a1e 100755 ---- a/iOS/Resources/bin/x86_64-apple-ios-simulator-clang -+++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-clang -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target x86_64-apple-ios-simulator $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target x86_64-apple-ios-simulator "$@" -diff --git a/iOS/Resources/bin/x86_64-apple-ios-simulator-clang++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-clang++ -index 86f03ea32bc..565d47b24c2 100755 ---- a/iOS/Resources/bin/x86_64-apple-ios-simulator-clang++ -+++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-clang++ -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang++ -target x86_64-apple-ios-simulator $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang++ -target x86_64-apple-ios-simulator "$@" -diff --git a/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp b/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp -index e6a42d9b85d..63fc8e8de2d 100755 ---- a/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp -+++ b/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp -@@ -1,2 +1,2 @@ - #!/bin/sh --xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target x86_64-apple-ios-simulator -E $@ -+xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target x86_64-apple-ios-simulator -E "$@" ---- /dev/null +diff --git a/iOS/testbed/__main__.py b/iOS/testbed/__main__.py +index b4499f5ac17..08fbe90a1c6 100644 +--- a/iOS/testbed/__main__.py +++ b/iOS/testbed/__main__.py -@@ -0,0 +1,395 @@ -+import argparse -+import asyncio -+import json -+import plistlib -+import shutil -+import subprocess -+import sys -+from contextlib import asynccontextmanager -+from datetime import datetime -+from pathlib import Path -+ -+ -+DECODE_ARGS = ("UTF-8", "backslashreplace") -+ -+ -+# Work around a bug involving sys.exit and TaskGroups -+# (https://github.com/python/cpython/issues/101515). -+def exit(*args): -+ raise MySystemExit(*args) -+ -+ -+class MySystemExit(Exception): -+ pass -+ -+ -+# All subprocesses are executed through this context manager so that no matter -+# what happens, they can always be cancelled from another task, and they will -+# always be cleaned up on exit. -+@asynccontextmanager -+async def async_process(*args, **kwargs): -+ process = await asyncio.create_subprocess_exec(*args, **kwargs) -+ try: -+ yield process -+ finally: -+ if process.returncode is None: -+ # Allow a reasonably long time for Xcode to clean itself up, -+ # because we don't want stale emulators left behind. -+ timeout = 10 -+ process.terminate() -+ try: -+ await asyncio.wait_for(process.wait(), timeout) -+ except TimeoutError: -+ print( -+ f"Command {args} did not terminate after {timeout} seconds " -+ f" - sending SIGKILL" -+ ) -+ process.kill() -+ -+ # Even after killing the process we must still wait for it, -+ # otherwise we'll get the warning "Exception ignored in __del__". -+ await asyncio.wait_for(process.wait(), timeout=1) -+ -+ -+async def async_check_output(*args, **kwargs): -+ async with async_process( -+ *args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs -+ ) as process: -+ stdout, stderr = await process.communicate() -+ if process.returncode == 0: -+ return stdout.decode(*DECODE_ARGS) -+ else: -+ raise subprocess.CalledProcessError( -+ process.returncode, -+ args, -+ stdout.decode(*DECODE_ARGS), -+ stderr.decode(*DECODE_ARGS), -+ ) -+ -+ -+# Return a list of UDIDs associated with booted simulators -+async def list_devices(): -+ # List the testing simulators, in JSON format -+ raw_json = await async_check_output( -+ "xcrun", "simctl", "--set", "testing", "list", "-j" -+ ) -+ json_data = json.loads(raw_json) -+ -+ # Filter out the booted iOS simulators -+ return [ -+ simulator["udid"] -+ for runtime, simulators in json_data["devices"].items() -+ for simulator in simulators -+ if runtime.split(".")[-1].startswith("iOS") and simulator["state"] == "Booted" -+ ] -+ -+ -+async def find_device(initial_devices): -+ while True: -+ new_devices = set(await list_devices()).difference(initial_devices) -+ if len(new_devices) == 0: -+ await asyncio.sleep(1) -+ elif len(new_devices) == 1: -+ udid = new_devices.pop() -+ print(f"{datetime.now():%Y-%m-%d %H:%M:%S}: New test simulator detected") -+ print(f"UDID: {udid}") -+ return udid -+ else: -+ exit(f"Found more than one new device: {new_devices}") -+ -+ -+async def log_stream_task(initial_devices): -+ # Wait up to 5 minutes for the build to complete and the simulator to boot. -+ udid = await asyncio.wait_for(find_device(initial_devices), 5 * 60) -+ -+ # Stream the iOS device's logs, filtering out messages that come from the -+ # XCTest test suite (catching NSLog messages from the test method), or -+ # Python itself (catching stdout/stderr content routed to the system log -+ # with config->use_system_logger). -+ args = [ -+ "xcrun", -+ "simctl", -+ "--set", -+ "testing", -+ "spawn", -+ udid, -+ "log", -+ "stream", -+ "--style", -+ "compact", -+ "--predicate", -+ ( -+ 'senderImagePath ENDSWITH "/iOSTestbedTests.xctest/iOSTestbedTests"' -+ ' OR senderImagePath ENDSWITH "/Python.framework/Python"' -+ ), -+ ] -+ -+ async with async_process( -+ *args, -+ stdout=subprocess.PIPE, -+ stderr=subprocess.STDOUT, -+ ) as process: -+ suppress_dupes = False -+ while line := (await process.stdout.readline()).decode(*DECODE_ARGS): -+ # The iOS log streamer can sometimes lag; when it does, it outputs -+ # a warning about messages being dropped... often multiple times. -+ # Only print the first of these duplicated warnings. -+ if line.startswith("=== Messages dropped "): -+ if not suppress_dupes: -+ suppress_dupes = True -+ sys.stdout.write(line) -+ else: -+ suppress_dupes = False -+ sys.stdout.write(line) -+ sys.stdout.flush() -+ -+ -+async def xcode_test(location, simulator, verbose): -+ # Run the test suite on the named simulator -+ print("Starting xcodebuild...") -+ args = [ -+ "xcodebuild", -+ "test", -+ "-project", -+ str(location / "iOSTestbed.xcodeproj"), -+ "-scheme", -+ "iOSTestbed", -+ "-destination", -+ f"platform=iOS Simulator,name={simulator}", -+ "-resultBundlePath", -+ str(location / f"{datetime.now():%Y%m%d-%H%M%S}.xcresult"), -+ "-derivedDataPath", -+ str(location / "DerivedData"), -+ ] -+ if not verbose: -+ args += ["-quiet"] -+ -+ async with async_process( -+ *args, -+ stdout=subprocess.PIPE, -+ stderr=subprocess.STDOUT, -+ ) as process: -+ while line := (await process.stdout.readline()).decode(*DECODE_ARGS): -+ sys.stdout.write(line) -+ sys.stdout.flush() -+ -+ status = await asyncio.wait_for(process.wait(), timeout=1) -+ exit(status) -+ -+ -+def clone_testbed( -+ source: Path, -+ target: Path, -+ framework: Path, -+ apps: list[Path], -+) -> None: -+ if target.exists(): -+ print(f"{target} already exists; aborting without creating project.") -+ sys.exit(10) -+ -+ if framework is None: -+ if not ( -+ source / "Python.xcframework/ios-arm64_x86_64-simulator/bin" -+ ).is_dir(): -+ print( -+ f"The testbed being cloned ({source}) does not contain " -+ f"a simulator framework. Re-run with --framework" -+ ) -+ sys.exit(11) -+ else: -+ if not framework.is_dir(): -+ print(f"{framework} does not exist.") -+ sys.exit(12) -+ elif not ( -+ framework.suffix == ".xcframework" -+ or (framework / "Python.framework").is_dir() +@@ -230,33 +230,69 @@ + shutil.copytree(source, target, symlinks=True) + print(" done") + ++ xc_framework_path = target / "Python.xcframework" ++ sim_framework_path = xc_framework_path / "ios-arm64_x86_64-simulator" + if framework is not None: + if framework.suffix == ".xcframework": + print(" Installing XCFramework...", end="", flush=True) +- xc_framework_path = (target / "Python.xcframework").resolve() + if xc_framework_path.is_dir(): + shutil.rmtree(xc_framework_path) + else: +- xc_framework_path.unlink() ++ xc_framework_path.unlink(missing_ok=True) + xc_framework_path.symlink_to( + framework.relative_to(xc_framework_path.parent, walk_up=True) + ) + print(" done") + else: + print(" Installing simulator framework...", end="", flush=True) +- sim_framework_path = ( +- target / "Python.xcframework" / "ios-arm64_x86_64-simulator" +- ).resolve() + if sim_framework_path.is_dir(): + shutil.rmtree(sim_framework_path) + else: +- sim_framework_path.unlink() ++ sim_framework_path.unlink(missing_ok=True) + sim_framework_path.symlink_to( + framework.relative_to(sim_framework_path.parent, walk_up=True) + ) + print(" done") + else: +- print(" Using pre-existing iOS framework.") ++ if ( ++ xc_framework_path.is_symlink() ++ and not xc_framework_path.readlink().is_absolute() + ): -+ print( -+ f"{framework} is not an XCframework, " -+ f"or a simulator slice of a framework build." -+ ) -+ sys.exit(13) -+ -+ print("Cloning testbed project:") -+ print(f" Cloning {source}...", end="", flush=True) -+ shutil.copytree(source, target, symlinks=True) -+ print(" done") -+ -+ if framework is not None: -+ if framework.suffix == ".xcframework": -+ print(" Installing XCFramework...", end="", flush=True) -+ xc_framework_path = (target / "Python.xcframework").resolve() -+ if xc_framework_path.is_dir(): -+ shutil.rmtree(xc_framework_path) -+ else: -+ xc_framework_path.unlink() ++ # 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( -+ framework.relative_to(xc_framework_path.parent, walk_up=True) ++ orig_xc_framework_path.relative_to( ++ xc_framework_path.parent, walk_up=True ++ ) + ) + print(" done") -+ else: -+ print(" Installing simulator framework...", end="", flush=True) -+ sim_framework_path = ( -+ target / "Python.xcframework" / "ios-arm64_x86_64-simulator" ++ 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() -+ if sim_framework_path.is_dir(): -+ shutil.rmtree(sim_framework_path) -+ else: -+ sim_framework_path.unlink() ++ sim_framework_path.unlink() + sim_framework_path.symlink_to( -+ framework.relative_to(sim_framework_path.parent, walk_up=True) -+ ) -+ print(" done") -+ else: -+ print(" Using pre-existing iOS framework.") -+ -+ for app_src in apps: -+ print(f" Installing app {app_src.name!r}...", end="", flush=True) -+ app_target = target / f"iOSTestbed/app/{app_src.name}" -+ if app_target.is_dir(): -+ shutil.rmtree(app_target) -+ shutil.copytree(app_src, app_target) -+ print(" done") -+ -+ print(f"Successfully cloned testbed: {target.resolve()}") -+ -+ -+def update_plist(testbed_path, args): -+ # Add the test runner arguments to the testbed's Info.plist file. -+ info_plist = testbed_path / "iOSTestbed" / "iOSTestbed-Info.plist" -+ with info_plist.open("rb") as f: -+ info = plistlib.load(f) -+ -+ info["TestArgs"] = args -+ -+ with info_plist.open("wb") as f: -+ plistlib.dump(info, f) -+ -+ -+async def run_testbed(simulator: str, args: list[str], verbose: bool=False): -+ location = Path(__file__).parent -+ print("Updating plist...", end="", flush=True) -+ update_plist(location, args) -+ print(" done.") -+ -+ # Get the list of devices that are booted at the start of the test run. -+ # The simulator started by the test suite will be detected as the new -+ # entry that appears on the device list. -+ initial_devices = await list_devices() -+ -+ try: -+ async with asyncio.TaskGroup() as tg: -+ tg.create_task(log_stream_task(initial_devices)) -+ tg.create_task(xcode_test(location, simulator=simulator, verbose=verbose)) -+ except* MySystemExit as e: -+ raise SystemExit(*e.exceptions[0].args) from None -+ except* subprocess.CalledProcessError as e: -+ # Extract it from the ExceptionGroup so it can be handled by `main`. -+ raise e.exceptions[0] -+ -+ -+def main(): -+ parser = argparse.ArgumentParser( -+ description=( -+ "Manages the process of testing a Python project in the iOS simulator." -+ ), -+ ) -+ -+ subcommands = parser.add_subparsers(dest="subcommand") -+ -+ clone = subcommands.add_parser( -+ "clone", -+ description=( -+ "Clone the testbed project, copying in an iOS Python framework and" -+ "any specified application code." -+ ), -+ help="Clone a testbed project to a new location.", -+ ) -+ clone.add_argument( -+ "--framework", -+ help=( -+ "The location of the XCFramework (or simulator-only slice of an " -+ "XCFramework) to use when running the testbed" -+ ), -+ ) -+ clone.add_argument( -+ "--app", -+ dest="apps", -+ action="append", -+ default=[], -+ help="The location of any code to include in the testbed project", -+ ) -+ clone.add_argument( -+ "location", -+ help="The path where the testbed will be cloned.", -+ ) -+ -+ run = subcommands.add_parser( -+ "run", -+ usage="%(prog)s [-h] [--simulator SIMULATOR] -- [ ...]", -+ description=( -+ "Run a testbed project. The arguments provided after `--` will be " -+ "passed to the running iOS process as if they were arguments to " -+ "`python -m`." -+ ), -+ help="Run a testbed project", -+ ) -+ run.add_argument( -+ "--simulator", -+ default="iPhone SE (3rd Generation)", -+ help="The name of the simulator to use (default: 'iPhone SE (3rd Generation)')", -+ ) -+ run.add_argument( -+ "-v", "--verbose", -+ action="store_true", -+ help="Enable verbose output", -+ ) -+ -+ try: -+ pos = sys.argv.index("--") -+ testbed_args = sys.argv[1:pos] -+ test_args = sys.argv[pos + 1 :] -+ except ValueError: -+ testbed_args = sys.argv[1:] -+ test_args = [] -+ -+ context = parser.parse_args(testbed_args) -+ -+ if context.subcommand == "clone": -+ clone_testbed( -+ source=Path(__file__).parent, -+ target=Path(context.location), -+ framework=Path(context.framework).resolve() if context.framework else None, -+ apps=[Path(app) for app in context.apps], -+ ) -+ elif context.subcommand == "run": -+ if test_args: -+ if not ( -+ Path(__file__).parent / "Python.xcframework/ios-arm64_x86_64-simulator/bin" -+ ).is_dir(): -+ print( -+ f"Testbed does not contain a compiled iOS framework. Use " -+ f"`python {sys.argv[0]} clone ...` to create a runnable " -+ f"clone of this testbed." -+ ) -+ sys.exit(20) -+ -+ asyncio.run( -+ run_testbed( -+ simulator=context.simulator, -+ verbose=context.verbose, -+ args=test_args, ++ orig_sim_framework_path.relative_to( ++ sim_framework_path.parent, walk_up=True + ) + ) ++ print(" done") + else: -+ print(f"Must specify test arguments (e.g., {sys.argv[0]} run -- test)") -+ print() -+ parser.print_help(sys.stderr) -+ sys.exit(21) -+ else: -+ parser.print_help(sys.stderr) -+ sys.exit(1) -+ -+ -+if __name__ == "__main__": -+ main() -diff --git a/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj b/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj -index 6819ac0eeed..c7d63909ee2 100644 ---- a/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj -+++ b/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj -@@ -263,6 +263,7 @@ - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "set -e\n\nmkdir -p \"$CODESIGNING_FOLDER_PATH/python/lib\"\nif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-iphonesimulator\" ]; then\n echo \"Installing Python modules for iOS Simulator\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64_x86_64-simulator/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nelse\n echo \"Installing Python modules for iOS Device\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nfi\n"; -+ showEnvVarsInLog = 0; - }; - 607A66562B0F06200010BFC8 /* Prepare Python Binary Modules */ = { - isa = PBXShellScriptBuildPhase; -@@ -282,6 +283,7 @@ - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "set -e\n\ninstall_dylib () {\n INSTALL_BASE=$1\n FULL_EXT=$2\n\n # The name of the extension file\n EXT=$(basename \"$FULL_EXT\")\n # The location of the extension file, relative to the bundle\n RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/} \n # The path to the extension file, relative to the install base\n PYTHON_EXT=${RELATIVE_EXT/$INSTALL_BASE/}\n # The full dotted name of the extension module, constructed from the file path.\n FULL_MODULE_NAME=$(echo $PYTHON_EXT | cut -d \".\" -f 1 | tr \"/\" \".\"); \n # A bundle identifier; not actually used, but required by Xcode framework packaging\n FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr \"_\" \"-\")\n # The name of the framework folder.\n FRAMEWORK_FOLDER=\"Frameworks/$FULL_MODULE_NAME.framework\"\n\n # If the framework folder doesn't exist, create it.\n if [ ! -d \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\" ]; then\n echo \"Creating framework for $RELATIVE_EXT\" \n mkdir -p \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\"\n cp \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleExecutable -string \"$FULL_MODULE_NAME\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleIdentifier -string \"$FRAMEWORK_BUNDLE_ID\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n fi\n \n echo \"Installing binary for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" \n mv \"$FULL_EXT\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\"\n # Create a placeholder .fwork file where the .so was\n echo \"$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" > ${FULL_EXT%.so}.fwork\n # Create a back reference to the .so file location in the framework\n echo \"${RELATIVE_EXT%.so}.fwork\" > \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin\" \n}\n\nPYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/python/lib\")\necho \"Install Python $PYTHON_VER standard library extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib python/lib/$PYTHON_VER/lib-dynload/ \"$FULL_EXT\"\ndone\necho \"Install app package extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app_packages\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app_packages/ \"$FULL_EXT\"\ndone\necho \"Install app extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app/ \"$FULL_EXT\"\ndone\n\n# Clean up dylib template \nrm -f \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\"\necho \"Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)...\"\nfind \"$CODESIGNING_FOLDER_PATH/Frameworks\" -name \"*.framework\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der \"{}\" \\; \n"; -+ showEnvVarsInLog = 0; - }; - /* End PBXShellScriptBuildPhase section */ - -diff --git a/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m b/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m -index db00d43da85..6db38253396 100644 ---- a/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m -+++ b/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m -@@ -24,8 +24,11 @@ - - NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; - -- // Disable all color, as the Xcode log can't display color -+ // Set some other common environment indicators to disable color, as the -+ // Xcode log can't display color. Stdout will report that it is *not* a -+ // TTY. - setenv("NO_COLOR", "1", true); -+ setenv("PY_COLORS", "0", true); - - // Arguments to pass into the test suite runner. - // argv[0] must identify the process; any subsequent arg -@@ -50,6 +53,8 @@ - // Enforce UTF-8 encoding for stderr, stdout, file-system encoding and locale. - // See https://docs.python.org/3/library/os.html#python-utf-8-mode. - preconfig.utf8_mode = 1; -+ // Use the system logger for stdout/err -+ config.use_system_logger = 1; - // Don't buffer stdio. We want output to appears in the log immediately - config.buffered_stdio = 0; - // Don't write bytecode; we can't modify the app bundle ++ print(" Using pre-existing iOS framework.") + + for app_src in apps: + print(f" Installing app {app_src.name!r}...", end="", flush=True) +@@ -372,8 +408,8 @@ + + if context.subcommand == "clone": + clone_testbed( +- source=Path(__file__).parent, +- target=Path(context.location), ++ source=Path(__file__).parent.resolve(), ++ target=Path(context.location).resolve(), + framework=Path(context.framework).resolve() if context.framework else None, + apps=[Path(app) for app in context.apps], + ) --- /dev/null +++ b/tvOS/README.rst @@ -0,0 +1,108 @@ From 3a7cd861f523cd5a80b9ba1d452278cdc138cfd8 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 17 Feb 2025 14:38:25 +0800 Subject: [PATCH 13/26] More updates to USAGE and README (#250) * Adds details on `python-config` and `testbed` * Adds a bare bones interpreter instantiation example * Clarifies the difference between instantiating the interpreter, and accessing the Python API with PythonKit * Adds header exclusions to the modulemap to silence build warnings. --- README.rst | 35 ++++++-- USAGE.md | 113 ++++++++++++++++-------- patch/Python/module.modulemap | 160 ++++++++++++++++++++++++++++++++++ 3 files changed, 265 insertions(+), 43 deletions(-) diff --git a/README.rst b/README.rst index 94c0ae94..2317b749 100644 --- a/README.rst +++ b/README.rst @@ -83,15 +83,6 @@ Each support package contains: * ``VERSIONS``, a text file describing the specific versions of code used to build the support package; -* ``platform-site``, a folder that contains site customization scripts that can be used - to make your local Python install look like it is an on-device install for each of the - underlying target architectures supported by the platform. This is needed because when - you run ``pip`` you'll be on a macOS machine with a specific architecture; if ``pip`` - tries to install a binary package, it will install a macOS binary wheel (which won't - work on iOS/tvOS/watchOS). However, if you add the ``platform-site`` folder to your - ``PYTHONPATH`` when invoking pip, the site customization will make your Python install - return ``platform`` and ``sysconfig`` responses consistent with on-device behavior, - which will cause ``pip`` to install platform-appropriate packages. * ``Python.xcframework``, a multi-architecture build of the Python runtime library On iOS/tvOS/watchOS, the ``Python.xcframework`` contains a @@ -105,6 +96,32 @@ needed to build packages. This is required because Xcode uses the ``xcrun`` alias to dynamically generate the name of binaries, but a lot of C tooling expects that ``CC`` will not contain spaces. +Each slice of an iOS/tvOS/watchOS XCframework also contains a +``platform-config`` folder with a subfolder for each supported architecture in +that slice. These subfolders can be used to make a macOS Python environment +behave as if it were on an iOS/tvOS/watchOS device. This works in one of two +ways: + +1. **A sitecustomize.py script**. If the ``platform-config`` subfolder is on + your ``PYTHONPATH`` when a Python interpreter is started, a site + customization will be applied that patches methods in ``sys``, ``sysconfig`` + and ``platform`` that are used to identify the system. + +2. **A make_cross_venv.py script**. If you call ``make_cross_venv.py``, + providing the location of a virtual environment, the script will add some + files to the ``site-packages`` folder of that environment that will + automatically apply the same set of patches as the ``sitecustomize.py`` + script whenever the environment is activated, without any need to modify + ``PYTHONPATH``. If you use ``build`` to create an isolated PEP 517 + environment to build a wheel, these patches will also be applied to the + isolated build environment that is created. + +iOS distributions also contain a copy of the iOS ``testbed`` project - an Xcode +project that can be used to run test suites of Python code. See the `CPython +documentation on testing packages +`__ for +details on how to use this testbed. + For a detailed instructions on using the support package in your own project, see the `usage guide <./USAGE.md>`__ diff --git a/USAGE.md b/USAGE.md index 40bbcf80..096f71b0 100644 --- a/USAGE.md +++ b/USAGE.md @@ -25,6 +25,75 @@ guides: For tvOS and watchOS, you should be able to broadly follow the instructions in the iOS guide. +### Using Objective C + +Once you've added the Python XCframework to your project, you'll need to +initialize the Python runtime in your Objective C code (This is step 10 of the +iOS guide linked above). This initialization should generally be done as early +as possible in the application's lifecycle, but definitely needs to be done +before you invoke Python code. + +As a *bare minimum*, you can do the following: + +1. Import the Python C API headers: + ```objc + #include + ``` + +2. Initialize the Python interpreter: + ```objc + NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; + NSString *pythonHome = [NSString stringWithFormat:@"%@/python", resourcePath, nil]; + NSString *pythonPath = [NSString stringWithFormat:@"%@/lib/python3.13", python_home, nil]; + NSString *libDynloadPath = [NSString stringWithFormat:@"%@/lib/python3.13/lib-dynload", python_home, nil]; + NSString *appPath = [NSString stringWithFormat:@"%@/app", resourcePath, nil]; + + setenv("PYTHONHOME", pythonHome, 1); + setenv("PYTHONPATH", [NSString stringWithFormat:@"%@:%@:%@", pythonpath, libDynloadPath, appPath, nil]); + + Py_Initialize(); + + // we now have a Python interpreter ready to be used + ``` + References to a specific Python version should reflect the version of + Python you are using. + +Again - this is the *bare minimum* initialization. In practice, you will likely +need to configure other aspects of the Python interpreter using the +`PyPreConfig` and `PyConfig` mechanisms. Consult the [Python documentation on +interpreter configuration](https://docs.python.org/3/c-api/init_config.html) for +more details on the configuration options that are available. You may find the +[bootstrap mainline code used by +Briefcase](https://github.com/beeware/briefcase-iOS-Xcode-template/blob/main/%7B%7B%20cookiecutter.format%20%7D%7D/%7B%7B%20cookiecutter.class_name%20%7D%7D/main.m) +a helpful point of comparison. + +### Using Swift + +If you want to use Swift instead of Objective C, the bare minimum initialization +code will look something like this: + +1. Import the Python framework: + ```swift + import Python + ``` + +2. Initialize the Python interpreter: + ```swift + guard let pythonHome = Bundle.main.path(forResource: "python", ofType: nil) else { return } + guard let pythonPath = Bundle.main.path(forResource: "python/lib/python3.13", ofType: nil) else { return } + guard let libDynloadPath = Bundle.main.path(forResource: "python/lib/python3.13/lib-dynload", ofType: nil) else { return } + let appPath = Bundle.main.path(forResource: "app", ofType: nil) + + setenv("PYTHONHOME", pythonHome, 1) + setenv("PYTHONPATH", [pythonPath, libDynloadPath, appPath].compactMap { $0 }.joined(separator: ":"), 1) + Py_Initialize() + // we now have a Python interpreter ready to be used + ``` + + Again, references to a specific Python version should reflect the version of + Python you are using; and you will likely need to use `PyPreConfig` and + `PreConfig` APIs. + ## Accessing the Python runtime There are 2 ways to access the Python runtime in your project code. @@ -32,53 +101,29 @@ There are 2 ways to access the Python runtime in your project code. ### Embedded C API You can use the [Python Embedded C -API](https://docs.python.org/3/extending/embedding.html) to instantiate a Python -interpreter. This is the approach taken by Briefcase; you may find the bootstrap -mainline code generated by Briefcase a helpful guide to what is needed to start -an interpreter and run Python code. +API](https://docs.python.org/3/extending/embedding.html) to invoke Python code +and interact with Python objects. This is a raw C API that is accesible to both +Objective C and Swift. ### PythonKit -An alternate approach is to use +If you're using Swift, an alternate approach is to use [PythonKit](https://github.com/pvieito/PythonKit). PythonKit is a package that provides a Swift API to running Python code. To use PythonKit in your project, add the Python Apple Support package to your -project as described above; then: - -1. Add PythonKit to your project using the Swift Package manager. See the - PythonKit documentation for details. +project and instantiate a Python interpreter as described above; then add +PythonKit to your project using the Swift Package manager (see the [PythonKit +documentation](https://github.com/pvieito/PythonKit) for details). -2. In your Swift code, initialize the Python runtime. This should generally be - done as early as possible in the application's lifecycle, but definitely - needs to be done before you invoke Python code. References to a specific - Python version should reflect the version of Python you are using: +Once you've done this, you can import PythonKit: ```swift -import Python - -guard let pythonHome = Bundle.main.path(forResource: "python", ofType: nil) else { return } -guard let pythonPath = Bundle.main.path(forResource: "python/lib/python3.13", ofType: nil) else { return } -guard let libDynloadPath = Bundle.main.path(forResource: "python/lib/python3.13/lib-dynload", ofType: nil) else { return } -let appPath = Bundle.main.path(forResource: "app", ofType: nil) - -setenv("PYTHONHOME", pythonHome, 1) -setenv("PYTHONPATH", [pythonPath, libDynloadPath, appPath].compactMap { $0 }.joined(separator: ":"), 1) -Py_Initialize() -// we now have a Python interpreter ready to be used +import PythonKit ``` - -3. Invoke Python code in your app. For example: +and use the PythonKit Swift API to interact with Python code: ```swift -import PythonKit - let sys = Python.import("sys") print("Python Version: \(sys.version_info.major).\(sys.version_info.minor)") print("Python Encoding: \(sys.getdefaultencoding().upper())") print("Python Path: \(sys.path)") - -_ = Python.import("math") // verifies `lib-dynload` is found and signed successfully ``` - -To integrate 3rd party python code and dependencies, you will need to make sure -`PYTHONPATH` contains their paths; once this has been done, you can run -`Python.import("")`. to import that module from inside swift. diff --git a/patch/Python/module.modulemap b/patch/Python/module.modulemap index 9a4dcbb8..96b05fc6 100644 --- a/patch/Python/module.modulemap +++ b/patch/Python/module.modulemap @@ -2,4 +2,164 @@ module Python { umbrella header "Python.h" export * link "Python" + + exclude header "datetime.h" + exclude header "dynamic_annotations.h" + exclude header "errcode.h" + exclude header "frameobject.h" + exclude header "marshal.h" + exclude header "opcode_ids.h" + exclude header "opcode.h" + exclude header "osdefs.h" + exclude header "py_curses.h" + exclude header "pyconfig-arm32_64.h" + exclude header "pyconfig-arm64.h" + exclude header "pyconfig-x86_64.h" + exclude header "pydtrace.h" + exclude header "pyexpat.h" + exclude header "structmember.h" + + exclude header "cpython/frameobject.h" + exclude header "cpython/pthread_stubs.h" + exclude header "cpython/pyatomic_msc.h" + exclude header "cpython/pyatomic_std.h" + exclude header "cpython/pystats.h" + + exclude header "internal/mimalloc/mimalloc.h" + exclude header "internal/mimalloc/mimalloc/atomic.h" + exclude header "internal/mimalloc/mimalloc/internal.h" + exclude header "internal/mimalloc/mimalloc/prim.h" + exclude header "internal/mimalloc/mimalloc/track.h" + exclude header "internal/mimalloc/mimalloc/types.h" + + exclude header "internal/pycore_abstract.h" + exclude header "internal/pycore_asdl.h" + exclude header "internal/pycore_ast_state.h" + exclude header "internal/pycore_ast.h" + exclude header "internal/pycore_atexit.h" + exclude header "internal/pycore_audit.h" + exclude header "internal/pycore_backoff.h" + exclude header "internal/pycore_bitutils.h" + exclude header "internal/pycore_blocks_output_buffer.h" + exclude header "internal/pycore_brc.h" + exclude header "internal/pycore_bytes_methods.h" + exclude header "internal/pycore_bytesobject.h" + exclude header "internal/pycore_call.h" + exclude header "internal/pycore_capsule.h" + exclude header "internal/pycore_cell.h" + exclude header "internal/pycore_ceval_state.h" + exclude header "internal/pycore_ceval.h" + exclude header "internal/pycore_code.h" + exclude header "internal/pycore_codecs.h" + exclude header "internal/pycore_compile.h" + exclude header "internal/pycore_complexobject.h" + exclude header "internal/pycore_condvar.h" + exclude header "internal/pycore_context.h" + exclude header "internal/pycore_critical_section.h" + exclude header "internal/pycore_crossinterp_data_registry.h" + exclude header "internal/pycore_crossinterp.h" + exclude header "internal/pycore_debug_offsets.h" + exclude header "internal/pycore_descrobject.h" + exclude header "internal/pycore_dict_state.h" + exclude header "internal/pycore_dict.h" + exclude header "internal/pycore_dtoa.h" + exclude header "internal/pycore_emscripten_signal.h" + exclude header "internal/pycore_emscripten_trampoline.h" + exclude header "internal/pycore_exceptions.h" + exclude header "internal/pycore_faulthandler.h" + exclude header "internal/pycore_fileutils_windows.h" + exclude header "internal/pycore_fileutils.h" + exclude header "internal/pycore_floatobject.h" + exclude header "internal/pycore_flowgraph.h" + exclude header "internal/pycore_format.h" + exclude header "internal/pycore_frame.h" + exclude header "internal/pycore_freelist_state.h" + exclude header "internal/pycore_freelist.h" + exclude header "internal/pycore_function.h" + exclude header "internal/pycore_gc.h" + exclude header "internal/pycore_genobject.h" + exclude header "internal/pycore_getopt.h" + exclude header "internal/pycore_gil.h" + exclude header "internal/pycore_global_objects_fini_generated.h" + exclude header "internal/pycore_global_objects.h" + exclude header "internal/pycore_global_strings.h" + exclude header "internal/pycore_hamt.h" + exclude header "internal/pycore_hashtable.h" + exclude header "internal/pycore_identifier.h" + exclude header "internal/pycore_import.h" + exclude header "internal/pycore_importdl.h" + exclude header "internal/pycore_index_pool.h" + exclude header "internal/pycore_initconfig.h" + exclude header "internal/pycore_instruction_sequence.h" + exclude header "internal/pycore_instruments.h" + exclude header "internal/pycore_interp.h" + exclude header "internal/pycore_intrinsics.h" + exclude header "internal/pycore_jit.h" + exclude header "internal/pycore_list.h" + exclude header "internal/pycore_llist.h" + exclude header "internal/pycore_lock.h" + exclude header "internal/pycore_long.h" + exclude header "internal/pycore_magic_number.h" + exclude header "internal/pycore_memoryobject.h" + exclude header "internal/pycore_mimalloc.h" + exclude header "internal/pycore_modsupport.h" + exclude header "internal/pycore_moduleobject.h" + exclude header "internal/pycore_namespace.h" + exclude header "internal/pycore_object_alloc.h" + exclude header "internal/pycore_object_deferred.h" + exclude header "internal/pycore_object_stack.h" + exclude header "internal/pycore_object_state.h" + exclude header "internal/pycore_object.h" + exclude header "internal/pycore_obmalloc_init.h" + exclude header "internal/pycore_obmalloc.h" + exclude header "internal/pycore_opcode_metadata.h" + exclude header "internal/pycore_opcode_utils.h" + exclude header "internal/pycore_optimizer.h" + exclude header "internal/pycore_parking_lot.h" + exclude header "internal/pycore_parser.h" + exclude header "internal/pycore_pathconfig.h" + exclude header "internal/pycore_pyarena.h" + exclude header "internal/pycore_pyatomic_ft_wrappers.h" + exclude header "internal/pycore_pybuffer.h" + exclude header "internal/pycore_pyerrors.h" + exclude header "internal/pycore_pyhash.h" + exclude header "internal/pycore_pylifecycle.h" + exclude header "internal/pycore_pymath.h" + exclude header "internal/pycore_pymem_init.h" + exclude header "internal/pycore_pymem.h" + exclude header "internal/pycore_pystate.h" + exclude header "internal/pycore_pystats.h" + exclude header "internal/pycore_pythonrun.h" + exclude header "internal/pycore_pythread.h" + exclude header "internal/pycore_qsbr.h" + exclude header "internal/pycore_range.h" + exclude header "internal/pycore_runtime_init_generated.h" + exclude header "internal/pycore_runtime_init.h" + exclude header "internal/pycore_runtime.h" + exclude header "internal/pycore_semaphore.h" + exclude header "internal/pycore_setobject.h" + exclude header "internal/pycore_signal.h" + exclude header "internal/pycore_sliceobject.h" + exclude header "internal/pycore_stackref.h" + exclude header "internal/pycore_strhex.h" + exclude header "internal/pycore_structseq.h" + exclude header "internal/pycore_symtable.h" + exclude header "internal/pycore_sysmodule.h" + exclude header "internal/pycore_time.h" + exclude header "internal/pycore_token.h" + exclude header "internal/pycore_traceback.h" + exclude header "internal/pycore_tracemalloc.h" + exclude header "internal/pycore_tstate.h" + exclude header "internal/pycore_tuple.h" + exclude header "internal/pycore_typeobject.h" + exclude header "internal/pycore_typevarobject.h" + exclude header "internal/pycore_ucnhash.h" + exclude header "internal/pycore_unicodeobject_generated.h" + exclude header "internal/pycore_unicodeobject.h" + exclude header "internal/pycore_unionobject.h" + exclude header "internal/pycore_uniqueid.h" + exclude header "internal/pycore_uop_ids.h" + exclude header "internal/pycore_uop_metadata.h" + exclude header "internal/pycore_warnings.h" + exclude header "internal/pycore_weakref.h" } From 1158013c280b625e64db4470c528f03f61b48546 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 06:08:50 +0800 Subject: [PATCH 14/26] Bump ncipollo/release-action from 1.15.0 to 1.16.0 (#253) Bumps [ncipollo/release-action](https://github.com/ncipollo/release-action) from 1.15.0 to 1.16.0. - [Release notes](https://github.com/ncipollo/release-action/releases) - [Commits](https://github.com/ncipollo/release-action/compare/v1.15.0...v1.16.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 4c0a16a4..0980fb50 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -47,7 +47,7 @@ jobs: merge-multiple: true - name: Create Release - uses: ncipollo/release-action@v1.15.0 + uses: ncipollo/release-action@v1.16.0 with: name: ${{ needs.ci.outputs.PYTHON_VER }}-${{ needs.config.outputs.BUILD_NUMBER }} tag: ${{ needs.ci.outputs.PYTHON_VER }}-${{ needs.config.outputs.BUILD_NUMBER }} From 832d39318cb752f0a0165ee2eeee263610fb12ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 06:09:14 +0800 Subject: [PATCH 15/26] Bump actions/upload-artifact from 4.6.0 to 4.6.1 (#254) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.0 to 4.6.1. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.6.0...v4.6.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 44609a7a..ef7ad512 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -121,7 +121,7 @@ jobs: make ${{ matrix.target }} BUILD_NUMBER=${{ needs.config.outputs.BUILD_NUMBER }} - name: Upload build artefacts - uses: actions/upload-artifact@v4.6.0 + uses: actions/upload-artifact@v4.6.1 with: name: Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz path: dist/Python-${{ needs.config.outputs.PYTHON_VER }}-${{ matrix.target }}-support.${{ needs.config.outputs.BUILD_NUMBER }}.tar.gz From aa545e919535a3334cf17a79dc4acfdd512d9a41 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 26 Feb 2025 07:05:14 +0800 Subject: [PATCH 16/26] Add patch for handling empty simulator lists. --- patch/Python/Python.patch | 49 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 910f33c2..139da995 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1271,10 +1271,53 @@ index c3e261ecd9e..26ef7a95de4 100644 iPhoneOS diff --git a/iOS/testbed/__main__.py b/iOS/testbed/__main__.py -index b4499f5ac17..08fbe90a1c6 100644 +index b4499f5ac17..d12a5ab065b 100644 --- a/iOS/testbed/__main__.py +++ b/iOS/testbed/__main__.py -@@ -230,33 +230,69 @@ +@@ -82,19 +82,29 @@ + + # Return a list of UDIDs associated with booted simulators + async def list_devices(): +- # List the testing simulators, in JSON format +- raw_json = await async_check_output( +- "xcrun", "simctl", "--set", "testing", "list", "-j" +- ) +- json_data = json.loads(raw_json) +- +- # Filter out the booted iOS simulators +- return [ +- simulator["udid"] +- for runtime, simulators in json_data["devices"].items() +- for simulator in simulators +- if runtime.split(".")[-1].startswith("iOS") and simulator["state"] == "Booted" +- ] ++ try: ++ # List the testing simulators, in JSON format ++ raw_json = await async_check_output( ++ "xcrun", "simctl", "--set", "testing", "list", "-j" ++ ) ++ json_data = json.loads(raw_json) ++ ++ # Filter out the booted iOS simulators ++ return [ ++ simulator["udid"] ++ for runtime, simulators in json_data["devices"].items() ++ for simulator in simulators ++ if runtime.split(".")[-1].startswith("iOS") and simulator["state"] == "Booted" ++ ] ++ except subprocess.CalledProcessError as e: ++ # If there's no ~/Library/Developer/XCTestDevices folder (which is the ++ # case on fresh installs, and in some CI environments), `simctl list` ++ # returns error code 1, rather than an empty list. Handle that case, ++ # but raise all other errors. ++ if e.returncode == 1: ++ return [] ++ else: ++ raise + + + async def find_device(initial_devices): +@@ -230,33 +240,69 @@ shutil.copytree(source, target, symlinks=True) print(" done") @@ -1351,7 +1394,7 @@ index b4499f5ac17..08fbe90a1c6 100644 for app_src in apps: print(f" Installing app {app_src.name!r}...", end="", flush=True) -@@ -372,8 +408,8 @@ +@@ -372,8 +418,8 @@ if context.subcommand == "clone": clone_testbed( From f2ab588b35a47f5820d3645a72bffa1f89dae266 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 26 Feb 2025 07:07:09 +0800 Subject: [PATCH 17/26] Add patch for handling empty simulator lists. --- patch/Python/Python.patch | 49 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 8db1f456..b6777235 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1270,10 +1270,53 @@ index c3e261ecd9e..26ef7a95de4 100644 iPhoneOS diff --git a/iOS/testbed/__main__.py b/iOS/testbed/__main__.py -index b4499f5ac17..08fbe90a1c6 100644 +index b4499f5ac17..d12a5ab065b 100644 --- a/iOS/testbed/__main__.py +++ b/iOS/testbed/__main__.py -@@ -230,33 +230,69 @@ +@@ -82,19 +82,29 @@ + + # Return a list of UDIDs associated with booted simulators + async def list_devices(): +- # List the testing simulators, in JSON format +- raw_json = await async_check_output( +- "xcrun", "simctl", "--set", "testing", "list", "-j" +- ) +- json_data = json.loads(raw_json) +- +- # Filter out the booted iOS simulators +- return [ +- simulator["udid"] +- for runtime, simulators in json_data["devices"].items() +- for simulator in simulators +- if runtime.split(".")[-1].startswith("iOS") and simulator["state"] == "Booted" +- ] ++ try: ++ # List the testing simulators, in JSON format ++ raw_json = await async_check_output( ++ "xcrun", "simctl", "--set", "testing", "list", "-j" ++ ) ++ json_data = json.loads(raw_json) ++ ++ # Filter out the booted iOS simulators ++ return [ ++ simulator["udid"] ++ for runtime, simulators in json_data["devices"].items() ++ for simulator in simulators ++ if runtime.split(".")[-1].startswith("iOS") and simulator["state"] == "Booted" ++ ] ++ except subprocess.CalledProcessError as e: ++ # If there's no ~/Library/Developer/XCTestDevices folder (which is the ++ # case on fresh installs, and in some CI environments), `simctl list` ++ # returns error code 1, rather than an empty list. Handle that case, ++ # but raise all other errors. ++ if e.returncode == 1: ++ return [] ++ else: ++ raise + + + async def find_device(initial_devices): +@@ -230,33 +240,69 @@ shutil.copytree(source, target, symlinks=True) print(" done") @@ -1350,7 +1393,7 @@ index b4499f5ac17..08fbe90a1c6 100644 for app_src in apps: print(f" Installing app {app_src.name!r}...", end="", flush=True) -@@ -372,8 +408,8 @@ +@@ -372,8 +418,8 @@ if context.subcommand == "clone": clone_testbed( From c794b061d3092ea6716039b22118de85fd98aa27 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 08:56:06 +0800 Subject: [PATCH 18/26] Bump actions/download-artifact from 4.1.8 to 4.1.9 (#255) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.1.8 to 4.1.9. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4.1.8...v4.1.9) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 0980fb50..61da403c 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -40,7 +40,7 @@ jobs: needs: [ config, ci ] steps: - name: Get build artifacts - uses: actions/download-artifact@v4.1.8 + uses: actions/download-artifact@v4.1.9 with: pattern: Python-* path: dist From 383a3e38744b3e358c9fc60003c95f8dfae7b4d3 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 14 Mar 2025 14:14:41 +0800 Subject: [PATCH 19/26] Update patch with recent Python main changes to iOS support. --- patch/Python/Python.patch | 262 +++++++++++++++++--------------------- 1 file changed, 115 insertions(+), 147 deletions(-) diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 2cfffa07..06990a0a 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1,25 +1,3 @@ -diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst -index c586cfb39e8..9a7db851122 100644 ---- a/Doc/c-api/init_config.rst -+++ b/Doc/c-api/init_config.rst -@@ -1212,6 +1212,17 @@ - - Default: ``1`` in Python config and ``0`` in isolated config. - -+ .. c:member:: int use_system_logger -+ -+ If non-zero, ``stdout`` and ``stderr`` will be redirected to the system -+ log. -+ -+ Only available on macOS 10.12 and later, and on iOS. -+ -+ Default: ``0`` (don't use system log). -+ -+ .. versionadded:: 3.13.2 -+ - .. c:member:: int user_site_directory - - If non-zero, add the user site directory to :data:`sys.path`. --- /dev/null +++ b/Doc/includes/wasm-ios-notavail.rst @@ -0,0 +1,8 @@ @@ -1038,7 +1016,7 @@ index e1a3111f36a..f55a12f1ab8 100644 editors.rst --- /dev/null +++ b/Doc/using/ios.rst -@@ -0,0 +1,388 @@ +@@ -0,0 +1,386 @@ +.. _using-ios: + +=================== @@ -1337,8 +1315,6 @@ index e1a3111f36a..f55a12f1ab8 100644 + * Buffered stdio (:c:member:`PyConfig.buffered_stdio`) is *disabled*; + * Writing bytecode (:c:member:`PyConfig.write_bytecode`) is *disabled*; + * Signal handlers (:c:member:`PyConfig.install_signal_handlers`) are *enabled*; -+ * System logging (:c:member:`PyConfig.use_system_logger`) is *enabled* -+ (optional, but strongly recommended); + * ``PYTHONHOME`` for the interpreter is configured to point at the + ``python`` subfolder of your app's bundle; and + * The ``PYTHONPATH`` for the interpreter includes: @@ -1460,20 +1436,6 @@ index 8b67652d1df..2dfac075843 100644 Other Resources =============== -diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h -index cbae97f12f5..20b4bc0bae1 100644 ---- a/Include/cpython/initconfig.h -+++ b/Include/cpython/initconfig.h -@@ -180,6 +180,9 @@ - int use_frozen_modules; - int safe_path; - int int_max_str_digits; -+#ifdef __APPLE__ -+ int use_system_logger; -+#endif - - /* --- Path configuration inputs ------------ */ - int pathconfig_warnings; --- /dev/null +++ b/Lib/_apple_support.py @@ -0,0 +1,66 @@ @@ -2203,11 +2165,11 @@ index 20f38fd36a8..1f64710c9c2 100644 @@ -0,0 +1,155 @@ +import unittest +from _apple_support import SystemLog -+from test.support import is_apple ++from test.support import is_apple_mobile +from unittest.mock import Mock, call + -+if not is_apple: -+ raise unittest.SkipTest("Apple-specific") ++if not is_apple_mobile: ++ raise unittest.SkipTest("iOS-specific") + + +# Test redirection of stdout and stderr to the Apple system log. @@ -2622,19 +2584,6 @@ index 6e4a4b7caff..d1357ea7fcd 100644 @unittest.skipUnless(hasattr(os, 'register_at_fork'), 'need os.register_at_fork') @support.requires_resource('cpu') def test_hang_global_shutdown_lock(self): -diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py -index 13713cf37b8..d4cd52db456 100644 ---- a/Lib/test/test_embed.py -+++ b/Lib/test/test_embed.py -@@ -578,6 +578,8 @@ - CONFIG_COMPAT.update({ - 'legacy_windows_stdio': 0, - }) -+ if support.is_apple: -+ CONFIG_COMPAT['use_system_logger'] = False - - CONFIG_PYTHON = dict(CONFIG_COMPAT, - _config_init=API_PYTHON, diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py index 203dd6fe57d..6d734d05245 100644 --- a/Lib/test/test_fcntl.py @@ -4503,50 +4452,6 @@ index 0a310000751..83a2bc469ae 100644 return _PyStatus_OK(); } - -diff --git a/Python/initconfig.c b/Python/initconfig.c -index 192089b5cc3..1b6e90668e8 100644 ---- a/Python/initconfig.c -+++ b/Python/initconfig.c -@@ -680,6 +680,9 @@ - assert(config->int_max_str_digits >= 0); - // config->use_frozen_modules is initialized later - // by _PyConfig_InitImportConfig(). -+#ifdef __APPLE__ -+ assert(config->use_system_logger >= 0); -+#endif - return 1; - } - #endif -@@ -774,6 +777,9 @@ - config->int_max_str_digits = -1; - config->_is_python_build = 0; - config->code_debug_ranges = 1; -+#ifdef __APPLE__ -+ config->use_system_logger = 0; -+#endif - } - - -@@ -799,6 +805,9 @@ - #ifdef MS_WINDOWS - config->legacy_windows_stdio = 0; - #endif -+#ifdef __APPLE__ -+ config->use_system_logger = 0; -+#endif - } - - -@@ -834,6 +843,9 @@ - #ifdef MS_WINDOWS - config->legacy_windows_stdio = 0; - #endif -+#ifdef __APPLE__ -+ config->use_system_logger = 0; -+#endif - } - - diff --git a/Python/marshal.c b/Python/marshal.c index 3fc3f890422..892debe38dc 100644 --- a/Python/marshal.c @@ -4581,40 +4486,50 @@ index 3fc3f890422..892debe38dc 100644 #define TYPE_NULL '0' diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c -index e9c1a0d72d6..067b75a5b63 100644 +index e9c1a0d72d6..811ebcb14ce 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c -@@ -34,7 +34,9 @@ +@@ -34,7 +34,21 @@ #include // getenv() #if defined(__APPLE__) -#include +# include ++# include +# include -+# include ++// The os_log unified logging APIs were introduced in macOS 10.12, iOS 10.0, ++// tvOS 10.0, and watchOS 3.0; we enable the use of the system logger ++// automatically on non-macOS platforms. ++# if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE ++# define USE_APPLE_SYSTEM_LOG 1 ++# else ++# define USE_APPLE_SYSTEM_LOG 0 ++# endif ++ ++# if USE_APPLE_SYSTEM_LOG ++# include ++# endif #endif #ifdef HAVE_SIGNAL_H -@@ -66,6 +68,9 @@ +@@ -66,6 +80,9 @@ static PyStatus init_import_site(void); static PyStatus init_set_builtins_open(void); static PyStatus init_sys_streams(PyThreadState *tstate); -+#if defined(__APPLE__) ++#if defined(__APPLE__) && USE_APPLE_SYSTEM_LOG +static PyStatus init_apple_streams(PyThreadState *tstate); +#endif static void wait_for_thread_shutdown(PyThreadState *tstate); static void call_ll_exitfuncs(_PyRuntimeState *runtime); -@@ -1177,6 +1182,19 @@ +@@ -1177,6 +1194,17 @@ return status; } -+#if defined(__APPLE__) -+ if (config->use_system_logger) { -+ status = init_apple_streams(tstate); -+ if (_PyStatus_EXCEPTION(status)) { -+ return status; -+ } ++#if defined(__APPLE__) && USE_APPLE_SYSTEM_LOG ++ status = init_apple_streams(tstate); ++ if (_PyStatus_EXCEPTION(status)) { ++ return status; + } +#endif + @@ -4625,11 +4540,11 @@ index e9c1a0d72d6..067b75a5b63 100644 status = add_main_module(interp); if (_PyStatus_EXCEPTION(status)) { return status; -@@ -2620,6 +2638,75 @@ +@@ -2620,6 +2648,69 @@ return res; } -+#if defined(__APPLE__) ++#if defined(__APPLE__) && USE_APPLE_SYSTEM_LOG + +static PyObject * +apple_log_write_impl(PyObject *self, PyObject *args) @@ -4640,14 +4555,9 @@ index e9c1a0d72d6..067b75a5b63 100644 + return NULL; + } + -+ // Call the underlying Apple logging API. The os_log unified logging APIs -+ // were introduced in macOS 10.12, iOS 10.0, tvOS 10.0, and watchOS 3.0; -+ // this call is a no-op on older versions. -+ #if TARGET_OS_IPHONE || (TARGET_OS_OSX && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) + // Pass the user-provided text through explicit %s formatting + // to avoid % literals being interpreted as a formatting directive. + os_log_with_type(OS_LOG_DEFAULT, logtype, "%s", text); -+ #endif + Py_RETURN_NONE; +} + @@ -4682,7 +4592,6 @@ index e9c1a0d72d6..067b75a5b63 100644 + if (result == NULL) { + goto error; + } -+ + goto done; + +error: @@ -4696,7 +4605,7 @@ index e9c1a0d72d6..067b75a5b63 100644 + return status; +} + -+#endif // __APPLE__ ++#endif // __APPLE__ && USE_APPLE_SYSTEM_LOG + static void @@ -8582,11 +8491,12 @@ index 9270b5f7172..252fc9750e5 100644 +build for testing purposes (either x86_64 or ARM64). --- /dev/null +++ b/iOS/testbed/__main__.py -@@ -0,0 +1,395 @@ +@@ -0,0 +1,456 @@ +import argparse +import asyncio +import json +import plistlib ++import re +import shutil +import subprocess +import sys @@ -8597,6 +8507,18 @@ index 9270b5f7172..252fc9750e5 100644 + +DECODE_ARGS = ("UTF-8", "backslashreplace") + ++# The system log prefixes each line: ++# 2025-01-17 16:14:29.090 Df iOSTestbed[23987:1fd393b4] (Python) ... ++# 2025-01-17 16:14:29.090 E iOSTestbed[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+iOSTestbed\[\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). @@ -8654,19 +8576,29 @@ index 9270b5f7172..252fc9750e5 100644 + +# Return a list of UDIDs associated with booted simulators +async def list_devices(): -+ # List the testing simulators, in JSON format -+ raw_json = await async_check_output( -+ "xcrun", "simctl", "--set", "testing", "list", "-j" -+ ) -+ json_data = json.loads(raw_json) -+ -+ # Filter out the booted iOS simulators -+ return [ -+ simulator["udid"] -+ for runtime, simulators in json_data["devices"].items() -+ for simulator in simulators -+ if runtime.split(".")[-1].startswith("iOS") and simulator["state"] == "Booted" -+ ] ++ try: ++ # List the testing simulators, in JSON format ++ raw_json = await async_check_output( ++ "xcrun", "simctl", "--set", "testing", "list", "-j" ++ ) ++ json_data = json.loads(raw_json) ++ ++ # Filter out the booted iOS simulators ++ return [ ++ simulator["udid"] ++ for runtime, simulators in json_data["devices"].items() ++ for simulator in simulators ++ if runtime.split(".")[-1].startswith("iOS") and simulator["state"] == "Booted" ++ ] ++ except subprocess.CalledProcessError as e: ++ # If there's no ~/Library/Developer/XCTestDevices folder (which is the ++ # case on fresh installs, and in some CI environments), `simctl list` ++ # returns error code 1, rather than an empty list. Handle that case, ++ # but raise all other errors. ++ if e.returncode == 1: ++ return [] ++ else: ++ raise + + +async def find_device(initial_devices): @@ -8716,6 +8648,8 @@ index 9270b5f7172..252fc9750e5 100644 + ) 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 iOS log streamer can sometimes lag; when it does, it outputs + # a warning about messages being dropped... often multiple times. + # Only print the first of these duplicated warnings. @@ -8800,33 +8734,69 @@ index 9270b5f7172..252fc9750e5 100644 + shutil.copytree(source, target, symlinks=True) + print(" done") + ++ xc_framework_path = target / "Python.xcframework" ++ sim_framework_path = xc_framework_path / "ios-arm64_x86_64-simulator" + if framework is not None: + if framework.suffix == ".xcframework": + print(" Installing XCFramework...", end="", flush=True) -+ xc_framework_path = (target / "Python.xcframework").resolve() + if xc_framework_path.is_dir(): + shutil.rmtree(xc_framework_path) + else: -+ xc_framework_path.unlink() ++ xc_framework_path.unlink(missing_ok=True) + xc_framework_path.symlink_to( + framework.relative_to(xc_framework_path.parent, walk_up=True) + ) + print(" done") + else: + print(" Installing simulator framework...", end="", flush=True) -+ sim_framework_path = ( -+ target / "Python.xcframework" / "ios-arm64_x86_64-simulator" -+ ).resolve() + if sim_framework_path.is_dir(): + shutil.rmtree(sim_framework_path) + else: -+ sim_framework_path.unlink() ++ sim_framework_path.unlink(missing_ok=True) + sim_framework_path.symlink_to( + framework.relative_to(sim_framework_path.parent, walk_up=True) + ) + print(" done") + else: -+ print(" Using pre-existing iOS framework.") ++ if ( ++ xc_framework_path.is_symlink() ++ and not xc_framework_path.readlink().is_absolute() ++ ): ++ # XCFramework is a relative symlink. Rewrite the symlink relative ++ # to the new location. ++ print(" Rewriting symlink to XCframework...", end="", flush=True) ++ orig_xc_framework_path = ( ++ source ++ / xc_framework_path.readlink() ++ ).resolve() ++ xc_framework_path.unlink() ++ xc_framework_path.symlink_to( ++ orig_xc_framework_path.relative_to( ++ xc_framework_path.parent, walk_up=True ++ ) ++ ) ++ print(" done") ++ elif ( ++ sim_framework_path.is_symlink() ++ and not sim_framework_path.readlink().is_absolute() ++ ): ++ print(" Rewriting symlink to simulator framework...", end="", flush=True) ++ # Simulator framework is a relative symlink. Rewrite the symlink ++ # relative to the new location. ++ orig_sim_framework_path = ( ++ source ++ / "Python.XCframework" ++ / sim_framework_path.readlink() ++ ).resolve() ++ sim_framework_path.unlink() ++ sim_framework_path.symlink_to( ++ orig_sim_framework_path.relative_to( ++ sim_framework_path.parent, walk_up=True ++ ) ++ ) ++ print(" done") ++ else: ++ print(" Using pre-existing iOS framework.") + + for app_src in apps: + print(f" Installing app {app_src.name!r}...", end="", flush=True) @@ -8942,8 +8912,8 @@ index 9270b5f7172..252fc9750e5 100644 + + if context.subcommand == "clone": + clone_testbed( -+ source=Path(__file__).parent, -+ target=Path(context.location), ++ source=Path(__file__).parent.resolve(), ++ target=Path(context.location).resolve(), + framework=Path(context.framework).resolve() if context.framework else None, + apps=[Path(app) for app in context.apps], + ) @@ -9783,7 +9753,7 @@ index 9270b5f7172..252fc9750e5 100644 +} --- /dev/null +++ b/iOS/testbed/iOSTestbedTests/iOSTestbedTests.m -@@ -0,0 +1,162 @@ +@@ -0,0 +1,160 @@ +#import +#import + @@ -9814,7 +9784,7 @@ index 9270b5f7172..252fc9750e5 100644 + // Xcode log can't display color. Stdout will report that it is *not* a + // TTY. + setenv("NO_COLOR", "1", true); -+ setenv("PY_COLORS", "0", true); ++ setenv("PYTHON_COLORS", "0", true); + + // Arguments to pass into the test suite runner. + // argv[0] must identify the process; any subsequent arg @@ -9839,8 +9809,6 @@ index 9270b5f7172..252fc9750e5 100644 + // 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 From 7ec320f77d4097cafd9843b4ff9e8e60786a8710 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 14 Mar 2025 14:19:40 +0800 Subject: [PATCH 20/26] No need to run CI on pushes. --- .github/workflows/ci.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ef7ad512..e1c8f38e 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,10 +1,6 @@ name: CI on: pull_request: - push: - branches: - - main - - 3.* workflow_call: inputs: build-number: From 9f467c795733ac6106b81a3bfe393cced21a6834 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 14 Mar 2025 14:21:20 +0800 Subject: [PATCH 21/26] Remove references to sysconfig_vars.json --- Makefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Makefile b/Makefile index 88d86143..e1b5d371 100644 --- a/Makefile +++ b/Makefile @@ -488,13 +488,11 @@ $$(PYTHON_STDLIB-$(sdk))/LICENSE.TXT: $$(PYTHON_LIB-$(sdk)) $$(PYTHON_FRAMEWORK- # Delete the single-SDK parts of the standard library rm -rf \ $$(PYTHON_STDLIB-$(sdk))/_sysconfigdata__*.py \ - $$(PYTHON_STDLIB-$(sdk))/_sysconfig_vars__*.json \ $$(PYTHON_STDLIB-$(sdk))/config-* \ $$(PYTHON_STDLIB-$(sdk))/lib-dynload/* # Copy the individual _sysconfigdata modules into names that include the architecture $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_STDLIB-$$(target))/_sysconfigdata_* $$(PYTHON_STDLIB-$(sdk))/; ) - $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_STDLIB-$$(target))/_sysconfig_vars_* $$(PYTHON_STDLIB-$(sdk))/; ) # Copy the platform site folders for each architecture mkdir -p $$(PYTHON_PLATFORM_CONFIG-$(sdk)) From e16b12efe5e2c4b1e3c9bb12537f9c0dc0fb0a1e Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 14 Mar 2025 14:40:53 +0800 Subject: [PATCH 22/26] Update modulemap for 3.12. --- patch/Python/module.modulemap | 62 +++-------------------------------- 1 file changed, 5 insertions(+), 57 deletions(-) diff --git a/patch/Python/module.modulemap b/patch/Python/module.modulemap index 96b05fc6..23a502bf 100644 --- a/patch/Python/module.modulemap +++ b/patch/Python/module.modulemap @@ -7,64 +7,42 @@ module Python { exclude header "dynamic_annotations.h" exclude header "errcode.h" exclude header "frameobject.h" + exclude header "interpreteridobject.h" exclude header "marshal.h" - exclude header "opcode_ids.h" exclude header "opcode.h" exclude header "osdefs.h" exclude header "py_curses.h" - exclude header "pyconfig-arm32_64.h" - exclude header "pyconfig-arm64.h" - exclude header "pyconfig-x86_64.h" exclude header "pydtrace.h" exclude header "pyexpat.h" exclude header "structmember.h" exclude header "cpython/frameobject.h" + exclude header "cpython/interpreteridobject.h" exclude header "cpython/pthread_stubs.h" - exclude header "cpython/pyatomic_msc.h" - exclude header "cpython/pyatomic_std.h" - exclude header "cpython/pystats.h" - - exclude header "internal/mimalloc/mimalloc.h" - exclude header "internal/mimalloc/mimalloc/atomic.h" - exclude header "internal/mimalloc/mimalloc/internal.h" - exclude header "internal/mimalloc/mimalloc/prim.h" - exclude header "internal/mimalloc/mimalloc/track.h" - exclude header "internal/mimalloc/mimalloc/types.h" exclude header "internal/pycore_abstract.h" exclude header "internal/pycore_asdl.h" exclude header "internal/pycore_ast_state.h" exclude header "internal/pycore_ast.h" exclude header "internal/pycore_atexit.h" - exclude header "internal/pycore_audit.h" - exclude header "internal/pycore_backoff.h" + exclude header "internal/pycore_atomic_funcs.h" + exclude header "internal/pycore_atomic.h" exclude header "internal/pycore_bitutils.h" exclude header "internal/pycore_blocks_output_buffer.h" - exclude header "internal/pycore_brc.h" exclude header "internal/pycore_bytes_methods.h" exclude header "internal/pycore_bytesobject.h" exclude header "internal/pycore_call.h" - exclude header "internal/pycore_capsule.h" - exclude header "internal/pycore_cell.h" exclude header "internal/pycore_ceval_state.h" exclude header "internal/pycore_ceval.h" exclude header "internal/pycore_code.h" - exclude header "internal/pycore_codecs.h" exclude header "internal/pycore_compile.h" - exclude header "internal/pycore_complexobject.h" exclude header "internal/pycore_condvar.h" exclude header "internal/pycore_context.h" - exclude header "internal/pycore_critical_section.h" - exclude header "internal/pycore_crossinterp_data_registry.h" - exclude header "internal/pycore_crossinterp.h" - exclude header "internal/pycore_debug_offsets.h" exclude header "internal/pycore_descrobject.h" exclude header "internal/pycore_dict_state.h" exclude header "internal/pycore_dict.h" exclude header "internal/pycore_dtoa.h" exclude header "internal/pycore_emscripten_signal.h" - exclude header "internal/pycore_emscripten_trampoline.h" exclude header "internal/pycore_exceptions.h" exclude header "internal/pycore_faulthandler.h" exclude header "internal/pycore_fileutils_windows.h" @@ -73,8 +51,6 @@ module Python { exclude header "internal/pycore_flowgraph.h" exclude header "internal/pycore_format.h" exclude header "internal/pycore_frame.h" - exclude header "internal/pycore_freelist_state.h" - exclude header "internal/pycore_freelist.h" exclude header "internal/pycore_function.h" exclude header "internal/pycore_gc.h" exclude header "internal/pycore_genobject.h" @@ -85,42 +61,25 @@ module Python { exclude header "internal/pycore_global_strings.h" exclude header "internal/pycore_hamt.h" exclude header "internal/pycore_hashtable.h" - exclude header "internal/pycore_identifier.h" exclude header "internal/pycore_import.h" - exclude header "internal/pycore_importdl.h" - exclude header "internal/pycore_index_pool.h" exclude header "internal/pycore_initconfig.h" - exclude header "internal/pycore_instruction_sequence.h" exclude header "internal/pycore_instruments.h" exclude header "internal/pycore_interp.h" exclude header "internal/pycore_intrinsics.h" - exclude header "internal/pycore_jit.h" exclude header "internal/pycore_list.h" - exclude header "internal/pycore_llist.h" - exclude header "internal/pycore_lock.h" exclude header "internal/pycore_long.h" - exclude header "internal/pycore_magic_number.h" exclude header "internal/pycore_memoryobject.h" - exclude header "internal/pycore_mimalloc.h" - exclude header "internal/pycore_modsupport.h" exclude header "internal/pycore_moduleobject.h" exclude header "internal/pycore_namespace.h" - exclude header "internal/pycore_object_alloc.h" - exclude header "internal/pycore_object_deferred.h" - exclude header "internal/pycore_object_stack.h" exclude header "internal/pycore_object_state.h" exclude header "internal/pycore_object.h" exclude header "internal/pycore_obmalloc_init.h" exclude header "internal/pycore_obmalloc.h" - exclude header "internal/pycore_opcode_metadata.h" exclude header "internal/pycore_opcode_utils.h" - exclude header "internal/pycore_optimizer.h" - exclude header "internal/pycore_parking_lot.h" + exclude header "internal/pycore_opcode.h" exclude header "internal/pycore_parser.h" exclude header "internal/pycore_pathconfig.h" exclude header "internal/pycore_pyarena.h" - exclude header "internal/pycore_pyatomic_ft_wrappers.h" - exclude header "internal/pycore_pybuffer.h" exclude header "internal/pycore_pyerrors.h" exclude header "internal/pycore_pyhash.h" exclude header "internal/pycore_pylifecycle.h" @@ -128,19 +87,13 @@ module Python { exclude header "internal/pycore_pymem_init.h" exclude header "internal/pycore_pymem.h" exclude header "internal/pycore_pystate.h" - exclude header "internal/pycore_pystats.h" - exclude header "internal/pycore_pythonrun.h" exclude header "internal/pycore_pythread.h" - exclude header "internal/pycore_qsbr.h" exclude header "internal/pycore_range.h" exclude header "internal/pycore_runtime_init_generated.h" exclude header "internal/pycore_runtime_init.h" exclude header "internal/pycore_runtime.h" - exclude header "internal/pycore_semaphore.h" - exclude header "internal/pycore_setobject.h" exclude header "internal/pycore_signal.h" exclude header "internal/pycore_sliceobject.h" - exclude header "internal/pycore_stackref.h" exclude header "internal/pycore_strhex.h" exclude header "internal/pycore_structseq.h" exclude header "internal/pycore_symtable.h" @@ -149,7 +102,6 @@ module Python { exclude header "internal/pycore_token.h" exclude header "internal/pycore_traceback.h" exclude header "internal/pycore_tracemalloc.h" - exclude header "internal/pycore_tstate.h" exclude header "internal/pycore_tuple.h" exclude header "internal/pycore_typeobject.h" exclude header "internal/pycore_typevarobject.h" @@ -157,9 +109,5 @@ module Python { exclude header "internal/pycore_unicodeobject_generated.h" exclude header "internal/pycore_unicodeobject.h" exclude header "internal/pycore_unionobject.h" - exclude header "internal/pycore_uniqueid.h" - exclude header "internal/pycore_uop_ids.h" - exclude header "internal/pycore_uop_metadata.h" exclude header "internal/pycore_warnings.h" - exclude header "internal/pycore_weakref.h" } From 2696eb0b2f0594358fd5a1c8bc78e42bc6f3f7aa Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 14 Mar 2025 14:45:27 +0800 Subject: [PATCH 23/26] Add shim definitin of IOSVersionInfo. --- patch/Python/_cross_target.py.tmpl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/patch/Python/_cross_target.py.tmpl b/patch/Python/_cross_target.py.tmpl index 9f75af53..5f822e24 100644 --- a/patch/Python/_cross_target.py.tmpl +++ b/patch/Python/_cross_target.py.tmpl @@ -1,5 +1,6 @@ # A site package that turns a macOS virtual environment # into an {{arch}} {{sdk}} cross-platform virtual environment +import collections import platform import subprocess import sys @@ -36,6 +37,12 @@ def cross_uname(): ) +platform.IOSVersionInfo = collections.namedtuple( + "IOSVersionInfo", + ["system", "release", "model", "is_simulator"] +) + + def cross_ios_ver(system="", release="", model="", is_simulator=False): if system == "": system = "{{os}}" From 0daa79fbd00e1c0335cac987739c921f676ac753 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Fri, 14 Mar 2025 14:55:36 +0800 Subject: [PATCH 24/26] Add missing pyconfig headers to modulemap. --- patch/Python/module.modulemap | 3 +++ 1 file changed, 3 insertions(+) diff --git a/patch/Python/module.modulemap b/patch/Python/module.modulemap index 23a502bf..9b52f071 100644 --- a/patch/Python/module.modulemap +++ b/patch/Python/module.modulemap @@ -12,6 +12,9 @@ module Python { exclude header "opcode.h" exclude header "osdefs.h" exclude header "py_curses.h" + exclude header "pyconfig-arm32_64.h" + exclude header "pyconfig-arm64.h" + exclude header "pyconfig-x86_64.h" exclude header "pydtrace.h" exclude header "pyexpat.h" exclude header "structmember.h" From 0013ca0bc85a38f21ca1c4c9f6c8b1e44ae6c400 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 18 Mar 2025 15:39:26 +0800 Subject: [PATCH 25/26] Update patch for Python 3.14.0a6 (#258) * Update patch for v3.14.0a6, * Automate the contents of the modulemap file. --- .github/workflows/ci.yaml | 7 +- Makefile | 24 +++- patch/Python/Python.patch | 171 +++------------------------ patch/Python/module.modulemap | 165 -------------------------- patch/Python/module.modulemap.prefix | 20 ++++ 5 files changed, 60 insertions(+), 327 deletions(-) delete mode 100644 patch/Python/module.modulemap create mode 100644 patch/Python/module.modulemap.prefix diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ef7ad512..6befbc46 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,10 +1,6 @@ name: CI on: pull_request: - push: - branches: - - main - - 3.* workflow_call: inputs: build-number: @@ -114,6 +110,9 @@ jobs: # Appending -dev ensures that we can always build the dev release. # It's a no-op for versions that have been published. python-version: ${{ needs.config.outputs.PYTHON_VER }}-dev + # Ensure that we *always* use the latest build, not a cached version. + # It's an edge case, but when a new alpha is released, we need to use it ASAP. + check-latest: true - name: Build ${{ matrix.target }} run: | diff --git a/Makefile b/Makefile index b0ba7299..7d3f6bfa 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ BUILD_NUMBER=custom # of a release cycle, as official binaries won't be published. # PYTHON_MICRO_VERSION is the full version number, without any alpha/beta/rc suffix. (e.g., 3.10.0) # PYTHON_VER is the major/minor version (e.g., 3.10) -PYTHON_VERSION=3.14.0a5 +PYTHON_VERSION=3.14.0a6 PYTHON_PKG_VERSION=$(PYTHON_VERSION) PYTHON_MICRO_VERSION=$(shell echo $(PYTHON_VERSION) | grep -Eo "\d+\.\d+\.\d+") PYTHON_PKG_MICRO_VERSION=$(shell echo $(PYTHON_PKG_VERSION) | grep -Eo "\d+\.\d+\.\d+") @@ -426,6 +426,7 @@ PYTHON_FRAMEWORK-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/Python.framework PYTHON_INSTALL_VERSION-$(sdk)=$$(PYTHON_FRAMEWORK-$(sdk))/Versions/$(PYTHON_VER) PYTHON_LIB-$(sdk)=$$(PYTHON_INSTALL_VERSION-$(sdk))/Python PYTHON_INCLUDE-$(sdk)=$$(PYTHON_INSTALL_VERSION-$(sdk))/include/python$(PYTHON_VER) +PYTHON_MODULEMAP-$(sdk)=$$(PYTHON_INCLUDE-$(sdk))/module.modulemap PYTHON_STDLIB-$(sdk)=$$(PYTHON_INSTALL_VERSION-$(sdk))/lib/python$(PYTHON_VER) else @@ -436,6 +437,7 @@ else # The non-macOS frameworks don't use the versioning structure. PYTHON_INSTALL-$(sdk)=$(PROJECT_DIR)/install/$(os)/$(sdk)/python-$(PYTHON_VERSION) +PYTHON_MODULEMAP-$(sdk)=$$(PYTHON_INCLUDE-$(sdk))/module.modulemap PYTHON_FRAMEWORK-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/Python.framework PYTHON_LIB-$(sdk)=$$(PYTHON_FRAMEWORK-$(sdk))/Python PYTHON_BIN-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/bin @@ -466,8 +468,14 @@ $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h: $$(PYTHON_LIB-$(sdk)) # Copy headers as-is from the first target in the $(sdk) SDK cp -r $$(PYTHON_INCLUDE-$$(firstword $$(SDK_TARGETS-$(sdk)))) $$(PYTHON_INCLUDE-$(sdk)) - # Copy in the modulemap file - cp -r patch/Python/module.modulemap $$(PYTHON_INCLUDE-$(sdk)) + # Create the modulemap file + cp -r patch/Python/module.modulemap.prefix $$(PYTHON_MODULEMAP-$(sdk)) + echo "" >> $$(PYTHON_MODULEMAP-$(sdk)) + cd $$(PYTHON_SRCDIR-$$(firstword $$(SDK_TARGETS-$(sdk))))/Include && \ + find cpython -name "*.h" | sort | sed -e 's/^/ exclude header "/' | sed 's/$$$$/"/' >> $$(PYTHON_MODULEMAP-$(sdk)) && \ + echo "" >> $$(PYTHON_MODULEMAP-$(sdk)) && \ + find internal -name "*.h" | sort | sed -e 's/^/ exclude header "/' | sed 's/$$$$/"/' >> $$(PYTHON_MODULEMAP-$(sdk)) + echo "\n}" >> $$(PYTHON_MODULEMAP-$(sdk)) # Link the PYTHONHOME version of the headers mkdir -p $$(PYTHON_INSTALL-$(sdk))/include @@ -585,8 +593,14 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ # Rewrite the framework to make it standalone patch/make-relocatable.sh $$(PYTHON_INSTALL_VERSION-macosx) 2>&1 > /dev/null - # Copy in the modulemap file - cp -r patch/Python/module.modulemap $$(PYTHON_FRAMEWORK-macosx)/Headers + # Create the modulemap file + cp -r patch/Python/module.modulemap.prefix $$(PYTHON_MODULEMAP-macosx) + echo "" >> $$(PYTHON_MODULEMAP-macosx) + cd $$(PYTHON_INCLUDE-macosx) && \ + find cpython -name "*.h" | sort | sed -e 's/^/ exclude header "/' | sed 's/$$$$/"/' >> $$(PYTHON_MODULEMAP-macosx) && \ + echo "" >> $$(PYTHON_MODULEMAP-macosx) && \ + find internal -name "*.h" | sort | sed -e 's/^/ exclude header "/' | sed 's/$$$$/"/' >> $$(PYTHON_MODULEMAP-macosx) + echo "\n}" >> $$(PYTHON_MODULEMAP-macosx) # Re-apply the signature on the binaries. codesign -s - --preserve-metadata=identifier,entitlements,flags,runtime -f $$(PYTHON_LIB-macosx) \ diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 139da995..c9c0ef6f 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -113,7 +113,7 @@ index 1f6baed66d3..235dd98c60a 100644 macos_release = mac_ver()[0] if macos_release: diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py -index 69f72452c40..34ce643340b 100644 +index 18e6b8d25e5..4994c56778c 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -719,6 +719,14 @@ @@ -163,7 +163,7 @@ index ec0857a4a99..2350e9dc821 100644 # elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX PLATFORM_TRIPLET=darwin diff --git a/configure b/configure -index d46bc563a67..d5cd81d16a8 100755 +index d0ae103014a..308124ef06d 100755 --- a/configure +++ b/configure @@ -974,6 +974,8 @@ @@ -588,7 +588,7 @@ index d46bc563a67..d5cd81d16a8 100755 fi ac_fn_c_check_func "$LINENO" "pread" "ac_cv_func_pread" if test "x$ac_cv_func_pread" = xyes -@@ -19860,12 +19973,6 @@ +@@ -19866,12 +19979,6 @@ then : printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h @@ -601,7 +601,7 @@ index d46bc563a67..d5cd81d16a8 100755 fi ac_fn_c_check_func "$LINENO" "sigfillset" "ac_cv_func_sigfillset" if test "x$ac_cv_func_sigfillset" = xyes -@@ -20134,11 +20241,11 @@ +@@ -20140,11 +20247,11 @@ fi @@ -615,7 +615,7 @@ index d46bc563a67..d5cd81d16a8 100755 ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" if test "x$ac_cv_func_getentropy" = xyes then : -@@ -20160,6 +20267,53 @@ +@@ -20166,6 +20273,53 @@ fi @@ -669,7 +669,7 @@ index d46bc563a67..d5cd81d16a8 100755 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5 printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; } if test ${ac_cv_c_undeclared_builtin_options+y} -@@ -23242,7 +23396,8 @@ +@@ -23248,7 +23402,8 @@ # check for openpty, login_tty, and forkpty @@ -679,7 +679,7 @@ index d46bc563a67..d5cd81d16a8 100755 for ac_func in openpty do : -@@ -23356,7 +23511,7 @@ +@@ -23362,7 +23517,7 @@ fi done @@ -688,7 +688,7 @@ index d46bc563a67..d5cd81d16a8 100755 printf %s "checking for library containing login_tty... " >&6; } if test ${ac_cv_search_login_tty+y} then : -@@ -23539,6 +23694,7 @@ +@@ -23545,6 +23700,7 @@ fi done @@ -696,7 +696,7 @@ index d46bc563a67..d5cd81d16a8 100755 # check for long file support functions ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" -@@ -23804,10 +23960,10 @@ +@@ -23810,10 +23966,10 @@ done @@ -709,7 +709,7 @@ index d46bc563a67..d5cd81d16a8 100755 then for ac_func in clock_settime -@@ -26146,8 +26302,8 @@ +@@ -26152,8 +26308,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -720,7 +720,7 @@ index d46bc563a67..d5cd81d16a8 100755 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -29017,7 +29173,7 @@ +@@ -29023,7 +29179,7 @@ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for device files" >&5 printf "%s\n" "$as_me: checking for device files" >&6;} @@ -729,7 +729,7 @@ index d46bc563a67..d5cd81d16a8 100755 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -29510,7 +29666,7 @@ +@@ -29504,7 +29660,7 @@ with_ensurepip=no ;; #( WASI) : with_ensurepip=no ;; #( @@ -738,7 +738,7 @@ index d46bc563a67..d5cd81d16a8 100755 with_ensurepip=no ;; #( *) : with_ensurepip=upgrade -@@ -30490,7 +30646,7 @@ +@@ -30484,7 +30640,7 @@ ;; #( Darwin) : ;; #( @@ -747,7 +747,7 @@ index d46bc563a67..d5cd81d16a8 100755 -@@ -34493,6 +34649,8 @@ +@@ -34487,6 +34643,8 @@ "Mac/Resources/framework/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/framework/Info.plist" ;; "Mac/Resources/app/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/app/Info.plist" ;; "iOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES iOS/Resources/Info.plist" ;; @@ -757,7 +757,7 @@ index d46bc563a67..d5cd81d16a8 100755 "Misc/python.pc") CONFIG_FILES="$CONFIG_FILES Misc/python.pc" ;; "Misc/python-embed.pc") CONFIG_FILES="$CONFIG_FILES Misc/python-embed.pc" ;; diff --git a/configure.ac b/configure.ac -index faa89095303..9bd51f7da97 100644 +index 8bb0f1c6ef4..bfd67de48bb 100644 --- a/configure.ac +++ b/configure.ac @@ -330,6 +330,12 @@ @@ -1128,7 +1128,7 @@ index faa89095303..9bd51f7da97 100644 + pipe2 plock poll posix_fadvise posix_fallocate posix_openpt \ pread preadv preadv2 process_vm_readv \ pthread_cond_timedwait_relative_np pthread_condattr_setclock pthread_init \ - pthread_kill pthread_getname_np pthread_setname_np \ + pthread_kill pthread_getname_np pthread_setname_np pthread_getattr_np \ @@ -5153,7 +5283,7 @@ sched_setparam sched_setscheduler sem_clockwait sem_getvalue sem_open \ sem_timedwait sem_unlink sendfile setegid seteuid setgid sethostname \ @@ -1232,7 +1232,7 @@ index faa89095303..9bd51f7da97 100644 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -7187,7 +7327,7 @@ +@@ -7174,7 +7314,7 @@ AS_CASE([$ac_sys_system], [Emscripten], [with_ensurepip=no], [WASI], [with_ensurepip=no], @@ -1241,7 +1241,7 @@ index faa89095303..9bd51f7da97 100644 [with_ensurepip=upgrade] ) ]) -@@ -7598,7 +7738,7 @@ +@@ -7585,7 +7725,7 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], @@ -1270,141 +1270,6 @@ index c3e261ecd9e..26ef7a95de4 100644 CFBundleSupportedPlatforms iPhoneOS -diff --git a/iOS/testbed/__main__.py b/iOS/testbed/__main__.py -index b4499f5ac17..d12a5ab065b 100644 ---- a/iOS/testbed/__main__.py -+++ b/iOS/testbed/__main__.py -@@ -82,19 +82,29 @@ - - # Return a list of UDIDs associated with booted simulators - async def list_devices(): -- # List the testing simulators, in JSON format -- raw_json = await async_check_output( -- "xcrun", "simctl", "--set", "testing", "list", "-j" -- ) -- json_data = json.loads(raw_json) -- -- # Filter out the booted iOS simulators -- return [ -- simulator["udid"] -- for runtime, simulators in json_data["devices"].items() -- for simulator in simulators -- if runtime.split(".")[-1].startswith("iOS") and simulator["state"] == "Booted" -- ] -+ try: -+ # List the testing simulators, in JSON format -+ raw_json = await async_check_output( -+ "xcrun", "simctl", "--set", "testing", "list", "-j" -+ ) -+ json_data = json.loads(raw_json) -+ -+ # Filter out the booted iOS simulators -+ return [ -+ simulator["udid"] -+ for runtime, simulators in json_data["devices"].items() -+ for simulator in simulators -+ if runtime.split(".")[-1].startswith("iOS") and simulator["state"] == "Booted" -+ ] -+ except subprocess.CalledProcessError as e: -+ # If there's no ~/Library/Developer/XCTestDevices folder (which is the -+ # case on fresh installs, and in some CI environments), `simctl list` -+ # returns error code 1, rather than an empty list. Handle that case, -+ # but raise all other errors. -+ if e.returncode == 1: -+ return [] -+ else: -+ raise - - - async def find_device(initial_devices): -@@ -230,33 +240,69 @@ - shutil.copytree(source, target, symlinks=True) - print(" done") - -+ xc_framework_path = target / "Python.xcframework" -+ sim_framework_path = xc_framework_path / "ios-arm64_x86_64-simulator" - if framework is not None: - if framework.suffix == ".xcframework": - print(" Installing XCFramework...", end="", flush=True) -- xc_framework_path = (target / "Python.xcframework").resolve() - if xc_framework_path.is_dir(): - shutil.rmtree(xc_framework_path) - else: -- xc_framework_path.unlink() -+ xc_framework_path.unlink(missing_ok=True) - xc_framework_path.symlink_to( - framework.relative_to(xc_framework_path.parent, walk_up=True) - ) - print(" done") - else: - print(" Installing simulator framework...", end="", flush=True) -- sim_framework_path = ( -- target / "Python.xcframework" / "ios-arm64_x86_64-simulator" -- ).resolve() - if sim_framework_path.is_dir(): - shutil.rmtree(sim_framework_path) - else: -- sim_framework_path.unlink() -+ sim_framework_path.unlink(missing_ok=True) - sim_framework_path.symlink_to( - framework.relative_to(sim_framework_path.parent, walk_up=True) - ) - print(" done") - else: -- print(" Using pre-existing iOS framework.") -+ if ( -+ xc_framework_path.is_symlink() -+ and not xc_framework_path.readlink().is_absolute() -+ ): -+ # XCFramework is a relative symlink. Rewrite the symlink relative -+ # to the new location. -+ print(" Rewriting symlink to XCframework...", end="", flush=True) -+ orig_xc_framework_path = ( -+ source -+ / xc_framework_path.readlink() -+ ).resolve() -+ xc_framework_path.unlink() -+ xc_framework_path.symlink_to( -+ orig_xc_framework_path.relative_to( -+ xc_framework_path.parent, walk_up=True -+ ) -+ ) -+ print(" done") -+ elif ( -+ sim_framework_path.is_symlink() -+ and not sim_framework_path.readlink().is_absolute() -+ ): -+ print(" Rewriting symlink to simulator framework...", end="", flush=True) -+ # Simulator framework is a relative symlink. Rewrite the symlink -+ # relative to the new location. -+ orig_sim_framework_path = ( -+ source -+ / "Python.XCframework" -+ / sim_framework_path.readlink() -+ ).resolve() -+ sim_framework_path.unlink() -+ sim_framework_path.symlink_to( -+ orig_sim_framework_path.relative_to( -+ sim_framework_path.parent, walk_up=True -+ ) -+ ) -+ print(" done") -+ else: -+ print(" Using pre-existing iOS framework.") - - for app_src in apps: - print(f" Installing app {app_src.name!r}...", end="", flush=True) -@@ -372,8 +418,8 @@ - - if context.subcommand == "clone": - clone_testbed( -- source=Path(__file__).parent, -- target=Path(context.location), -+ source=Path(__file__).parent.resolve(), -+ target=Path(context.location).resolve(), - framework=Path(context.framework).resolve() if context.framework else None, - apps=[Path(app) for app in context.apps], - ) --- /dev/null +++ b/tvOS/README.rst @@ -0,0 +1,108 @@ diff --git a/patch/Python/module.modulemap b/patch/Python/module.modulemap deleted file mode 100644 index 96b05fc6..00000000 --- a/patch/Python/module.modulemap +++ /dev/null @@ -1,165 +0,0 @@ -module Python { - umbrella header "Python.h" - export * - link "Python" - - exclude header "datetime.h" - exclude header "dynamic_annotations.h" - exclude header "errcode.h" - exclude header "frameobject.h" - exclude header "marshal.h" - exclude header "opcode_ids.h" - exclude header "opcode.h" - exclude header "osdefs.h" - exclude header "py_curses.h" - exclude header "pyconfig-arm32_64.h" - exclude header "pyconfig-arm64.h" - exclude header "pyconfig-x86_64.h" - exclude header "pydtrace.h" - exclude header "pyexpat.h" - exclude header "structmember.h" - - exclude header "cpython/frameobject.h" - exclude header "cpython/pthread_stubs.h" - exclude header "cpython/pyatomic_msc.h" - exclude header "cpython/pyatomic_std.h" - exclude header "cpython/pystats.h" - - exclude header "internal/mimalloc/mimalloc.h" - exclude header "internal/mimalloc/mimalloc/atomic.h" - exclude header "internal/mimalloc/mimalloc/internal.h" - exclude header "internal/mimalloc/mimalloc/prim.h" - exclude header "internal/mimalloc/mimalloc/track.h" - exclude header "internal/mimalloc/mimalloc/types.h" - - exclude header "internal/pycore_abstract.h" - exclude header "internal/pycore_asdl.h" - exclude header "internal/pycore_ast_state.h" - exclude header "internal/pycore_ast.h" - exclude header "internal/pycore_atexit.h" - exclude header "internal/pycore_audit.h" - exclude header "internal/pycore_backoff.h" - exclude header "internal/pycore_bitutils.h" - exclude header "internal/pycore_blocks_output_buffer.h" - exclude header "internal/pycore_brc.h" - exclude header "internal/pycore_bytes_methods.h" - exclude header "internal/pycore_bytesobject.h" - exclude header "internal/pycore_call.h" - exclude header "internal/pycore_capsule.h" - exclude header "internal/pycore_cell.h" - exclude header "internal/pycore_ceval_state.h" - exclude header "internal/pycore_ceval.h" - exclude header "internal/pycore_code.h" - exclude header "internal/pycore_codecs.h" - exclude header "internal/pycore_compile.h" - exclude header "internal/pycore_complexobject.h" - exclude header "internal/pycore_condvar.h" - exclude header "internal/pycore_context.h" - exclude header "internal/pycore_critical_section.h" - exclude header "internal/pycore_crossinterp_data_registry.h" - exclude header "internal/pycore_crossinterp.h" - exclude header "internal/pycore_debug_offsets.h" - exclude header "internal/pycore_descrobject.h" - exclude header "internal/pycore_dict_state.h" - exclude header "internal/pycore_dict.h" - exclude header "internal/pycore_dtoa.h" - exclude header "internal/pycore_emscripten_signal.h" - exclude header "internal/pycore_emscripten_trampoline.h" - exclude header "internal/pycore_exceptions.h" - exclude header "internal/pycore_faulthandler.h" - exclude header "internal/pycore_fileutils_windows.h" - exclude header "internal/pycore_fileutils.h" - exclude header "internal/pycore_floatobject.h" - exclude header "internal/pycore_flowgraph.h" - exclude header "internal/pycore_format.h" - exclude header "internal/pycore_frame.h" - exclude header "internal/pycore_freelist_state.h" - exclude header "internal/pycore_freelist.h" - exclude header "internal/pycore_function.h" - exclude header "internal/pycore_gc.h" - exclude header "internal/pycore_genobject.h" - exclude header "internal/pycore_getopt.h" - exclude header "internal/pycore_gil.h" - exclude header "internal/pycore_global_objects_fini_generated.h" - exclude header "internal/pycore_global_objects.h" - exclude header "internal/pycore_global_strings.h" - exclude header "internal/pycore_hamt.h" - exclude header "internal/pycore_hashtable.h" - exclude header "internal/pycore_identifier.h" - exclude header "internal/pycore_import.h" - exclude header "internal/pycore_importdl.h" - exclude header "internal/pycore_index_pool.h" - exclude header "internal/pycore_initconfig.h" - exclude header "internal/pycore_instruction_sequence.h" - exclude header "internal/pycore_instruments.h" - exclude header "internal/pycore_interp.h" - exclude header "internal/pycore_intrinsics.h" - exclude header "internal/pycore_jit.h" - exclude header "internal/pycore_list.h" - exclude header "internal/pycore_llist.h" - exclude header "internal/pycore_lock.h" - exclude header "internal/pycore_long.h" - exclude header "internal/pycore_magic_number.h" - exclude header "internal/pycore_memoryobject.h" - exclude header "internal/pycore_mimalloc.h" - exclude header "internal/pycore_modsupport.h" - exclude header "internal/pycore_moduleobject.h" - exclude header "internal/pycore_namespace.h" - exclude header "internal/pycore_object_alloc.h" - exclude header "internal/pycore_object_deferred.h" - exclude header "internal/pycore_object_stack.h" - exclude header "internal/pycore_object_state.h" - exclude header "internal/pycore_object.h" - exclude header "internal/pycore_obmalloc_init.h" - exclude header "internal/pycore_obmalloc.h" - exclude header "internal/pycore_opcode_metadata.h" - exclude header "internal/pycore_opcode_utils.h" - exclude header "internal/pycore_optimizer.h" - exclude header "internal/pycore_parking_lot.h" - exclude header "internal/pycore_parser.h" - exclude header "internal/pycore_pathconfig.h" - exclude header "internal/pycore_pyarena.h" - exclude header "internal/pycore_pyatomic_ft_wrappers.h" - exclude header "internal/pycore_pybuffer.h" - exclude header "internal/pycore_pyerrors.h" - exclude header "internal/pycore_pyhash.h" - exclude header "internal/pycore_pylifecycle.h" - exclude header "internal/pycore_pymath.h" - exclude header "internal/pycore_pymem_init.h" - exclude header "internal/pycore_pymem.h" - exclude header "internal/pycore_pystate.h" - exclude header "internal/pycore_pystats.h" - exclude header "internal/pycore_pythonrun.h" - exclude header "internal/pycore_pythread.h" - exclude header "internal/pycore_qsbr.h" - exclude header "internal/pycore_range.h" - exclude header "internal/pycore_runtime_init_generated.h" - exclude header "internal/pycore_runtime_init.h" - exclude header "internal/pycore_runtime.h" - exclude header "internal/pycore_semaphore.h" - exclude header "internal/pycore_setobject.h" - exclude header "internal/pycore_signal.h" - exclude header "internal/pycore_sliceobject.h" - exclude header "internal/pycore_stackref.h" - exclude header "internal/pycore_strhex.h" - exclude header "internal/pycore_structseq.h" - exclude header "internal/pycore_symtable.h" - exclude header "internal/pycore_sysmodule.h" - exclude header "internal/pycore_time.h" - exclude header "internal/pycore_token.h" - exclude header "internal/pycore_traceback.h" - exclude header "internal/pycore_tracemalloc.h" - exclude header "internal/pycore_tstate.h" - exclude header "internal/pycore_tuple.h" - exclude header "internal/pycore_typeobject.h" - exclude header "internal/pycore_typevarobject.h" - exclude header "internal/pycore_ucnhash.h" - exclude header "internal/pycore_unicodeobject_generated.h" - exclude header "internal/pycore_unicodeobject.h" - exclude header "internal/pycore_unionobject.h" - exclude header "internal/pycore_uniqueid.h" - exclude header "internal/pycore_uop_ids.h" - exclude header "internal/pycore_uop_metadata.h" - exclude header "internal/pycore_warnings.h" - exclude header "internal/pycore_weakref.h" -} diff --git a/patch/Python/module.modulemap.prefix b/patch/Python/module.modulemap.prefix new file mode 100644 index 00000000..e3b3aafb --- /dev/null +++ b/patch/Python/module.modulemap.prefix @@ -0,0 +1,20 @@ +module Python { + umbrella header "Python.h" + export * + link "Python" + + exclude header "datetime.h" + exclude header "dynamic_annotations.h" + exclude header "errcode.h" + exclude header "frameobject.h" + exclude header "marshal.h" + exclude header "opcode_ids.h" + exclude header "opcode.h" + exclude header "osdefs.h" + exclude header "py_curses.h" + exclude header "pyconfig-arm32_64.h" + exclude header "pyconfig-arm64.h" + exclude header "pyconfig-x86_64.h" + exclude header "pydtrace.h" + exclude header "pyexpat.h" + exclude header "structmember.h" From 8d93cde378ed110d403ab034d038afd9bb7a4769 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 18 Mar 2025 16:15:05 +0800 Subject: [PATCH 26/26] Add a missing entry in the modulemap prefix. --- patch/Python/module.modulemap.prefix | 1 + 1 file changed, 1 insertion(+) diff --git a/patch/Python/module.modulemap.prefix b/patch/Python/module.modulemap.prefix index e3b3aafb..4f198f12 100644 --- a/patch/Python/module.modulemap.prefix +++ b/patch/Python/module.modulemap.prefix @@ -7,6 +7,7 @@ module Python { exclude header "dynamic_annotations.h" exclude header "errcode.h" exclude header "frameobject.h" + exclude header "interpreteridobject.h" exclude header "marshal.h" exclude header "opcode_ids.h" exclude header "opcode.h"