diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 717febe..6cbfb9d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -89,7 +89,7 @@ jobs: strategy: fail-fast: false matrix: - target: ['macOS', 'iOS', 'tvOS', 'watchOS', 'visionOS'] + target: ['macOS', 'iOS', 'tvOS', 'watchOS', 'visionOS', 'MacCatalyst'] steps: - uses: actions/checkout@v4.1.7 @@ -171,7 +171,12 @@ jobs: strategy: fail-fast: false matrix: - target: ["iOS", "visionOS"] + target: ["iOS", "visionOS", "MacCatalyst"] + include: + - testbed-pre-args: + + - target: MacCatalyst + testbed-pre-args: '--catalyst' steps: - uses: actions/checkout@v4.1.7 @@ -209,4 +214,4 @@ jobs: # - test_os as a test of system library calls # - test_bz2 as a simple test of third party libraries # - test_ctypes as a test of FFI - python -m testbed run -- test --single-process --rerun -W test_builtin test_grammar test_os test_bz2 test_ctypes + python -m testbed ${{ matrix.testbed-pre-args }} run -- test --single-process --rerun -W test_builtin test_grammar test_os test_bz2 test_ctypes diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 587abe8..ec42173 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -51,3 +51,6 @@ jobs: # visionOS build curl -o visionOS-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.outputs.TAG }}/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-visionOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz aws s3 cp visionOS-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.outputs.PYTHON_VER }}/visionOS/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-visionOS-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + # MacCatalyst build + curl -o MacCatalyst-artefact.tar.gz -L https://github.com/beeware/Python-Apple-support/releases/download/${{ steps.build-vars.outputs.TAG }}/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-MacCatalyst-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz + aws s3 cp MacCatalyst-artefact.tar.gz s3://briefcase-support/python/${{ steps.build-vars.outputs.PYTHON_VER }}/MacCatalyst/Python-${{ steps.build-vars.outputs.PYTHON_VER }}-MacCatalyst-support.${{ steps.build-vars.outputs.BUILD_NUMBER }}.tar.gz diff --git a/Makefile b/Makefile index ab0b356..d74df6c 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ OPENSSL_VERSION=3.0.16-2 XZ_VERSION=5.6.4-2 # Supported OS -OS_LIST=macOS iOS tvOS watchOS visionOS +OS_LIST=macOS iOS tvOS watchOS visionOS MacCatalyst CURL_FLAGS=--disable --fail --location --create-dirs --progress-bar @@ -42,25 +42,37 @@ CURL_FLAGS=--disable --fail --location --create-dirs --progress-bar TARGETS-macOS=macosx.x86_64 macosx.arm64 TRIPLE_OS-macOS=macos VERSION_MIN-macOS=11.0 +CONFIGFLAGS-macOS= # iOS targets TARGETS-iOS=iphonesimulator.x86_64 iphonesimulator.arm64 iphoneos.arm64 TRIPLE_OS-iOS=ios VERSION_MIN-iOS=13.0 +CONFIGFLAGS-iOS= # tvOS targets TARGETS-tvOS=appletvsimulator.x86_64 appletvsimulator.arm64 appletvos.arm64 TRIPLE_OS-tvOS=tvos VERSION_MIN-tvOS=12.0 +CONFIGFLAGS-tvOS= # watchOS targets TARGETS-watchOS=watchsimulator.x86_64 watchsimulator.arm64 watchos.arm64_32 TRIPLE_OS-watchOS=watchos VERSION_MIN-watchOS=4.0 +CONFIGFLAGS-watchOS= +# visionOS targets TARGETS-visionOS=xrsimulator.arm64 xros.arm64 TRIPLE_OS-visionOS=xros VERSION_MIN-visionOS=2.0 +CONFIGFLAGS-visionOS= + +# Mac Catalyst Targets +TARGETS-MacCatalyst=macabi.x86_64 macabi.arm64 +TRIPLE_OS-MacCatalyst=ios +VERSION_MIN-MacCatalyst=14.2 +CONFIGFLAGS-MacCatalyst=--with-catalyst-macos-version=11.2 # The architecture of the machine doing the build HOST_ARCH=$(shell uname -m) @@ -132,13 +144,19 @@ os=$2 OS_LOWER-$(target)=$(shell echo $(os) | tr '[:upper:]' '[:lower:]') # $(target) can be broken up into is composed of $(SDK).$(ARCH) -SDK-$(target)=$$(basename $(target)) +BASE-$(target)=$$(basename $(target)) +SDK-$(target)=$$(subst macabi,macosx,$$(BASE-$(target))) ARCH-$(target)=$$(subst .,,$$(suffix $(target))) ifneq ($(os),macOS) - ifeq ($$(findstring simulator,$$(SDK-$(target))),) + ifeq ($$(findstring simulator,$$(BASE-$(target))),) + ifeq ($$(findstring macabi,$$(BASE-$(target))),) TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(TRIPLE_OS-$(os))$$(VERSION_MIN-$(os)) IS_SIMULATOR-$(target)=False + else +TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(TRIPLE_OS-$(os))$$(VERSION_MIN-$(os))-macabi +IS_SIMULATOR-$(target)=False + endif else TARGET_TRIPLE-$(target)=$$(ARCH-$(target))-apple-$$(TRIPLE_OS-$(os))$$(VERSION_MIN-$(os))-simulator IS_SIMULATOR-$(target)=True @@ -266,10 +284,17 @@ ifneq ($(os),macOS) PYTHON_SRCDIR-$(target)=build/$(os)/$(target)/python-$(PYTHON_VERSION) PYTHON_INSTALL-$(target)=$(PROJECT_DIR)/install/$(os)/$(target)/python-$(PYTHON_VERSION) PYTHON_FRAMEWORK-$(target)=$$(PYTHON_INSTALL-$(target))/Python.framework +ifneq ($$(BASE-$(target)),macabi) 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) +else +PYTHON_LIB-$(target)=$$(PYTHON_FRAMEWORK-$(target))/Versions/$(PYTHON_VER)/Python +PYTHON_BIN-$(target)=$$(PYTHON_FRAMEWORK-$(target))/Versions/$(PYTHON_VER)/bin +PYTHON_INCLUDE-$(target)=$$(PYTHON_FRAMEWORK-$(target))/Versions/$(PYTHON_VER)/Headers +PYTHON_STDLIB-$(target)=$$(PYTHON_FRAMEWORK-$(target))/Versions/$(PYTHON_VER)/lib/python$(PYTHON_VER) +endif PYTHON_PLATFORM_CONFIG-$(target)=$$(PYTHON_INSTALL-$(target))/platform-config/$$(ARCH-$(target))-$$(SDK-$(target)) PYTHON_PLATFORM_SITECUSTOMIZE-$(target)=$$(PYTHON_PLATFORM_CONFIG-$(target))/sitecustomize.py @@ -312,10 +337,12 @@ $$(PYTHON_SRCDIR-$(target))/Makefile: \ --with-openssl="$$(OPENSSL_INSTALL-$(target))" \ --enable-framework="$$(PYTHON_INSTALL-$(target))" \ --with-system-libmpdec \ + $$(CONFIGFLAGS-$(os)) \ 2>&1 | tee -a ../python-$(PYTHON_VERSION).config.log $$(PYTHON_SRCDIR-$(target))/python.exe: $$(PYTHON_SRCDIR-$(target))/Makefile @echo ">>> Build Python for $(target)" + cd $$(PYTHON_SRCDIR-$(target)) && \ PATH="$(PROJECT_DIR)/$$(PYTHON_SRCDIR-$(target))/$(os)/Resources/bin:$(PATH)" \ make -j8 all \ @@ -367,6 +394,7 @@ $(target): $$(PYTHON_PLATFORM_SITECUSTOMIZE-$(target)) $$(PYTHON_LIB-$(target)) vars-$(target): @echo ">>> Environment variables for $(target)" + @echo "BASE-$(target): $$(BASE-$(target))" @echo "SDK-$(target): $$(SDK-$(target))" @echo "ARCH-$(target): $$(ARCH-$(target))" @echo "TARGET_TRIPLE-$(target): $$(TARGET_TRIPLE-$(target))" @@ -411,8 +439,12 @@ SDK_TARGETS-$(sdk)=$$(filter $(sdk).%,$$(TARGETS-$(os))) SDK_ARCHES-$(sdk)=$$(sort $$(subst .,,$$(suffix $$(SDK_TARGETS-$(sdk))))) ifeq ($$(findstring simulator,$(sdk)),) +ifeq ($$(findstring macabi,$(sdk)),) SDK_SLICE-$(sdk)=$$(TRIPLE_OS-$(os))-$$(shell echo $$(SDK_ARCHES-$(sdk)) | sed "s/ /_/g") else +SDK_SLICE-$(sdk)=$$(TRIPLE_OS-$(os))-$$(shell echo $$(SDK_ARCHES-$(sdk)) | sed "s/ /_/g")-maccatalyst +endif +else SDK_SLICE-$(sdk)=$$(TRIPLE_OS-$(os))-$$(shell echo $$(SDK_ARCHES-$(sdk)) | sed "s/ /_/g")-simulator endif @@ -446,24 +478,45 @@ else 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 +ifneq ($(sdk),macabi) 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) +else +PYTHON_LIB-$(sdk)=$$(PYTHON_FRAMEWORK-$(sdk))/Versions/$(PYTHON_VER)/Python +PYTHON_BIN-$(sdk)=$$(PYTHON_FRAMEWORK-$(sdk))/Versions/$(PYTHON_VER)/bin +PYTHON_INCLUDE-$(sdk)=$$(PYTHON_FRAMEWORK-$(sdk))/Versions/$(PYTHON_VER)/Headers +PYTHON_STDLIB-$(sdk)=$$(PYTHON_FRAMEWORK-$(sdk))/Versions/$(PYTHON_VER)/lib/python$(PYTHON_VER) +endif PYTHON_PLATFORM_CONFIG-$(sdk)=$$(PYTHON_INSTALL-$(sdk))/platform-config $$(PYTHON_LIB-$(sdk)): $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_LIB-$$(target))) @echo ">>> Build Python fat library for the $(sdk) SDK" mkdir -p $$(dir $$(PYTHON_LIB-$(sdk))) +ifeq ($(sdk),macabi) + ln -si $(PYTHON_VER) $$(PYTHON_FRAMEWORK-$(sdk))/Versions/Current + ln -si Versions/Current/Headers $$(PYTHON_FRAMEWORK-$(sdk))/Headers + ln -si Versions/Current/Resources $$(PYTHON_FRAMEWORK-$(sdk))/Resources + ln -si Versions/Current/Python $$(PYTHON_FRAMEWORK-$(sdk))/Python +endif lipo -create -output $$@ $$^ \ 2>&1 | tee -a install/$(os)/$(sdk)/python-$(PYTHON_VERSION).lipo.log # Disable dSYM production (for now) # dsymutil $$@ -o $$(PYTHON_INSTALL-$(sdk))/Python.dSYM +ifneq ($(sdk),macabi) $$(PYTHON_FRAMEWORK-$(sdk))/Info.plist: $$(PYTHON_LIB-$(sdk)) @echo ">>> Install Info.plist for the $(sdk) SDK" # Copy Info.plist as-is from the first target in the $(sdk) SDK cp -r $$(PYTHON_FRAMEWORK-$$(firstword $$(SDK_TARGETS-$(sdk))))/Info.plist $$(PYTHON_FRAMEWORK-$(sdk)) +else +$$(PYTHON_FRAMEWORK-$(sdk))/Versions/$(PYTHON_VER)/Resources: + echo ">>> Copying Resources Folder for Versioned Framework from the first target in the SDK" + mkdir -p $$(PYTHON_FRAMEWORK-$(sdk))/Versions/$(PYTHON_VER) + # Copy Resources as-is from the first target in the $(sdk) SDK + cp -r $$(PYTHON_FRAMEWORK-$$(firstword $$(SDK_TARGETS-$(sdk))))/Versions/$(PYTHON_VER)/Resources $$(PYTHON_FRAMEWORK-$(sdk))/Versions/$(PYTHON_VER) +endif $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h: $$(PYTHON_LIB-$(sdk)) @echo ">>> Build Python fat headers for the $(sdk) SDK" @@ -487,11 +540,20 @@ $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h: $$(PYTHON_LIB-$(sdk)) echo "\n}" >> $$(PYTHON_MODULEMAP-$(sdk)) # Link the PYTHONHOME version of the headers +ifneq ($(sdk),macabi) mkdir -p $$(PYTHON_INSTALL-$(sdk))/include ln -si ../Python.framework/Headers $$(PYTHON_INSTALL-$(sdk))/include/python$(PYTHON_VER) +else + mkdir -p $$(PYTHON_FRAMEWORK-$(sdk))/Versions/$(PYTHON_VER)/include + rm -rf $(PYTHON_FRAMEWORK-$(sdk))/Versions/$(PYTHON_VER)/include/* + ln -si ../Headers $$(PYTHON_FRAMEWORK-$(sdk))/Versions/$(PYTHON_VER)/include/python$(PYTHON_VER) +endif ifeq ($(os), visionOS) echo "Skipping arch-specific header copying for visionOS" + + # Add the headers from each target -- there's should only be one target, so we copy it to the same name. + $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_INCLUDE-$$(target))/pyconfig.h $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h; ) else # Add the individual headers from each target in an arch-specific name $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_INCLUDE-$$(target))/pyconfig.h $$(PYTHON_INCLUDE-$(sdk))/pyconfig-$$(ARCH-$$(target)).h; ) @@ -500,8 +562,11 @@ else cp $$(PYTHON_SRCDIR-$$(firstword $$(SDK_TARGETS-$(sdk))))/$(os)/Resources/pyconfig.h $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h endif - +ifneq ($(sdk),macabi) $$(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))) +else +$$(PYTHON_STDLIB-$(sdk))/LICENSE.TXT: $$(PYTHON_LIB-$(sdk)) $$(PYTHON_FRAMEWORK-$(sdk))/Versions/$(PYTHON_VER)/Resources $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_PLATFORM_SITECUSTOMIZE-$$(target))) +endif @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 @@ -667,16 +732,19 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ 2>&1 | tee -a support/$(PYTHON_VER)/python-$(os).xcframework.log @echo ">>> Install PYTHONHOME for $(os)" + # Do not install stuff for macabi becuase it's already built into the framework. +ifneq ($(os),MacCatalyst) $$(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)); ) +endif $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/platform-config $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) # Disable dSYM production (for now) # $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/Python.dSYM $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) -ifeq ($(filter $(os),iOS visionOS),$(os)) +ifeq ($(filter $(os),iOS visionOS MacCatalyst),$(os)) @echo ">>> Clone testbed project for $(os)" - $(HOST_PYTHON) $$(PYTHON_SRCDIR-$$(firstword $$(SDK_TARGETS-$$(firstword $$(SDKS-$(os))))))/$(os)/testbed clone --framework $$(PYTHON_XCFRAMEWORK-$(os)) support/$(PYTHON_VER)/$(os)/testbed + $(HOST_PYTHON) $$(PYTHON_SRCDIR-$$(firstword $$(SDK_TARGETS-$$(firstword $$(SDKS-$(os))))))/$$(subst MacCatalyst,iOS,$(os))/testbed clone --framework $$(PYTHON_XCFRAMEWORK-$(os)) support/$(PYTHON_VER)/$(os)/testbed endif @echo ">>> Create VERSIONS file for $(os)" diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index 73c5b5a..a9a6219 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1,3 +1,21 @@ +diff --git a/Lib/_ios_support.py b/Lib/_ios_support.py +index 20467a7c2bc..deefacaf8c5 100644 +--- a/Lib/_ios_support.py ++++ b/Lib/_ios_support.py +@@ -25,6 +25,7 @@ + def get_platform_ios(): + # Determine if this is a simulator using the multiarch value + is_simulator = sys.implementation._multiarch.endswith("simulator") ++ is_catalyst = sys.implementation._multiarch.endswith("macabi") + + # We can't use ctypes; abort + if not objc: +@@ -68,4 +69,4 @@ + release = objc.objc_msgSend(device_systemVersion, SEL_UTF8String).decode() + model = objc.objc_msgSend(device_model, SEL_UTF8String).decode() + +- return system, release, model, is_simulator ++ return system, release, model, is_simulator, is_catalyst diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 823a3692fd1..00639dd8488 100644 --- a/Lib/ctypes/__init__.py @@ -34,7 +52,7 @@ index 99504911a3d..527c2f36dd0 100644 if hasattr((_libc := ctypes.CDLL(None)), "dl_iterate_phdr"): diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py -index 8bcd741c446..d8a6f28edba 100644 +index 8bcd741c446..58642d961d6 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -52,7 +52,7 @@ @@ -46,23 +64,42 @@ index 8bcd741c446..d8a6f28edba 100644 _CASE_INSENSITIVE_PLATFORMS = (_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY + _CASE_INSENSITIVE_PLATFORMS_STR_KEY) -@@ -1535,7 +1535,7 @@ +@@ -1535,7 +1535,8 @@ """ extension_loaders = [] if hasattr(_imp, 'create_dynamic'): - if sys.platform in {"ios", "tvos", "watchos"}: -+ if sys.platform in {"ios", "tvos", "watchos", "visionos"}: ++ # Mac Catalyst does NOT use the AppleFrameworkLoader. ++ if sys.platform in {"ios", "tvos", "watchos", "visionos"} and not sys.implementation._multiarch.endswith("macabi"): extension_loaders = [(AppleFrameworkLoader, [ suffix.replace(".so", ".fwork") for suffix in _imp.extension_suffixes() diff --git a/Lib/platform.py b/Lib/platform.py -index 55e211212d4..cad919bc0c4 100644 +index 55e211212d4..c81849e870d 100644 --- a/Lib/platform.py +++ b/Lib/platform.py -@@ -528,6 +528,78 @@ - return IOSVersionInfo(system, release, model, is_simulator) +@@ -508,11 +508,11 @@ + # A namedtuple for iOS version information. + IOSVersionInfo = collections.namedtuple( + "IOSVersionInfo", +- ["system", "release", "model", "is_simulator"] ++ ["system", "release", "model", "is_simulator", "is_catalyst"] + ) +-def ios_ver(system="", release="", model="", is_simulator=False): ++def ios_ver(system="", release="", model="", is_simulator=False, is_catalyst=False): + """Get iOS version information, and return it as a namedtuple: + (system, release, model, is_simulator). + +@@ -525,7 +525,82 @@ + if result is not None: + return IOSVersionInfo(*result) + +- return IOSVersionInfo(system, release, model, is_simulator) ++ return IOSVersionInfo(system, release, model, is_simulator, is_catalyst) ++ ++ +# A namedtuple for tvOS version information. +TVOSVersionInfo = collections.namedtuple( + "TVOSVersionInfo", @@ -82,6 +119,7 @@ index 55e211212d4..cad919bc0c4 100644 + import _ios_support + result = _ios_support.get_platform_ios() + if result is not None: ++ result = result[:-1] # ignore the Catalyst flag + return TVOSVersionInfo(*result) + + return TVOSVersionInfo(system, release, model, is_simulator) @@ -106,6 +144,7 @@ index 55e211212d4..cad919bc0c4 100644 + import _ios_support + result = _ios_support.get_platform_ios() + if result is not None: ++ result = result[:-1] # ignore the Catalyst flag + return WatchOSVersionInfo(*result) + + return WatchOSVersionInfo(system, release, model, is_simulator) @@ -130,15 +169,14 @@ index 55e211212d4..cad919bc0c4 100644 + import _ios_support + result = _ios_support.get_platform_ios() + if result is not None: ++ result = result[:-1] # ignore the Catalyst flag + return VisionOSVersionInfo(*result) + + return VisionOSVersionInfo(system, release, model, is_simulator) -+ -+ + + def _java_getprop(name, default): - """This private helper is deprecated in 3.13 and will be removed in 3.15""" - from java.lang import System -@@ -727,7 +799,7 @@ +@@ -727,7 +802,7 @@ default in case the command should fail. """ @@ -147,7 +185,7 @@ index 55e211212d4..cad919bc0c4 100644 # XXX Others too ? return default -@@ -891,14 +963,30 @@ +@@ -891,10 +966,26 @@ csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0) return 'Alpha' if cpu_number >= 128 else 'VAX' @@ -159,10 +197,10 @@ index 55e211212d4..cad919bc0c4 100644 + # there's only one CPU architecture for devices, so we know the right + # answer. def get_ios(): - if sys.implementation._multiarch.endswith("simulator"): - return os.uname().machine - return 'arm64' - ++ if sys.implementation._multiarch.endswith("simulator") or sys.implementation._multiarch.endswith("macabi"): ++ return os.uname().machine ++ return 'arm64' ++ + def get_tvos(): + if sys.implementation._multiarch.endswith("simulator"): + return os.uname().machine @@ -174,21 +212,18 @@ index 55e211212d4..cad919bc0c4 100644 + return 'arm64_32' + + def get_visionos(): -+ if sys.implementation._multiarch.endswith("simulator"): -+ return os.uname().machine -+ return 'arm64' -+ - def from_subprocess(): - """ - Fall back to `uname -p` -@@ -1058,9 +1146,15 @@ + if sys.implementation._multiarch.endswith("simulator"): + return os.uname().machine + return 'arm64' +@@ -1058,9 +1149,15 @@ system = 'Android' release = android_ver().release - # Normalize responses on iOS + # Normalize responses on Apple mobile platforms if sys.platform == 'ios': - system, release, _, _ = ios_ver() +- system, release, _, _ = ios_ver() ++ system, release, _, _, _ = ios_ver() + if sys.platform == 'tvos': + system, release, _, _ = tvos_ver() + if sys.platform == 'watchos': @@ -198,10 +233,12 @@ index 55e211212d4..cad919bc0c4 100644 vals = system, node, release, version, machine # Replace 'unknown' values with the more portable '' -@@ -1350,6 +1444,12 @@ +@@ -1349,7 +1446,13 @@ + if system == 'Darwin': # macOS and iOS both report as a "Darwin" kernel if sys.platform == "ios": - system, release, _, _ = ios_ver() +- system, release, _, _ = ios_ver() ++ system, release, _, _, _ = ios_ver() + elif sys.platform == "tvos": + system, release, _, _ = tvos_ver() + elif sys.platform == "watchos": @@ -227,15 +264,18 @@ index f9327197159..74899abecb0 100644 def joinuser(*args): diff --git a/Lib/subprocess.py b/Lib/subprocess.py -index 54c2eb515b6..03896a234bf 100644 +index 54c2eb515b6..24dd754db06 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py -@@ -75,7 +75,7 @@ +@@ -75,7 +75,10 @@ _mswindows = True # some platforms do not support subprocesses -_can_fork_exec = sys.platform not in {"emscripten", "wasi", "ios", "tvos", "watchos"} -+_can_fork_exec = sys.platform not in {"emscripten", "wasi", "ios", "tvos", "watchos", "visionos"} ++_can_fork_exec = ( ++ sys.platform not in {"emscripten", "wasi", "ios", "tvos", "watchos", "visionos"} ++ or sys.implementation._multiarch.endswith("macabi") ++) if _mswindows: import _winapi @@ -282,7 +322,7 @@ index f93b98dd681..0db3dbdce05 100644 import _osx_support osname, release, machine = _osx_support.get_platform_osx( diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py -index 1b551254f86..8594f92c097 100644 +index 1b551254f86..3ae2728e2cf 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -7159,9 +7159,9 @@ @@ -290,18 +330,27 @@ index 1b551254f86..8594f92c097 100644 def test_type_check_in_subinterp(self): - # iOS requires the use of the custom framework loader, -+ # Apple mobile platforms require the use of the custom framework loader, - # not the ExtensionFileLoader. +- # not the ExtensionFileLoader. - if sys.platform == "ios": -+ if support.is_apple_mobile: ++ # Apple mobile platforms EXCEPT Mac Catalyst require the use of the ++ # custom framework loader, not the ExtensionFileLoader. ++ if support.needs_apple_fworks: extension_loader = "AppleFrameworkLoader" else: extension_loader = "ExtensionFileLoader" diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py -index b7cd7940eb1..32243a49e7a 100644 +index b7cd7940eb1..4f027912b0a 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py -@@ -558,7 +558,7 @@ +@@ -46,6 +46,7 @@ + # sys + "MS_WINDOWS", "is_jython", "is_android", "is_emscripten", "is_wasi", + "is_apple_mobile", "check_impl_detail", "unix_shell", "setswitchinterval", ++ "is_mac_catalyst", "needs_apple_fworks", + # os + "get_pagesize", + # network +@@ -558,7 +559,7 @@ sys.platform == "android", f"Android blocks {name} with SELinux" ) @@ -310,15 +359,60 @@ index b7cd7940eb1..32243a49e7a 100644 unix_shell = '/system/bin/sh' if is_android else '/bin/sh' else: unix_shell = None -@@ -574,7 +574,7 @@ +@@ -574,8 +575,10 @@ def skip_wasi_stack_overflow(): return unittest.skipIf(is_wasi, "Exhausts stack on WASI") -is_apple_mobile = sys.platform in {"ios", "tvos", "watchos"} +is_apple_mobile = sys.platform in {"ios", "tvos", "watchos", "visionos"} is_apple = is_apple_mobile or sys.platform == "darwin" ++is_mac_catalyst = sys.implementation._multiarch.endswith("macabi") ++needs_apple_fworks = is_apple_mobile and not is_mac_catalyst has_fork_support = hasattr(os, "fork") and not ( + # WASM and Apple mobile platforms do not support subprocesses. +@@ -586,6 +589,9 @@ + # Although Android supports fork, it's unsafe to call it from Python because + # all Android apps are multi-threaded. + or is_android ++ ++ # Mac Catalyst supports subprocesses. ++ and not is_mac_catalyst + ) + + def requires_fork(): +@@ -601,6 +607,9 @@ + # practice (see PEP 738). And most of the tests that use them are calling + # sys.executable, which won't work when Python is embedded in an Android app. + or is_android ++ ++ # Mac Catalyst supports subprocesses. ++ and not is_mac_catalyst + ) + + def requires_subprocess(): +diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py +index f74694a7a74..5ad6f4d4ff1 100644 +--- a/Lib/test/test_capi/test_misc.py ++++ b/Lib/test/test_capi/test_misc.py +@@ -1920,7 +1920,7 @@ + + # Apple extensions must be distributed as frameworks. This requires + # a specialist loader. +- if support.is_apple_mobile: ++ if support.needs_apple_fworks: + loader = "AppleFrameworkLoader" + else: + loader = "ExtensionFileLoader" +@@ -2604,7 +2604,7 @@ + origin = importlib.util.find_spec('_testmultiphase').origin + # Apple extensions must be distributed as frameworks. This requires + # a specialist loader. +- if support.is_apple_mobile: ++ if support.needs_apple_fworks: + loader = importlib.machinery.AppleFrameworkLoader(fullname, origin) + else: + loader = importlib.machinery.ExtensionFileLoader(fullname, origin) diff --git a/Lib/test/test_ctypes/test_dllist.py b/Lib/test/test_ctypes/test_dllist.py index 15603dc3d77..bff6c0fb95f 100644 --- a/Lib/test/test_ctypes/test_dllist.py @@ -332,8 +426,217 @@ index 15603dc3d77..bff6c0fb95f 100644 if WINDOWS: KNOWN_LIBRARIES = ["KERNEL32.DLL"] +diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py +index 6e34094c5aa..87a81fdecfd 100644 +--- a/Lib/test/test_import/__init__.py ++++ b/Lib/test/test_import/__init__.py +@@ -33,7 +33,7 @@ + swap_attr, + swap_item, + cpython_only, +- is_apple_mobile, ++ needs_apple_fworks, + is_emscripten, + is_wasi, + run_in_subinterp, +@@ -111,7 +111,7 @@ + def require_extension(module, *, skip=False): + # Apple extensions must be distributed as frameworks. This requires + # a specialist loader. +- if is_apple_mobile: ++ if needs_apple_fworks: + _require_loader(module, AppleFrameworkLoader, skip) + else: + _require_loader(module, ExtensionFileLoader, skip) +@@ -126,7 +126,7 @@ + def create_extension_loader(modname, filename): + # Apple extensions must be distributed as frameworks. This requires + # a specialist loader. +- if is_apple_mobile: ++ if needs_apple_fworks: + return AppleFrameworkLoader(modname, filename) + else: + return ExtensionFileLoader(modname, filename) +@@ -2217,7 +2217,7 @@ + if filename: + # Apple extensions must be distributed as frameworks. This requires + # a specialist loader. +- if is_apple_mobile: ++ if needs_apple_fworks: + loader = "AppleFrameworkLoader" + else: + loader = "ExtensionFileLoader" +@@ -2692,7 +2692,7 @@ + # Apple extensions must be distributed as frameworks. This requires + # a specialist loader, and we need to differentiate between the + # spec.origin and the original file location. +- if is_apple_mobile: ++ if needs_apple_fworks: + assert cls.LOADER is AppleFrameworkLoader + + cls.ORIGIN = spec.origin +diff --git a/Lib/test/test_importlib/extension/test_finder.py b/Lib/test/test_importlib/extension/test_finder.py +index cdc8884d668..d1b525f6419 100644 +--- a/Lib/test/test_importlib/extension/test_finder.py ++++ b/Lib/test/test_importlib/extension/test_finder.py +@@ -1,4 +1,4 @@ +-from test.support import is_apple_mobile ++from test.support import needs_apple_fworks + from test.test_importlib import abc, util + + machinery = util.import_importlib('importlib.machinery') +@@ -20,7 +20,7 @@ + ) + + def find_spec(self, fullname): +- if is_apple_mobile: ++ if needs_apple_fworks: + # Apple mobile platforms require a specialist loader that uses + # .fwork files as placeholders for the true `.so` files. + loaders = [ +diff --git a/Lib/test/test_importlib/extension/test_loader.py b/Lib/test/test_importlib/extension/test_loader.py +index 0dd21e079eb..efae18d4bbd 100644 +--- a/Lib/test/test_importlib/extension/test_loader.py ++++ b/Lib/test/test_importlib/extension/test_loader.py +@@ -1,4 +1,4 @@ +-from test.support import is_apple_mobile ++from test.support import needs_apple_fworks + from test.test_importlib import abc, util + + machinery = util.import_importlib('importlib.machinery') +@@ -28,7 +28,7 @@ + + # Apple extensions must be distributed as frameworks. This requires + # a specialist loader. +- if is_apple_mobile: ++ if needs_apple_fworks: + self.LoaderClass = self.machinery.AppleFrameworkLoader + else: + self.LoaderClass = self.machinery.ExtensionFileLoader +@@ -110,7 +110,7 @@ + + # Apple extensions must be distributed as frameworks. This requires + # a specialist loader. +- if is_apple_mobile: ++ if needs_apple_fworks: + self.LoaderClass = self.machinery.AppleFrameworkLoader + else: + self.LoaderClass = self.machinery.ExtensionFileLoader +@@ -198,7 +198,7 @@ + + # Apple extensions must be distributed as frameworks. This requires + # a specialist loader. +- if is_apple_mobile: ++ if needs_apple_fworks: + self.LoaderClass = self.machinery.AppleFrameworkLoader + else: + self.LoaderClass = self.machinery.ExtensionFileLoader +diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py +index 5de89714eb5..0c6661791ca 100644 +--- a/Lib/test/test_importlib/test_util.py ++++ b/Lib/test/test_importlib/test_util.py +@@ -714,7 +714,7 @@ + def test_incomplete_multi_phase_init_module(self): + # Apple extensions must be distributed as frameworks. This requires + # a specialist loader. +- if support.is_apple_mobile: ++ if support.needs_apple_fworks: + loader = "AppleFrameworkLoader" + else: + loader = "ExtensionFileLoader" +diff --git a/Lib/test/test_importlib/util.py b/Lib/test/test_importlib/util.py +index edbe78545a2..d1f6965196f 100644 +--- a/Lib/test/test_importlib/util.py ++++ b/Lib/test/test_importlib/util.py +@@ -8,7 +8,7 @@ + import os.path + from test import support + from test.support import import_helper +-from test.support import is_apple_mobile ++from test.support import needs_apple_fworks + from test.support import os_helper + import unittest + import sys +@@ -48,7 +48,7 @@ + for ext in machinery.EXTENSION_SUFFIXES: + # Apple mobile platforms mechanically load .so files, + # but the findable files are labelled .fwork +- if is_apple_mobile: ++ if needs_apple_fworks: + ext = ext.replace(".so", ".fwork") + + filename = EXTENSIONS.name + ext +diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py +index 88b5b0e6e35..c0dfb3ae6d6 100644 +--- a/Lib/test/test_os.py ++++ b/Lib/test/test_os.py +@@ -2510,19 +2510,51 @@ + + @unittest.skipUnless(hasattr(os, 'closerange'), 'test needs os.closerange()') + def test_closerange(self): +- fd = os_helper.make_bad_fd() +- # Make sure none of the descriptors we are about to close are +- # currently valid (issue 6542). +- for i in range(10): +- try: os.fstat(fd+i) +- except OSError: +- pass +- else: +- break +- if i < 2: +- raise unittest.SkipTest( +- "Unable to acquire a range of invalid file descriptors") +- self.assertEqual(os.closerange(fd, fd + i-1), None) ++ if support.is_mac_catalyst: ++ # On Mac Catalyst, somehow there's a guarded FD in the way, ++ # undected using fstat. We make some random fds first, stop ++ # once not consecutive, close all of them one by one using ++ # the result of open(), then assert that closerange is failing. ++ # This ensures that none of those things we're closing is ++ # guarded, if we're careful to not use code that makes guarded ++ # file descriptors. ++ ++ copies = [] ++ # Open a file for testing and get its FD ++ file = open(os_helper.TESTFN, "wb") ++ fd = file.fileno() ++ copies.append(file) ++ for i in range(1, 10): ++ file_dup = open(os_helper.TESTFN, "wb") ++ if file_dup.fileno() != fd + i: ++ file_dup.close() ++ break ++ else: ++ copies.append(file_dup) ++ # Close everything ++ for copy in copies: ++ copy.close() ++ os.unlink(os_helper.TESTFN) ++ if i < 2: ++ raise unittest.SkipTest( ++ "Unable to acquire a range of invalid file descriptors") ++ ++ # Now we're left with invalid FDs. Let's go close them! ++ self.assertEqual(os.closerange(fd, fd + i-1), None) ++ else: ++ fd = os_helper.make_bad_fd() ++ # Make sure none of the descriptors we are about to close are ++ # currently valid (issue 6542). ++ for i in range(10): ++ try: os.fstat(fd+i) ++ except OSError: ++ pass ++ else: ++ break ++ if i < 2: ++ raise unittest.SkipTest( ++ "Unable to acquire a range of invalid file descriptors") ++ self.assertEqual(os.closerange(fd, fd + i-1), None) + + @unittest.skipUnless(hasattr(os, 'dup2'), 'test needs os.dup2()') + def test_dup2(self): diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py -index 719c4feace6..92a831a9148 100644 +index 719c4feace6..e32d0bd8c16 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -271,13 +271,21 @@ @@ -361,6 +664,58 @@ index 719c4feace6..92a831a9148 100644 else: self.assertEqual(res.system, "") self.assertEqual(res.release, "") +@@ -480,7 +488,7 @@ + + # ios_ver is only fully available on iOS where ctypes is available. + if sys.platform == "ios" and _ctypes: +- system, release, model, is_simulator = result ++ system, release, model, is_simulator, is_catalyst = result + # Result is a namedtuple + self.assertEqual(result.system, system) + self.assertEqual(result.release, release) +@@ -491,6 +499,7 @@ + # ios_ver(), so we check that the values are broadly what we expect. + + # System is either iOS or iPadOS, depending on the test device ++ # Mac Catalyst returns iPadOS for whatever reason. + self.assertIn(system, {"iOS", "iPadOS"}) + + # Release is a numeric version specifier with at least 2 parts +@@ -503,6 +512,9 @@ + # we get a model descriptor like "iPhone13,1" + if is_simulator: + self.assertIn(model, {"iPhone", "iPad"}) ++ # Mac Catalyst identifies as iPad with no version. ++ elif is_catalyst: ++ self.assertEqual(model, "iPad") + else: + self.assertTrue( + (model.startswith("iPhone") or model.startswith("iPad")) +@@ -510,6 +522,10 @@ + ) + + self.assertEqual(type(is_simulator), bool) ++ ++ # Mac Catalyst platform will return iPadOS. ++ if is_catalyst: ++ self.assertEqual(system, "iPadOS") + else: + # On non-iOS platforms, calling ios_ver doesn't fail; you get + # default values +@@ -519,11 +535,12 @@ + self.assertFalse(result.is_simulator) + + # Check the fallback values can be overridden by arguments +- override = platform.ios_ver("Foo", "Bar", "Whiz", True) ++ override = platform.ios_ver("Foo", "Bar", "Whiz", True, True) + self.assertEqual(override.system, "Foo") + self.assertEqual(override.release, "Bar") + self.assertEqual(override.model, "Whiz") + self.assertTrue(override.is_simulator) ++ self.assertTrue(override.is_catalyst) + + @unittest.skipIf(support.is_emscripten, "Does not apply to Emscripten") + def test_libc_ver(self): diff --git a/Lib/test/test_webbrowser.py b/Lib/test/test_webbrowser.py index 4c3ea1cd8df..04a210e5c86 100644 --- a/Lib/test/test_webbrowser.py @@ -402,8 +757,99 @@ index f2e2394089d..2efbbfb0014 100644 from _ios_support import objc if objc: # If objc exists, we know ctypes is also importable. +--- /dev/null ++++ b/MacCatalyst/Resources/Info.plist.in +@@ -0,0 +1,38 @@ ++ ++ ++ ++ ++ CFBundleDevelopmentRegion ++ en ++ CFBundleExecutable ++ Python ++ CFBundleGetInfoString ++ Python Runtime and Library ++ CFBundleIdentifier ++ @PYTHONFRAMEWORKIDENTIFIER@ ++ CFBundleInfoDictionaryVersion ++ 6.0 ++ CFBundleName ++ Python ++ CFBundlePackageType ++ FMWK ++ CFBundleShortVersionString ++ %VERSION% ++ CFBundleLongVersionString ++ %VERSION%, (c) 2001-2024 Python Software Foundation. ++ CFBundleSignature ++ ???? ++ CFBundleVersion ++ %VERSION% ++ CFBundleSupportedPlatforms ++ ++ MacOSX ++ ++ LSMinimumSystemVersion ++ @CATALYST_MACOS_VERSION@ ++ UIDeviceFamily ++ ++ 2 ++ ++ ++ +--- /dev/null ++++ b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-ar +@@ -0,0 +1,2 @@ ++#!/bin/sh ++xcrun --sdk macosx${MACOSX_SDK_VERSION} ar "$@" +--- /dev/null ++++ b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang +@@ -0,0 +1,2 @@ ++#!/bin/sh ++xcrun --sdk macosx${MACOSX_SDK_VERSION} clang -target arm64-apple-ios-macabi "$@" +--- /dev/null ++++ b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang++ +@@ -0,0 +1,2 @@ ++#!/bin/sh ++xcrun --sdk macosx${MACOSX_SDK_VERSION} clang++ -target arm64-apple-ios-macabi "$@" +--- /dev/null ++++ b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-cpp +@@ -0,0 +1,2 @@ ++#!/bin/sh ++xcrun --sdk macosx${MACOSX_SDK_VERSION} clang -target arm64-apple-ios-macabi -E "$@" +--- /dev/null ++++ b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-ar +@@ -0,0 +1,2 @@ ++#!/bin/sh ++xcrun --sdk macosx${MACOSX_SDK_VERSION} ar "$@" +--- /dev/null ++++ b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang +@@ -0,0 +1,2 @@ ++#!/bin/sh ++xcrun --sdk macosx${MACOSX_SDK_VERSION} clang -target x86_64-apple-ios-macabi "$@" +--- /dev/null ++++ b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang++ +@@ -0,0 +1,2 @@ ++#!/bin/sh ++xcrun --sdk macosx${MACOSX_SDK_VERSION} clang++ -target x86_64-apple-ios-macabi "$@" +--- /dev/null ++++ b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-cpp +@@ -0,0 +1,2 @@ ++#!/bin/sh ++xcrun --sdk macosx${MACOSX_SDK_VERSION} clang -target x86_64-apple-ios-macabi -E "$@" +--- /dev/null ++++ b/MacCatalyst/Resources/pyconfig.h +@@ -0,0 +1,7 @@ ++#ifdef __arm64__ ++#include "pyconfig-arm64.h" ++#endif ++ ++#ifdef __x86_64__ ++#include "pyconfig-x86_64.h" ++#endif diff --git a/Makefile.pre.in b/Makefile.pre.in -index b5703fbe6ae..e436b2efb8e 100644 +index b5703fbe6ae..d8df7de3102 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -209,6 +209,12 @@ @@ -419,7 +865,7 @@ index b5703fbe6ae..e436b2efb8e 100644 # Option to install to strip binaries STRIPFLAG=-s -@@ -2264,7 +2270,7 @@ +@@ -2264,31 +2270,64 @@ # a full Xcode install that has an iPhone SE (3rd edition) simulator available. # This must be run *after* a `make install` has completed the build. The # `--with-framework-name` argument *cannot* be used when configuring the build. @@ -428,17 +874,31 @@ index b5703fbe6ae..e436b2efb8e 100644 .PHONY: testios testios: @if test "$(MACHDEP)" != "ios"; then \ -@@ -2284,11 +2290,41 @@ + echo "Cannot run the iOS testbed for a non-iOS build."; \ exit 1;\ fi - -- # Clone the testbed project into the XCFOLDER -- $(PYTHON_FOR_BUILD) $(srcdir)/iOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER)" -+ # Clone the testbed project into the XCFOLDER-iOS -+ $(PYTHON_FOR_BUILD) $(srcdir)/iOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER-iOS)" ++ @if test $(PYTHONFRAMEWORK) != "Python"; then \ ++ echo "Cannot run the iOS testbed with a non-default framework name."; \ ++ exit 1;\ ++ fi ++ @if ! test -d $(PYTHONFRAMEWORKPREFIX); then \ ++ echo "Cannot find a finalized iOS Python.framework. Have you run 'make install' to finalize the framework build?"; \ ++ exit 1;\ ++ fi + -+ # Run the testbed project -+ $(PYTHON_FOR_BUILD) "$(XCFOLDER-iOS)" run --verbose -- test -uall --single-process --rerun -W + @if test "$(findstring -iphonesimulator,$(MULTIARCH))" != "-iphonesimulator"; then \ +- echo "Cannot run the iOS testbed for non-simulator builds."; \ ++ if test "$(findstring -macabi,$(MULTIARCH))" != "-macabi"; then \ ++ echo "Cannot run the iOS testbed for device builds."; \ ++ exit 1;\ ++ else \ ++ $(PYTHON_FOR_BUILD) $(srcdir)/iOS/testbed --catalyst clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER-iOS)"; \ ++ $(PYTHON_FOR_BUILD) "$(XCFOLDER-iOS)" --catalyst run --verbose -- test -uall --single-process --rerun -W; \ ++ fi \ ++ else \ ++ $(PYTHON_FOR_BUILD) $(srcdir)/iOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER-iOS)"; \ ++ $(PYTHON_FOR_BUILD) "$(XCFOLDER-iOS)" run --verbose -- test -uall --single-process --rerun -W; \ ++ fi + +# Run the test suite on the visionOS simulator. Must be run on a macOS machine with +# a full Xcode install that has an Apple Vision Pro simulator available. @@ -453,17 +913,21 @@ index b5703fbe6ae..e436b2efb8e 100644 + fi + @if test "$(findstring -xrsimulator,$(MULTIARCH))" != "-xrsimulator"; then \ + echo "Cannot run the visionOS testbed for non-simulator builds."; \ -+ exit 1;\ -+ fi -+ @if test $(PYTHONFRAMEWORK) != "Python"; then \ + exit 1;\ + fi + @if test $(PYTHONFRAMEWORK) != "Python"; then \ +- echo "Cannot run the iOS testbed with a non-default framework name."; \ + echo "Cannot run the visionOS testbed with a non-default framework name."; \ -+ exit 1;\ -+ fi -+ @if ! test -d $(PYTHONFRAMEWORKPREFIX); then \ + exit 1;\ + fi + @if ! test -d $(PYTHONFRAMEWORKPREFIX); then \ +- echo "Cannot find a finalized iOS Python.framework. Have you run 'make install' to finalize the framework build?"; \ + echo "Cannot find a finalized visionOS Python.framework. Have you run 'make install' to finalize the framework build?"; \ -+ exit 1;\ -+ fi -+ + exit 1;\ + fi + +- # Clone the testbed project into the XCFOLDER +- $(PYTHON_FOR_BUILD) $(srcdir)/iOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER)" + # Clone the testbed project into the XCFOLDER-visionOS + $(PYTHON_FOR_BUILD) $(srcdir)/visionOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER-visionOS)" @@ -473,11 +937,44 @@ index b5703fbe6ae..e436b2efb8e 100644 # Like test, but using --slow-ci which enables all test resources and use # longer timeout. Run an optional pybuildbot.identify script to include +@@ -2977,7 +3016,7 @@ + fi; \ + done + $(LN) -fsn include/python$(LDVERSION) $(DESTDIR)$(prefix)/Headers +- sed 's/%VERSION%/'"`$(RUNSHARED) ./$(BUILDPYTHON) -c 'import platform; print(platform.python_version())'`"'/g' < $(RESSRCDIR)/Info.plist > $(DESTDIR)$(prefix)/Resources/Info.plist ++ sed 's/%VERSION%/'"`$(RUNSHARED) ./$(PYTHON_FOR_BUILD) -c 'import platform; print(platform.python_version())'`"'/g' < $(RESSRCDIR)/Info.plist > $(DESTDIR)$(prefix)/Resources/Info.plist + $(LN) -fsn $(VERSION) $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Versions/Current + $(LN) -fsn Versions/Current/$(PYTHONFRAMEWORK) $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/$(PYTHONFRAMEWORK) + $(LN) -fsn Versions/Current/Headers $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Headers +@@ -3005,6 +3044,14 @@ + $(INSTALL) -m $(EXEMODE) $$file $(DESTDIR)$(BINDIR); \ + done + ++# Stub compilation assistance binaries are installed separately on Mac Catalyst. ++.PHONY: frameworkinstallcatalyststubs ++frameworkinstallcatalyststubs: $(LDLIBRARY) ++ $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$(BINDIR) ++ for file in $(srcdir)/$(RESSRCDIR)/bin/* ; do \ ++ $(INSTALL) -m $(EXEMODE) $$file $(DESTDIR)$(BINDIR); \ ++ done ++ + # This installs Mac/Lib into the framework + # Install a number of symlinks to keep software that expects a normal unix + # install (which includes python-config) happy. diff --git a/Misc/platform_triplet.c b/Misc/platform_triplet.c -index f5cd73bdea8..6c1863c943b 100644 +index f5cd73bdea8..ba634ae1699 100644 --- a/Misc/platform_triplet.c +++ b/Misc/platform_triplet.c -@@ -257,6 +257,32 @@ +@@ -254,9 +254,41 @@ + # else + PLATFORM_TRIPLET=arm64-iphonesimulator + # endif ++# elif defined(TARGET_OS_MACCATALYST) && TARGET_OS_MACCATALYST ++# if __x86_64__ ++PLATFORM_TRIPLET=x86_64-iphoneos-macabi ++# else ++PLATFORM_TRIPLET=arm64-iphoneos-macabi ++# endif # else PLATFORM_TRIPLET=arm64-iphoneos # endif @@ -511,7 +1008,7 @@ index f5cd73bdea8..6c1863c943b 100644 # elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX PLATFORM_TRIPLET=darwin diff --git a/config.sub b/config.sub -index 1bb6a05dc11..49febd56a37 100755 +index 1bb6a05dc11..6efa3fbf821 100755 --- a/config.sub +++ b/config.sub @@ -1743,7 +1743,7 @@ @@ -523,23 +1020,35 @@ index 1bb6a05dc11..49febd56a37 100755 | mpw* | magic* | mmixware* | mon960* | lnews* \ | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ | aos* | aros* | cloudabi* | sortix* | twizzler* \ -@@ -1867,7 +1867,7 @@ +@@ -1769,7 +1769,7 @@ + | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ + | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \ + | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \ +- | fiwix* | mlibc* | cos* | mbr* | ironclad* ) ++ | fiwix* | mlibc* | cos* | mbr* | ironclad* | macabi) + ;; + # This one is extra strict with allowed versions + sco3.2v2 | sco3.2v[4-9]* | sco5v6*) +@@ -1867,7 +1867,9 @@ ;; *-eabi*- | *-gnueabi*-) ;; - ios*-simulator- | tvos*-simulator- | watchos*-simulator- ) + ios*-simulator- | tvos*-simulator- | watchos*-simulator- | xros*-simulator-) ++ ;; ++ ios*-macabi- ) ;; none--*) # None (no kernel, i.e. freestanding / bare metal), diff --git a/configure b/configure -index 884f8a4b068..7c93d36e717 100755 +index 884f8a4b068..64b6e3ce2b3 100755 --- a/configure +++ b/configure -@@ -982,6 +982,10 @@ +@@ -982,6 +982,11 @@ CFLAGS CC HAS_XCRUN ++CATALYST_MACOS_VERSION +EXPORT_XROS_DEPLOYMENT_TARGET +XROS_DEPLOYMENT_TARGET +WATCHOS_DEPLOYMENT_TARGET @@ -547,7 +1056,25 @@ index 884f8a4b068..7c93d36e717 100755 IPHONEOS_DEPLOYMENT_TARGET EXPORT_MACOSX_DEPLOYMENT_TARGET CONFIGURE_MACOSX_DEPLOYMENT_TARGET -@@ -4116,6 +4120,15 @@ +@@ -1085,6 +1090,7 @@ + with_framework_name + enable_framework + with_app_store_compliance ++with_catalyst_macos_version + enable_wasm_dynamic_linking + enable_wasm_pthreads + with_suffix +@@ -1874,6 +1880,9 @@ + Enable any patches required for compiliance with app + stores. Optional PATCH-FILE specifies the custom + patch to apply. ++ --with-catalyst-macos-version=VER ++ The minimum macOS version a Catalyst build can run ++ on (only valid for *-apple-ios*-macabi targets) + --with-suffix=SUFFIX set executable suffix to SUFFIX (default is empty, + yes is mapped to '.exe') + --without-static-libpython +@@ -4116,6 +4125,15 @@ *-apple-ios*) ac_sys_system=iOS ;; @@ -563,7 +1090,7 @@ index 884f8a4b068..7c93d36e717 100755 *-*-darwin*) ac_sys_system=Darwin ;; -@@ -4197,7 +4210,7 @@ +@@ -4197,7 +4215,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. # @@ -572,10 +1099,17 @@ index 884f8a4b068..7c93d36e717 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 -@@ -4212,6 +4225,17 @@ +@@ -4209,33 +4227,89 @@ + # configure will fail. + if test -z "$AR"; then + case "$host" in ++ x86_64-apple-ios*-macabi) AR=x86_64-apple-ios-macabi-ar ;; ++ aarch64-apple-ios*-macabi) AR=arm64-apple-ios-macabi-ar ;; ++ ++ x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;; 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 ;; +- x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;; + + aarch64-apple-tvos*-simulator) AR=arm64-apple-tvos-simulator-ar ;; + aarch64-apple-tvos*) AR=arm64-apple-tvos-ar ;; @@ -590,10 +1124,15 @@ index 884f8a4b068..7c93d36e717 100755 *) esac fi -@@ -4220,6 +4244,17 @@ + if test -z "$CC"; then + case "$host" in ++ x86_64-apple-ios*-macabi) CC=x86_64-apple-ios-macabi-clang ;; ++ aarch64-apple-ios*-macabi) CC=arm64-apple-ios-macabi-clang ;; ++ ++ x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;; 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 ;; +- x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;; + + aarch64-apple-tvos*-simulator) CC=arm64-apple-tvos-simulator-clang ;; + aarch64-apple-tvos*) CC=arm64-apple-tvos-clang ;; @@ -608,10 +1147,15 @@ index 884f8a4b068..7c93d36e717 100755 *) esac fi -@@ -4228,6 +4263,17 @@ + if test -z "$CPP"; then + case "$host" in ++ x86_64-apple-ios*-macabi) CPP=x86_64-apple-ios-macabi-cpp ;; ++ aarch64-apple-ios*-macabi) CPP=arm64-apple-ios-macabi-cpp ;; ++ ++ x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;; 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 ;; +- x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;; + + aarch64-apple-tvos*-simulator) CPP=arm64-apple-tvos-simulator-cpp ;; + aarch64-apple-tvos*) CPP=arm64-apple-tvos-cpp ;; @@ -626,10 +1170,15 @@ index 884f8a4b068..7c93d36e717 100755 *) esac fi -@@ -4236,6 +4282,17 @@ + if test -z "$CXX"; then + case "$host" in ++ x86_64-apple-ios*-macabi) CXX=x86_64-apple-ios-macabi-clang++ ;; ++ aarch64-apple-ios*-macabi) CXX=arm64-apple-ios-macabi-clang++ ;; ++ ++ x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;; aarch64-apple-ios*-simulator) CXX=arm64-apple-ios-simulator-clang++ ;; aarch64-apple-ios*) CXX=arm64-apple-ios-clang++ ;; - x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;; +- x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;; + + aarch64-apple-tvos*-simulator) CXX=arm64-apple-tvos-simulator-clang++ ;; + aarch64-apple-tvos*) CXX=arm64-apple-tvos-clang++ ;; @@ -644,7 +1193,7 @@ index 884f8a4b068..7c93d36e717 100755 *) esac fi -@@ -4358,8 +4415,11 @@ +@@ -4358,8 +4432,11 @@ case $enableval in yes) case $ac_sys_system in @@ -658,7 +1207,7 @@ index 884f8a4b068..7c93d36e717 100755 *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 esac esac -@@ -4368,6 +4428,9 @@ +@@ -4368,6 +4445,9 @@ no) case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -668,10 +1217,39 @@ index 884f8a4b068..7c93d36e717 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4474,6 +4537,51 @@ - - ac_config_files="$ac_config_files iOS/Resources/Info.plist" +@@ -4461,6 +4541,67 @@ + ;; + iOS) : ++ _flag_ios_catalyst=`echo $host | cut -d '-' -f4` ++ case $_flag_ios_catalyst in ++ macabi) ++ FRAMEWORKINSTALLFIRST="frameworkinstallversionedstructure frameworkinstallcatalyststubs" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallversionedstructure frameworkinstallcatalyststubs " ++ FRAMEWORKINSTALLLAST="" ++ FRAMEWORKALTINSTALLLAST="" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ prefix=$PYTHONFRAMEWORKINSTALLDIR/Versions/$VERSION ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR/Versions/$VERSION" ++ RESSRCDIR=MacCatalyst/Resources ++ ac_config_files="$ac_config_files MacCatalyst/Resources/Info.plist" ++ ++ ;; ++ *) ++ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=iOS/Resources ++ ac_config_files="$ac_config_files iOS/Resources/Info.plist" ++ ++ ;; ++ esac + ;; + tvOS) : + FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" @@ -704,23 +1282,22 @@ index 884f8a4b068..7c93d36e717 100755 + + ;; + visionOS) : -+ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" -+ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " -+ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKPYTHONW= -+ INSTALLTARGETS="libinstall inclinstall sharedinstall" -+ -+ prefix=$PYTHONFRAMEWORKPREFIX -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" + FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" + FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " + FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" +@@ -4470,9 +4611,9 @@ + + prefix=$PYTHONFRAMEWORKPREFIX + PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" +- RESSRCDIR=iOS/Resources + RESSRCDIR=visionOS/Resources -+ + +- ac_config_files="$ac_config_files iOS/Resources/Info.plist" + ac_config_files="$ac_config_files visionOS/Resources/Info.plist" -+ + ;; *) - as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 -@@ -4485,6 +4593,9 @@ +@@ -4485,6 +4626,9 @@ e) case $ac_sys_system in iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; @@ -730,7 +1307,7 @@ index 884f8a4b068..7c93d36e717 100755 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -4539,8 +4650,8 @@ +@@ -4539,8 +4683,8 @@ case "$withval" in yes) case $ac_sys_system in @@ -741,7 +1318,7 @@ index 884f8a4b068..7c93d36e717 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 ;; -@@ -4558,8 +4669,8 @@ +@@ -4558,8 +4702,8 @@ else case e in #( e) case $ac_sys_system in @@ -752,16 +1329,77 @@ index 884f8a4b068..7c93d36e717 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; } -@@ -4577,6 +4688,8 @@ +@@ -4578,6 +4722,40 @@ ++ ++# Check whether --with-catalyst-macos-version was given. ++if test ${with_catalyst_macos_version+y} ++then : ++ withval=$with_catalyst_macos_version; case "$host" in ++ *-apple-ios*-macabi) ++ CATALYST_MACOS_VERSION="$withval" ++ ;; ++ *) ++ as_fn_error $? "--with-catalyst-macos-version is only valid when targeting Mac Catalyst (*-apple-ios*-macabi)." "$LINENO" 5 ++ ;; ++ esac ++ ++else case e in #( ++ e) case "$host" in ++ *-apple-ios*-macabi) ++ CATALYST_MACOS_VERSION=11.2 ++ ;; ++ *) ++ CATALYST_MACOS_VERSION= ++ ;; ++ esac ++ ++ ;; ++esac ++fi ++ ++ ++ ++ ++ +EXPORT_XROS_DEPLOYMENT_TARGET='#' + - ++ if test "$cross_compiling" = yes; then case "$host" in -@@ -4614,6 +4727,78 @@ + *-*-linux*) +@@ -4592,6 +4770,28 @@ + *-*-cygwin*) + _host_ident= + ;; ++ *-apple-ios*-macabi) ++ _host_os=`echo $host | cut -d '-' -f3` ++ _host_device=`echo $host | cut -d '-' -f4` # should be macabi ++ _host_device=${_host_device:=os} ++ ++ # IPHONEOS_DEPLOYMENT_TARGET is the minimum supported iOS version ++ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking iOS deployment target" >&5 ++printf %s "checking iOS deployment target... " >&6; } ++ IPHONEOS_DEPLOYMENT_TARGET=$(echo ${_host_os} | cut -c4-) ++ IPHONEOS_DEPLOYMENT_TARGET=${IPHONEOS_DEPLOYMENT_TARGET:=14.2} # else it returns invalid version number ++ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $IPHONEOS_DEPLOYMENT_TARGET" >&5 ++printf "%s\n" "$IPHONEOS_DEPLOYMENT_TARGET" >&6; } ++ ++ case "$host_cpu" in ++ aarch64) ++ _host_ident=${IPHONEOS_DEPLOYMENT_TARGET}-arm64-iphoneos-${_host_device} # platform_triplet.c uses iphoneos-macabi ++ ;; ++ *) ++ _host_ident=${IPHONEOS_DEPLOYMENT_TARGET}-$host_cpu-iphoneos-${_host_device} ++ ;; ++ esac ++ ;; + *-apple-ios*) + _host_os=`echo $host | cut -d '-' -f3` + _host_device=`echo $host | cut -d '-' -f4` +@@ -4614,6 +4814,78 @@ ;; esac ;; @@ -840,7 +1478,7 @@ index 884f8a4b068..7c93d36e717 100755 *-*-darwin*) case "$host_cpu" in arm*) -@@ -4704,9 +4889,15 @@ +@@ -4704,9 +4976,15 @@ define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; @@ -857,7 +1495,7 @@ index 884f8a4b068..7c93d36e717 100755 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -4769,7 +4960,13 @@ +@@ -4769,7 +5047,17 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' @@ -869,10 +1507,14 @@ index 884f8a4b068..7c93d36e717 100755 + + +# XROS_DEPLOYMENT_TARGET should get exported ++ ++ ++# The minimum macOS version that a Mac Catalyst build can run on. ++ # checks for alternative programs -@@ -4810,6 +5007,16 @@ +@@ -4810,6 +5098,16 @@ as_fn_append CFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" as_fn_append LDFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" ;; #( @@ -889,7 +1531,7 @@ index 884f8a4b068..7c93d36e717 100755 *) : ;; esac -@@ -7179,6 +7386,12 @@ +@@ -7179,6 +7477,12 @@ MULTIARCH="" ;; #( iOS) : MULTIARCH="" ;; #( @@ -902,7 +1544,7 @@ index 884f8a4b068..7c93d36e717 100755 FreeBSD*) : MULTIARCH="" ;; #( *) : -@@ -7199,7 +7412,7 @@ +@@ -7199,7 +7503,7 @@ printf "%s\n" "$MULTIARCH" >&6; } case $ac_sys_system in #( @@ -911,7 +1553,15 @@ index 884f8a4b068..7c93d36e717 100755 SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2` ;; #( *) : SOABI_PLATFORM=$PLATFORM_TRIPLET -@@ -7250,6 +7463,18 @@ +@@ -7246,10 +7550,26 @@ + PY_SUPPORT_TIER=3 ;; #( + x86_64-*-freebsd*/clang) : + PY_SUPPORT_TIER=3 ;; #( ++ aarch64-apple-ios*-macabi/clang) : ++ PY_SUPPORT_TIER=3 ;; #( ++ x86_64-apple-ios*-macabi/clang) : ++ PY_SUPPORT_TIER=3 ;; #( + aarch64-apple-ios*-simulator/clang) : PY_SUPPORT_TIER=3 ;; #( aarch64-apple-ios*/clang) : PY_SUPPORT_TIER=3 ;; #( @@ -930,7 +1580,7 @@ index 884f8a4b068..7c93d36e717 100755 aarch64-*-linux-android/clang) : PY_SUPPORT_TIER=3 ;; #( x86_64-*-linux-android/clang) : -@@ -7686,7 +7911,7 @@ +@@ -7686,7 +8006,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; @@ -939,7 +1589,7 @@ index 884f8a4b068..7c93d36e717 100755 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5;; -@@ -7752,7 +7977,7 @@ +@@ -7752,7 +8072,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -948,7 +1598,7 @@ index 884f8a4b068..7c93d36e717 100755 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -13574,7 +13799,7 @@ +@@ -13574,7 +13894,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -957,7 +1607,7 @@ index 884f8a4b068..7c93d36e717 100755 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -13707,7 +13932,7 @@ +@@ -13707,7 +14027,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -966,7 +1616,7 @@ index 884f8a4b068..7c93d36e717 100755 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -13731,7 +13956,7 @@ +@@ -13731,7 +14051,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -975,7 +1625,7 @@ index 884f8a4b068..7c93d36e717 100755 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -15508,7 +15733,7 @@ +@@ -15508,7 +15828,7 @@ ctypes_malloc_closure=yes ;; #( @@ -984,7 +1634,7 @@ index 884f8a4b068..7c93d36e717 100755 ctypes_malloc_closure=yes ;; #( -@@ -19260,12 +19485,6 @@ +@@ -19260,12 +19580,6 @@ then : printf "%s\n" "#define HAVE_DUP3 1" >>confdefs.h @@ -997,7 +1647,7 @@ index 884f8a4b068..7c93d36e717 100755 fi ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes -@@ -19326,18 +19545,6 @@ +@@ -19326,18 +19640,6 @@ then : printf "%s\n" "#define HAVE_FEXECVE 1" >>confdefs.h @@ -1016,7 +1666,7 @@ index 884f8a4b068..7c93d36e717 100755 fi ac_fn_c_check_func "$LINENO" "fpathconf" "ac_cv_func_fpathconf" if test "x$ac_cv_func_fpathconf" = xyes -@@ -19764,24 +19971,6 @@ +@@ -19764,24 +20066,6 @@ then : printf "%s\n" "#define HAVE_POSIX_OPENPT 1" >>confdefs.h @@ -1041,7 +1691,7 @@ index 884f8a4b068..7c93d36e717 100755 fi ac_fn_c_check_func "$LINENO" "pread" "ac_cv_func_pread" if test "x$ac_cv_func_pread" = xyes -@@ -20100,12 +20289,6 @@ +@@ -20100,12 +20384,6 @@ then : printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h @@ -1054,7 +1704,7 @@ index 884f8a4b068..7c93d36e717 100755 fi ac_fn_c_check_func "$LINENO" "sigfillset" "ac_cv_func_sigfillset" if test "x$ac_cv_func_sigfillset" = xyes -@@ -20374,11 +20557,11 @@ +@@ -20374,11 +20652,11 @@ fi @@ -1068,7 +1718,7 @@ index 884f8a4b068..7c93d36e717 100755 ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" if test "x$ac_cv_func_getentropy" = xyes then : -@@ -20400,6 +20583,53 @@ +@@ -20400,6 +20678,53 @@ fi @@ -1122,7 +1772,7 @@ index 884f8a4b068..7c93d36e717 100755 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5 printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; } if test ${ac_cv_c_undeclared_builtin_options+y} -@@ -23844,7 +24074,8 @@ +@@ -23844,7 +24169,8 @@ # check for openpty, login_tty, and forkpty @@ -1132,7 +1782,7 @@ index 884f8a4b068..7c93d36e717 100755 for ac_func in openpty do : -@@ -23958,7 +24189,7 @@ +@@ -23958,7 +24284,7 @@ fi done @@ -1141,7 +1791,7 @@ index 884f8a4b068..7c93d36e717 100755 printf %s "checking for library containing login_tty... " >&6; } if test ${ac_cv_search_login_tty+y} then : -@@ -24141,6 +24372,7 @@ +@@ -24141,6 +24467,7 @@ fi done @@ -1149,7 +1799,7 @@ index 884f8a4b068..7c93d36e717 100755 # check for long file support functions ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" -@@ -24406,10 +24638,10 @@ +@@ -24406,10 +24733,10 @@ done @@ -1162,7 +1812,7 @@ index 884f8a4b068..7c93d36e717 100755 then for ac_func in clock_settime -@@ -24726,7 +24958,7 @@ +@@ -24726,7 +25053,7 @@ e) if test "$cross_compiling" = yes then : @@ -1171,7 +1821,7 @@ index 884f8a4b068..7c93d36e717 100755 ac_cv_buggy_getaddrinfo="no" elif test "${enable_ipv6+set}" = set; then ac_cv_buggy_getaddrinfo="no -- configured with --(en|dis)able-ipv6" -@@ -26748,8 +26980,8 @@ +@@ -26748,8 +27075,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -1182,7 +1832,7 @@ index 884f8a4b068..7c93d36e717 100755 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -29619,7 +29851,7 @@ +@@ -29619,7 +29946,7 @@ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for device files" >&5 printf "%s\n" "$as_me: checking for device files" >&6;} @@ -1191,7 +1841,7 @@ index 884f8a4b068..7c93d36e717 100755 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -30129,7 +30361,7 @@ +@@ -30129,7 +30456,7 @@ with_ensurepip=no ;; #( WASI) : with_ensurepip=no ;; #( @@ -1200,7 +1850,7 @@ index 884f8a4b068..7c93d36e717 100755 with_ensurepip=no ;; #( *) : with_ensurepip=upgrade -@@ -31078,7 +31310,7 @@ +@@ -31078,7 +31405,7 @@ SunOS*) _PYTHREAD_NAME_MAXLEN=31;; NetBSD*) _PYTHREAD_NAME_MAXLEN=15;; # gh-131268 Darwin) _PYTHREAD_NAME_MAXLEN=63;; @@ -1209,18 +1859,36 @@ index 884f8a4b068..7c93d36e717 100755 FreeBSD*) _PYTHREAD_NAME_MAXLEN=19;; # gh-131268 OpenBSD*) _PYTHREAD_NAME_MAXLEN=23;; # gh-131268 *) _PYTHREAD_NAME_MAXLEN=;; -@@ -31110,7 +31342,7 @@ +@@ -31110,8 +31437,15 @@ ;; #( Darwin) : ;; #( - iOS) : + iOS|tvOS|watchOS|visionOS) : ++ case "$_host_device" in ++ macabi) ;; ++ *) ++ ++ py_cv_module__posixsubprocess=n/a ++ ;; ++ esac -@@ -35272,6 +35504,9 @@ + py_cv_module__curses=n/a +@@ -31119,7 +31453,6 @@ + py_cv_module__gdbm=n/a + py_cv_module__multiprocessing=n/a + py_cv_module__posixshmem=n/a +- py_cv_module__posixsubprocess=n/a + py_cv_module__scproxy=n/a + py_cv_module__tkinter=n/a + py_cv_module_grp=n/a +@@ -35271,7 +35604,11 @@ + "Mac/PythonLauncher/Makefile") CONFIG_FILES="$CONFIG_FILES Mac/PythonLauncher/Makefile" ;; "Mac/Resources/framework/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/framework/Info.plist" ;; "Mac/Resources/app/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/app/Info.plist" ;; ++ "MacCatalyst/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES MacCatalyst/Resources/Info.plist" ;; "iOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES iOS/Resources/Info.plist" ;; + "tvOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES tvOS/Resources/Info.plist" ;; + "watchOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES watchOS/Resources/Info.plist" ;; @@ -1229,7 +1897,7 @@ index 884f8a4b068..7c93d36e717 100755 "Misc/python.pc") CONFIG_FILES="$CONFIG_FILES Misc/python.pc" ;; "Misc/python-embed.pc") CONFIG_FILES="$CONFIG_FILES Misc/python-embed.pc" ;; diff --git a/configure.ac b/configure.ac -index cf25148bad2..7ab0609bf8a 100644 +index cf25148bad2..71957cbbaae 100644 --- a/configure.ac +++ b/configure.ac @@ -330,6 +330,15 @@ @@ -1257,10 +1925,17 @@ index cf25148bad2..7ab0609bf8a 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 -@@ -420,6 +429,17 @@ +@@ -417,33 +426,89 @@ + # configure will fail. + if test -z "$AR"; then + case "$host" in ++ x86_64-apple-ios*-macabi) AR=x86_64-apple-ios-macabi-ar ;; ++ aarch64-apple-ios*-macabi) AR=arm64-apple-ios-macabi-ar ;; ++ ++ x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;; 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 ;; +- x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;; + + aarch64-apple-tvos*-simulator) AR=arm64-apple-tvos-simulator-ar ;; + aarch64-apple-tvos*) AR=arm64-apple-tvos-ar ;; @@ -1275,10 +1950,15 @@ index cf25148bad2..7ab0609bf8a 100644 *) esac fi -@@ -428,6 +448,17 @@ + if test -z "$CC"; then + case "$host" in ++ x86_64-apple-ios*-macabi) CC=x86_64-apple-ios-macabi-clang ;; ++ aarch64-apple-ios*-macabi) CC=arm64-apple-ios-macabi-clang ;; ++ ++ x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;; 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 ;; +- x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;; + + aarch64-apple-tvos*-simulator) CC=arm64-apple-tvos-simulator-clang ;; + aarch64-apple-tvos*) CC=arm64-apple-tvos-clang ;; @@ -1293,10 +1973,15 @@ index cf25148bad2..7ab0609bf8a 100644 *) esac fi -@@ -436,6 +467,17 @@ + if test -z "$CPP"; then + case "$host" in ++ x86_64-apple-ios*-macabi) CPP=x86_64-apple-ios-macabi-cpp ;; ++ aarch64-apple-ios*-macabi) CPP=arm64-apple-ios-macabi-cpp ;; ++ ++ x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;; 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 ;; +- x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;; + + aarch64-apple-tvos*-simulator) CPP=arm64-apple-tvos-simulator-cpp ;; + aarch64-apple-tvos*) CPP=arm64-apple-tvos-cpp ;; @@ -1311,10 +1996,15 @@ index cf25148bad2..7ab0609bf8a 100644 *) esac fi -@@ -444,6 +486,17 @@ + if test -z "$CXX"; then + case "$host" in ++ x86_64-apple-ios*-macabi) CXX=x86_64-apple-ios-macabi-clang++ ;; ++ aarch64-apple-ios*-macabi) CXX=arm64-apple-ios-macabi-clang++ ;; ++ ++ x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;; aarch64-apple-ios*-simulator) CXX=arm64-apple-ios-simulator-clang++ ;; aarch64-apple-ios*) CXX=arm64-apple-ios-clang++ ;; - x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;; +- x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;; + + aarch64-apple-tvos*-simulator) CXX=arm64-apple-tvos-simulator-clang++ ;; + aarch64-apple-tvos*) CXX=arm64-apple-tvos-clang++ ;; @@ -1329,7 +2019,7 @@ index cf25148bad2..7ab0609bf8a 100644 *) esac fi -@@ -558,8 +611,11 @@ +@@ -558,8 +623,11 @@ case $enableval in yes) case $ac_sys_system in @@ -1343,7 +2033,7 @@ index cf25148bad2..7ab0609bf8a 100644 *) AC_MSG_ERROR([Unknown platform for framework build]) esac esac -@@ -568,6 +624,9 @@ +@@ -568,6 +636,9 @@ no) case $ac_sys_system in iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;; @@ -1353,22 +2043,50 @@ index cf25148bad2..7ab0609bf8a 100644 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -670,6 +729,48 @@ - - AC_CONFIG_FILES([iOS/Resources/Info.plist]) +@@ -657,6 +728,35 @@ + AC_CONFIG_FILES([Mac/Resources/app/Info.plist]) ;; + iOS) : ++ _flag_ios_catalyst=`echo $host | cut -d '-' -f4` ++ case $_flag_ios_catalyst in ++ macabi) ++ FRAMEWORKINSTALLFIRST="frameworkinstallversionedstructure frameworkinstallcatalyststubs" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallversionedstructure frameworkinstallcatalyststubs " ++ FRAMEWORKINSTALLLAST="" ++ FRAMEWORKALTINSTALLLAST="" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ prefix=$PYTHONFRAMEWORKINSTALLDIR/Versions/$VERSION ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR/Versions/$VERSION" ++ RESSRCDIR=MacCatalyst/Resources ++ AC_CONFIG_FILES([MacCatalyst/Resources/Info.plist]) ++ ;; ++ *) ++ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=iOS/Resources ++ AC_CONFIG_FILES([iOS/Resources/Info.plist]) ++ ;; ++ esac ++ ;; + tvOS) : -+ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" -+ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " -+ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKPYTHONW= -+ INSTALLTARGETS="libinstall inclinstall sharedinstall" -+ -+ prefix=$PYTHONFRAMEWORKPREFIX -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" + FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" + FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " + FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" +@@ -666,9 +766,37 @@ + + prefix=$PYTHONFRAMEWORKPREFIX + PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" +- RESSRCDIR=iOS/Resources + RESSRCDIR=tvOS/Resources -+ + +- AC_CONFIG_FILES([iOS/Resources/Info.plist]) + AC_CONFIG_FILES([tvOS/Resources/Info.plist]) + ;; + watchOS) : @@ -1398,11 +2116,10 @@ index cf25148bad2..7ab0609bf8a 100644 + RESSRCDIR=visionOS/Resources + + AC_CONFIG_FILES([visionOS/Resources/Info.plist]) -+ ;; + ;; *) AC_MSG_ERROR([Unknown platform for framework build]) - ;; -@@ -678,6 +779,9 @@ +@@ -678,6 +806,9 @@ ],[ case $ac_sys_system in iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;; @@ -1412,7 +2129,7 @@ index cf25148bad2..7ab0609bf8a 100644 *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework -@@ -730,8 +834,8 @@ +@@ -730,8 +861,8 @@ case "$withval" in yes) case $ac_sys_system in @@ -1423,7 +2140,7 @@ index cf25148bad2..7ab0609bf8a 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]) ;; -@@ -745,8 +849,8 @@ +@@ -745,8 +876,8 @@ esac ],[ case $ac_sys_system in @@ -1434,16 +2151,70 @@ index cf25148bad2..7ab0609bf8a 100644 APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" AC_MSG_RESULT([applying default app store compliance patch]) ;; -@@ -759,6 +863,8 @@ +@@ -759,6 +890,35 @@ ]) AC_SUBST([APP_STORE_COMPLIANCE_PATCH]) ++ ++AC_ARG_WITH([catalyst-macos-version], ++ [AS_HELP_STRING([--with-catalyst-macos-version=VER], ++ [The minimum macOS version a Catalyst build can run on (only valid for *-apple-ios*-macabi targets)])], ++ [case "$host" in ++ *-apple-ios*-macabi) ++ CATALYST_MACOS_VERSION="$withval" ++ ;; ++ *) ++ AC_MSG_ERROR([--with-catalyst-macos-version is only valid when targeting Mac Catalyst (*-apple-ios*-macabi).]) ++ ;; ++ esac ++ ], ++ [case "$host" in ++ *-apple-ios*-macabi) ++ CATALYST_MACOS_VERSION=11.2 ++ ;; ++ *) ++ CATALYST_MACOS_VERSION= ++ ;; ++ esac ++ ] ++) ++ ++ ++ ++ +EXPORT_XROS_DEPLOYMENT_TARGET='#' + AC_SUBST([_PYTHON_HOST_PLATFORM]) if test "$cross_compiling" = yes; then case "$host" in -@@ -794,6 +900,70 @@ +@@ -774,6 +934,26 @@ + *-*-cygwin*) + _host_ident= + ;; ++ *-apple-ios*-macabi) ++ _host_os=`echo $host | cut -d '-' -f3` ++ _host_device=`echo $host | cut -d '-' -f4` # should be macabi ++ _host_device=${_host_device:=os} ++ ++ # IPHONEOS_DEPLOYMENT_TARGET is the minimum supported iOS version ++ AC_MSG_CHECKING([iOS deployment target]) ++ IPHONEOS_DEPLOYMENT_TARGET=$(echo ${_host_os} | cut -c4-) ++ IPHONEOS_DEPLOYMENT_TARGET=${IPHONEOS_DEPLOYMENT_TARGET:=14.2} # else it returns invalid version number ++ AC_MSG_RESULT([$IPHONEOS_DEPLOYMENT_TARGET]) ++ ++ case "$host_cpu" in ++ aarch64) ++ _host_ident=${IPHONEOS_DEPLOYMENT_TARGET}-arm64-iphoneos-${_host_device} # platform_triplet.c uses iphoneos-macabi ++ ;; ++ *) ++ _host_ident=${IPHONEOS_DEPLOYMENT_TARGET}-$host_cpu-iphoneos-${_host_device} ++ ;; ++ esac ++ ;; + *-apple-ios*) + _host_os=`echo $host | cut -d '-' -f3` + _host_device=`echo $host | cut -d '-' -f4` +@@ -794,6 +974,70 @@ ;; esac ;; @@ -1514,7 +2285,7 @@ index cf25148bad2..7ab0609bf8a 100644 *-*-darwin*) case "$host_cpu" in arm*) -@@ -883,9 +1053,15 @@ +@@ -883,9 +1127,15 @@ define_xopen_source=no;; Darwin/@<:@[12]@:>@@<:@0-9@:>@.*) define_xopen_source=no;; @@ -1531,7 +2302,7 @@ index cf25148bad2..7ab0609bf8a 100644 # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) -@@ -944,8 +1120,14 @@ +@@ -944,8 +1194,18 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' @@ -1544,10 +2315,14 @@ index cf25148bad2..7ab0609bf8a 100644 +AC_SUBST([XROS_DEPLOYMENT_TARGET]) +# XROS_DEPLOYMENT_TARGET should get exported +AC_SUBST([EXPORT_XROS_DEPLOYMENT_TARGET]) ++ ++# The minimum macOS version that a Mac Catalyst build can run on. ++AC_SUBST([CATALYST_MACOS_VERSION]) ++ # checks for alternative programs -@@ -979,11 +1161,19 @@ +@@ -979,11 +1239,19 @@ ], ) @@ -1568,7 +2343,7 @@ index cf25148bad2..7ab0609bf8a 100644 ], ) -@@ -1172,6 +1362,9 @@ +@@ -1172,6 +1440,9 @@ AS_CASE([$ac_sys_system], [Darwin*], [MULTIARCH=""], [iOS], [MULTIARCH=""], @@ -1578,7 +2353,7 @@ index cf25148bad2..7ab0609bf8a 100644 [FreeBSD*], [MULTIARCH=""], [MULTIARCH=$($CC --print-multiarch 2>/dev/null)] ) -@@ -1193,7 +1386,7 @@ +@@ -1193,7 +1464,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], @@ -1587,8 +2362,12 @@ index cf25148bad2..7ab0609bf8a 100644 [SOABI_PLATFORM=$PLATFORM_TRIPLET] ) -@@ -1227,6 +1420,12 @@ +@@ -1225,8 +1496,16 @@ + [powerpc64le-*-linux-gnu/clang], [PY_SUPPORT_TIER=3], dnl Linux on PPC64 little endian, glibc, clang + [s390x-*-linux-gnu/gcc], [PY_SUPPORT_TIER=3], dnl Linux on 64bit s390x (big endian), glibc, gcc [x86_64-*-freebsd*/clang], [PY_SUPPORT_TIER=3], dnl FreeBSD on AMD64 ++ [aarch64-apple-ios*-macabi/clang], [PY_SUPPORT_TIER=3], dnl MacCatalyst on arm64 ++ [x86_64-apple-ios*-macabi/clang], [PY_SUPPORT_TIER=3], dnl MacCatalyst on x86_64 [aarch64-apple-ios*-simulator/clang], [PY_SUPPORT_TIER=3], dnl iOS Simulator on arm64 [aarch64-apple-ios*/clang], [PY_SUPPORT_TIER=3], dnl iOS on ARM64 + [aarch64-apple-tvos*-simulator/clang], [PY_SUPPORT_TIER=3], dnl tvOS Simulator on arm64 @@ -1600,7 +2379,7 @@ index cf25148bad2..7ab0609bf8a 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 -@@ -1536,7 +1735,7 @@ +@@ -1536,7 +1815,7 @@ case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; @@ -1609,7 +2388,7 @@ index cf25148bad2..7ab0609bf8a 100644 LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) AC_MSG_ERROR([Unknown platform for framework build]);; -@@ -1601,7 +1800,7 @@ +@@ -1601,7 +1880,7 @@ BLDLIBRARY='-L. -lpython$(LDVERSION)' RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} ;; @@ -1618,7 +2397,7 @@ index cf25148bad2..7ab0609bf8a 100644 LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) -@@ -3470,7 +3669,7 @@ +@@ -3470,7 +3749,7 @@ BLDSHARED="$LDSHARED" fi ;; @@ -1627,7 +2406,7 @@ index cf25148bad2..7ab0609bf8a 100644 LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" -@@ -3594,7 +3793,7 @@ +@@ -3594,7 +3873,7 @@ Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; # -u libsys_s pulls in all symbols in libsys @@ -1636,7 +2415,7 @@ index cf25148bad2..7ab0609bf8a 100644 LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -3618,7 +3817,7 @@ +@@ -3618,7 +3897,7 @@ LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" @@ -1645,7 +2424,7 @@ index cf25148bad2..7ab0609bf8a 100644 LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; -@@ -4106,7 +4305,7 @@ +@@ -4106,7 +4385,7 @@ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], @@ -1654,7 +2433,7 @@ index cf25148bad2..7ab0609bf8a 100644 ctypes_malloc_closure=yes ], [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] -@@ -5215,9 +5414,9 @@ +@@ -5215,9 +5494,9 @@ # checks for library functions AC_CHECK_FUNCS([ \ accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ @@ -1666,7 +2445,7 @@ index cf25148bad2..7ab0609bf8a 100644 gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \ getgrnam_r getgrouplist gethostname getitimer getloadavg getlogin \ getpeername getpgid getpid getppid getpriority _getpty \ -@@ -5225,8 +5424,7 @@ +@@ -5225,8 +5504,7 @@ getspnam getuid getwd grantpt if_nameindex initgroups kill killpg lchown linkat \ lockf lstat lutimes madvise mbrtowc memrchr mkdirat mkfifo mkfifoat \ mknod mknodat mktime mmap mremap nice openat opendir pathconf pause pipe \ @@ -1676,7 +2455,7 @@ index cf25148bad2..7ab0609bf8a 100644 pread preadv preadv2 process_vm_readv \ pthread_cond_timedwait_relative_np pthread_condattr_setclock pthread_init \ pthread_kill pthread_get_name_np pthread_getname_np pthread_set_name_np -@@ -5236,7 +5434,7 @@ +@@ -5236,7 +5514,7 @@ sched_setparam sched_setscheduler sem_clockwait sem_getvalue sem_open \ sem_timedwait sem_unlink sendfile setegid seteuid setgid sethostname \ setitimer setlocale setpgid setpgrp setpriority setregid setresgid \ @@ -1685,7 +2464,7 @@ index cf25148bad2..7ab0609bf8a 100644 sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ -@@ -5251,12 +5449,20 @@ +@@ -5251,12 +5529,20 @@ AC_CHECK_FUNCS([lchmod]) fi @@ -1709,7 +2488,7 @@ index cf25148bad2..7ab0609bf8a 100644 fi AC_CHECK_DECL([dirfd], -@@ -5539,20 +5745,22 @@ +@@ -5539,20 +5825,22 @@ ]) # check for openpty, login_tty, and forkpty @@ -1746,7 +2525,7 @@ index cf25148bad2..7ab0609bf8a 100644 # check for long file support functions AC_CHECK_FUNCS([fseek64 fseeko fstatvfs ftell64 ftello statvfs]) -@@ -5591,10 +5799,10 @@ +@@ -5591,10 +5879,10 @@ ]) ]) @@ -1759,7 +2538,7 @@ index cf25148bad2..7ab0609bf8a 100644 then AC_CHECK_FUNCS([clock_settime], [], [ AC_CHECK_LIB([rt], [clock_settime], [ -@@ -5752,7 +5960,7 @@ +@@ -5752,7 +6040,7 @@ [ac_cv_buggy_getaddrinfo=no], [ac_cv_buggy_getaddrinfo=yes], [ @@ -1768,7 +2547,7 @@ index cf25148bad2..7ab0609bf8a 100644 ac_cv_buggy_getaddrinfo="no" elif test "${enable_ipv6+set}" = set; then ac_cv_buggy_getaddrinfo="no -- configured with --(en|dis)able-ipv6" -@@ -6345,8 +6553,8 @@ +@@ -6345,8 +6633,8 @@ LIBPYTHON="\$(BLDLIBRARY)" fi @@ -1779,7 +2558,7 @@ index cf25148bad2..7ab0609bf8a 100644 MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi -@@ -7005,7 +7213,7 @@ +@@ -7005,7 +7293,7 @@ dnl NOTE: Inform user how to proceed with files when cross compiling. dnl Some cross-compile builds are predictable; they won't ever dnl have /dev/ptmx or /dev/ptc, so we can set them explicitly. @@ -1788,7 +2567,7 @@ index cf25148bad2..7ab0609bf8a 100644 ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else -@@ -7307,7 +7515,7 @@ +@@ -7307,7 +7595,7 @@ AS_CASE([$ac_sys_system], [Emscripten], [with_ensurepip=no], [WASI], [with_ensurepip=no], @@ -1797,7 +2576,7 @@ index cf25148bad2..7ab0609bf8a 100644 [with_ensurepip=upgrade] ) ]) -@@ -7694,7 +7902,7 @@ +@@ -7694,7 +7982,7 @@ SunOS*) _PYTHREAD_NAME_MAXLEN=31;; NetBSD*) _PYTHREAD_NAME_MAXLEN=15;; # gh-131268 Darwin) _PYTHREAD_NAME_MAXLEN=63;; @@ -1806,7 +2585,7 @@ index cf25148bad2..7ab0609bf8a 100644 FreeBSD*) _PYTHREAD_NAME_MAXLEN=19;; # gh-131268 OpenBSD*) _PYTHREAD_NAME_MAXLEN=23;; # gh-131268 *) _PYTHREAD_NAME_MAXLEN=;; -@@ -7719,7 +7927,7 @@ +@@ -7719,18 +8007,22 @@ [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], dnl The _scproxy module is available on macOS [Darwin], [], @@ -1815,6 +2594,22 @@ index cf25148bad2..7ab0609bf8a 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 + dnl Stub implementations are provided for pwd, grp etc APIs ++ dnl subprocess is however supported for Mac Catalyst ++ case "$_host_device" in ++ macabi) ;; ++ *) PY_STDLIB_MOD_SET_NA([_posixsubprocess]) ;; ++ esac + PY_STDLIB_MOD_SET_NA( + [_curses], + [_curses_panel], + [_gdbm], + [_multiprocessing], + [_posixshmem], +- [_posixsubprocess], + [_scproxy], + [_tkinter], + [grp], diff --git a/iOS/Resources/Info.plist.in b/iOS/Resources/Info.plist.in index c3e261ecd9e..26ef7a95de4 100644 --- a/iOS/Resources/Info.plist.in @@ -1835,11 +2630,62 @@ index c3e261ecd9e..26ef7a95de4 100644 CFBundleSupportedPlatforms iPhoneOS +diff --git a/iOS/testbed/Python.xcframework/Info.plist b/iOS/testbed/Python.xcframework/Info.plist +index c6418de6e74..7b32df19ab4 100644 +--- a/iOS/testbed/Python.xcframework/Info.plist ++++ b/iOS/testbed/Python.xcframework/Info.plist +@@ -4,6 +4,23 @@ + + AvailableLibraries + ++ ++ BinaryPath ++ Python.framework/Versions/Latest/Python ++ LibraryIdentifier ++ ios-arm64_x86_64-maccatalyst ++ LibraryPath ++ python.framework ++ SupportedArchitectures ++ ++ x86_64 ++ arm64 ++ ++ SupportedPlatform ++ ios ++ SupportedPlatformVariant ++ maccatalyst ++ + + BinaryPath + Python.framework/Python +--- /dev/null ++++ b/iOS/testbed/Python.xcframework/ios-arm64_x86_64-maccatalyst/README +@@ -0,0 +1,4 @@ ++This directory is intentionally empty. ++ ++It is a placeholder slice for the XCFramework on Mac Catalyst, ++to install or copy your built framework to. diff --git a/iOS/testbed/__main__.py b/iOS/testbed/__main__.py -index c05497ede3a..1146bf3b988 100644 +index c05497ede3a..2bee1f54206 100644 --- a/iOS/testbed/__main__.py +++ b/iOS/testbed/__main__.py -@@ -127,7 +127,7 @@ +@@ -28,6 +28,15 @@ + r"\s+\(Python\)\s" # Logger name + ) + ++# Prefix: 2025-04-27 21:43:38.530606-0500 iOSTestbed[96892:48053672] ++CATALYST_LOG_PREFIX_REGEX = re.compile( ++ r"^\d{4}-\d{2}-\d{2}" # YYYY-MM-DD ++ r"\s+\d{2}:\d{2}:\d{2}\.\d{6}" # HH:MM:SS.ssssss (microseconds, 6 digits) ++ r"[-+]\d{4}" # Timezone offset like -0500 ++ r"\s+iOSTestbed\[\d+:\d+\] " # iOSTestbed[ProcessID:ThreadID] (both numbers), then a space ++) ++ ++ + + # Work around a bug involving sys.exit and TaskGroups + # (https://github.com/python/cpython/issues/101515). +@@ -127,7 +136,7 @@ async def select_simulator_device(): # List the testing simulators, in JSON format raw_json = await async_check_output( @@ -1848,6 +2694,412 @@ index c05497ede3a..1146bf3b988 100644 ) json_data = json.loads(raw_json) +@@ -243,9 +252,13 @@ + sys.stdout.flush() + + +-async def xcode_test(location, simulator, verbose): ++async def xcode_test(location, simulator, verbose, catalyst): + # Run the test suite on the named simulator + print("Starting xcodebuild...", flush=True) ++ if catalyst: ++ destination_arg = "platform=macOS,variant=Mac Catalyst" ++ else: ++ destination_arg = f"platform=iOS Simulator,name={simulator}"; + args = [ + "xcodebuild", + "test", +@@ -254,13 +267,13 @@ + "-scheme", + "iOSTestbed", + "-destination", +- f"platform=iOS Simulator,name={simulator}", ++ destination_arg, + "-resultBundlePath", + str(location / f"{datetime.now():%Y%m%d-%H%M%S}.xcresult"), + "-derivedDataPath", + str(location / "DerivedData"), + ] +- if not verbose: ++ if not verbose and not catalyst: + args += ["-quiet"] + + async with async_process( +@@ -269,8 +282,16 @@ + stderr=subprocess.STDOUT, + ) as process: + while line := (await process.stdout.readline()).decode(*DECODE_ARGS): +- sys.stdout.write(line) +- sys.stdout.flush() ++ # For Mac Catalyst, the *actual* logs are streamed here. Only stream ++ # things that does NOT come from the process when verbose. ++ if catalyst: ++ if CATALYST_LOG_PREFIX_REGEX.match(line) or verbose: ++ line = CATALYST_LOG_PREFIX_REGEX.sub("", line) ++ sys.stdout.write(line) ++ sys.stdout.flush() ++ else: ++ sys.stdout.write(line) ++ sys.stdout.flush() + + status = await asyncio.wait_for(process.wait(), timeout=1) + exit(status) +@@ -281,20 +302,31 @@ + target: Path, + framework: Path, + apps: list[Path], ++ catalyst: bool, + ) -> 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) ++ if catalyst: ++ if not ( ++ source / "Python.xcframework/ios-arm64_x86_64-maccatalyst/Python.framework/Versions" ++ ).is_dir(): ++ print( ++ f"The testbed being cloned ({source}) does not contain " ++ f"a Mac Catalyst framework. Re-run with --framework" ++ ) ++ sys.exit(11) ++ else: ++ 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.") +@@ -305,7 +337,7 @@ + ): + print( + f"{framework} is not an XCframework, " +- f"or a simulator slice of a framework build." ++ f"or a simulator / Catalyst slice of a framework build." + ) + sys.exit(13) + +@@ -315,7 +347,10 @@ + print(" done") + + xc_framework_path = target / "Python.xcframework" +- sim_framework_path = xc_framework_path / "ios-arm64_x86_64-simulator" ++ if catalyst: ++ sim_framework_path = xc_framework_path / "ios-arm64_x86_64-maccatalyst" ++ else: ++ 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) +@@ -328,7 +363,7 @@ + ) + print(" done") + else: +- print(" Installing simulator framework...", end="", flush=True) ++ print(" Installing simulator/catalyst framework...", end="", flush=True) + if sim_framework_path.is_dir(): + shutil.rmtree(sim_framework_path) + else: +@@ -360,7 +395,7 @@ + sim_framework_path.is_symlink() + and not sim_framework_path.readlink().is_absolute() + ): +- print(" Rewriting symlink to simulator framework...", end="", flush=True) ++ print(" Rewriting symlink to simulator/catalyst framework...", end="", flush=True) + # Simulator framework is a relative symlink. Rewrite the symlink + # relative to the new location. + orig_sim_framework_path = ( +@@ -401,42 +436,52 @@ + plistlib.dump(info, f) + + +-async def run_testbed(simulator: str | None, args: list[str], verbose: bool=False): ++async def run_testbed(simulator: str | None, args: list[str], catalyst: bool, verbose: bool=False): + location = Path(__file__).parent + print("Updating plist...", end="", flush=True) + update_plist(location, args) + print(" done.", flush=True) + +- if simulator is None: +- simulator = await select_simulator_device() +- print(f"Running test on {simulator}", flush=True) +- +- # We need to get an exclusive lock on simulator creation, to avoid issues +- # with multiple simulators starting and being unable to tell which +- # simulator is due to which testbed instance. See +- # https://github.com/python/cpython/issues/130294 for details. Wait up to +- # 10 minutes for a simulator to boot. +- print("Obtaining lock on simulator creation...", flush=True) +- simulator_lock = SimulatorLock(timeout=10*60) +- await simulator_lock.acquire() +- print("Simulator lock acquired.", flush=True) +- +- # Get the list of devices that are booted at the start of the test run. +- # The simulator started by the test suite will be detected as the new +- # entry that appears on the device list. +- initial_devices = await list_devices() +- +- try: +- async with asyncio.TaskGroup() as tg: +- tg.create_task(log_stream_task(initial_devices, simulator_lock)) +- tg.create_task(xcode_test(location, simulator=simulator, verbose=verbose)) +- except* MySystemExit as e: +- raise SystemExit(*e.exceptions[0].args) from None +- except* subprocess.CalledProcessError as e: +- # Extract it from the ExceptionGroup so it can be handled by `main`. +- raise e.exceptions[0] +- finally: +- simulator_lock.release() ++ if not catalyst: ++ if simulator is None: ++ simulator = await select_simulator_device() ++ print(f"Running test on {simulator}", flush=True) ++ ++ # We need to get an exclusive lock on simulator creation, to avoid issues ++ # with multiple simulators starting and being unable to tell which ++ # simulator is due to which testbed instance. See ++ # https://github.com/python/cpython/issues/130294 for details. Wait up to ++ # 10 minutes for a simulator to boot. ++ print("Obtaining lock on simulator creation...", flush=True) ++ simulator_lock = SimulatorLock(timeout=10*60) ++ await simulator_lock.acquire() ++ print("Simulator lock acquired.", flush=True) ++ ++ # Get the list of devices that are booted at the start of the test run. ++ # The simulator started by the test suite will be detected as the new ++ # entry that appears on the device list. ++ initial_devices = await list_devices() ++ ++ try: ++ async with asyncio.TaskGroup() as tg: ++ tg.create_task(log_stream_task(initial_devices, simulator_lock)) ++ tg.create_task(xcode_test(location, simulator=simulator, verbose=verbose, catalyst=False)) ++ except* MySystemExit as e: ++ raise SystemExit(*e.exceptions[0].args) from None ++ except* subprocess.CalledProcessError as e: ++ # Extract it from the ExceptionGroup so it can be handled by `main`. ++ raise e.exceptions[0] ++ finally: ++ simulator_lock.release() ++ else: ++ try: ++ async with asyncio.TaskGroup() as tg: ++ tg.create_task(xcode_test(location, simulator="", verbose=verbose, catalyst=True)) ++ 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(): +@@ -448,6 +493,12 @@ + + subcommands = parser.add_subparsers(dest="subcommand") + ++ parser.add_argument( ++ "--catalyst", ++ action="store_true", ++ help="Use Mac Catalyst.", ++ ) ++ + clone = subcommands.add_parser( + "clone", + description=( +@@ -514,11 +565,16 @@ + target=Path(context.location).resolve(), + framework=Path(context.framework).resolve() if context.framework else None, + apps=[Path(app) for app in context.apps], ++ catalyst = context.catalyst + ) + elif context.subcommand == "run": + if test_args: ++ if context.catalyst: ++ expected_location = "Python.xcframework/ios-arm64_x86_64-maccatalyst/Python.framework" ++ else: ++ expected_location = "Python.xcframework/ios-arm64_x86_64-simulator/bin" + if not ( +- Path(__file__).parent / "Python.xcframework/ios-arm64_x86_64-simulator/bin" ++ Path(__file__).parent / expected_location + ).is_dir(): + print( + f"Testbed does not contain a compiled iOS framework. Use " +@@ -530,8 +586,10 @@ + asyncio.run( + run_testbed( + simulator=context.simulator, ++ # Mac catalyst requires verbose, or no logs will print. + verbose=context.verbose, + args=test_args, ++ catalyst=context.catalyst + ) + ) + else: +diff --git a/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj b/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj +index c7d63909ee2..ace08f7efd9 100644 +--- a/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj ++++ b/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj +@@ -70,6 +70,7 @@ + 607A66592B0F08600010BFC8 /* iOSTestbed-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "iOSTestbed-Info.plist"; sourceTree = ""; }; + 608619532CB77BA900F46182 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = ""; }; + 608619552CB7819B00F46182 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = ""; }; ++ EE325B2A2DBE97CD000142D0 /* iOSTestbed.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = iOSTestbed.entitlements; sourceTree = ""; }; + /* End PBXFileReference section */ + + /* Begin PBXFrameworksBuildPhase section */ +@@ -115,6 +116,7 @@ + 607A66142B0EFA380010BFC8 /* iOSTestbed */ = { + isa = PBXGroup; + children = ( ++ EE325B2A2DBE97CD000142D0 /* iOSTestbed.entitlements */, + 608619552CB7819B00F46182 /* app */, + 608619532CB77BA900F46182 /* app_packages */, + 607A66592B0F08600010BFC8 /* iOSTestbed-Info.plist */, +@@ -262,7 +264,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"; ++ shellScript = "set -e\n\nif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-iphonesimulator\" ]; then\n mkdir -p \"$CODESIGNING_FOLDER_PATH/python/lib\"\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/\" \nelif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-maccatalyst\" ]; then\n mkdir -p \"$CODESIGNING_FOLDER_PATH/Contents/Resources/python/lib\"\n echo \"Installing Python modules for Mac Catalyst\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64_x86_64-maccatalyst/Python.framework/Versions/3.14/lib/\" \"$CODESIGNING_FOLDER_PATH/Contents/Resources/python/lib/\"\nelse\n mkdir -p \"$CODESIGNING_FOLDER_PATH/python/lib\"\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 */ = { +@@ -282,7 +284,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"; ++ 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\nif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-maccatalyst\" ]; then\n PYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/Contents/Resources/python/lib\")\n find \"$CODESIGNING_FOLDER_PATH/Contents/Resources/python/lib/$PYTHON_VER/lib-dynload\" -name \"*.so\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der {} \\;\n find \"$CODESIGNING_FOLDER_PATH/Contents/Resources/app_packages\" -name \"*.so\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der {} \\;\n find \"$CODESIGNING_FOLDER_PATH/Contents/Resources/app\" -name \"*.so\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der {} \\;\n \nelse\n PYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/python/lib\")\n echo \"Install Python $PYTHON_VER standard library extension modules...\"\n find \"$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\"\n done\n echo \"Install app package extension modules...\"\n find \"$CODESIGNING_FOLDER_PATH/app_packages\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app_packages/ \"$FULL_EXT\"\n done\n echo \"Install app extension modules...\"\n find \"$CODESIGNING_FOLDER_PATH/app\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app/ \"$FULL_EXT\"\n done\n\n # Clean up dylib template \n rm -f \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\"\n echo \"Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)...\"\n find \"$CODESIGNING_FOLDER_PATH/Frameworks\" -name \"*.framework\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der \"{}\" \\; \nfi\n"; + showEnvVarsInLog = 0; + }; + /* End PBXShellScriptBuildPhase section */ +@@ -362,6 +364,7 @@ + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; ++ ENABLE_HARDENED_RUNTIME = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; +@@ -381,6 +384,7 @@ + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; ++ MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; +@@ -423,6 +427,7 @@ + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ++ ENABLE_HARDENED_RUNTIME = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; +@@ -436,6 +441,7 @@ + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; ++ MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; +@@ -449,9 +455,12 @@ + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; ++ CODE_SIGN_ENTITLEMENTS = iOSTestbed/iOSTestbed.entitlements; ++ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; ++ ENABLE_HARDENED_RUNTIME = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; + INFOPLIST_FILE = "iOSTestbed/iOSTestbed-Info.plist"; +@@ -468,6 +477,9 @@ + MARKETING_VERSION = 3.13.0a1; + PRODUCT_BUNDLE_IDENTIFIER = org.python.iOSTestbed; + PRODUCT_NAME = "$(TARGET_NAME)"; ++ PROVISIONING_PROFILE_SPECIFIER = ""; ++ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; ++ SUPPORTS_MACCATALYST = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; +@@ -479,9 +491,12 @@ + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; ++ CODE_SIGN_ENTITLEMENTS = iOSTestbed/iOSTestbed.entitlements; ++ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; ++ ENABLE_HARDENED_RUNTIME = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; +@@ -499,6 +514,9 @@ + MARKETING_VERSION = 3.13.0a1; + PRODUCT_BUNDLE_IDENTIFIER = org.python.iOSTestbed; + PRODUCT_NAME = "$(TARGET_NAME)"; ++ PROVISIONING_PROFILE_SPECIFIER = ""; ++ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; ++ SUPPORTS_MACCATALYST = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; +@@ -509,9 +527,10 @@ + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; ++ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; +- DEVELOPMENT_TEAM = 3HEZE76D99; ++ DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; +@@ -529,9 +548,10 @@ + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; ++ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; +- DEVELOPMENT_TEAM = 3HEZE76D99; ++ DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; +--- /dev/null ++++ b/iOS/testbed/iOSTestbed/iOSTestbed.entitlements +@@ -0,0 +1,8 @@ ++ ++ ++ ++ ++ com.apple.security.cs.disable-library-validation ++ ++ ++ --- /dev/null +++ b/tvOS/README.rst @@ -0,0 +1,108 @@ diff --git a/patch/Python/release.MacCatalyst.exclude b/patch/Python/release.MacCatalyst.exclude new file mode 100644 index 0000000..f1d0f75 --- /dev/null +++ b/patch/Python/release.MacCatalyst.exclude @@ -0,0 +1,6 @@ +# This is a list of support package path patterns that we exclude +# from all Python-Apple-support tarballs. +# It is used by `tar -X` during the Makefile build. +# Remove pyc files. These take up space, but since most stdlib modules are +# never imported by user code, they mostly have no value. +*/__pycache__