Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/build-python-version.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ on:
required: true
type: choice
options:
- 3.12.14
- 3.13.15
- 3.14.7
- 3.12.15
- 3.13.16
- 3.14.8

env:
PYTHON_VERSION: ${{ inputs.python_version || github.event.inputs.python_version }}
Expand Down
47 changes: 42 additions & 5 deletions darwin/build_ios.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@

_LOG = None # open file handle; set via set_log()

# Minimum iOS version for every slice. Xcode 27 rejects deployment targets below 15.0,
# and Flutter 3.47 / Flet 1.1.0 use the same floor. CPython's configure hardcodes
# 13.0 as its default (see build()), so it's rewritten to this value.
IOS_DEPLOYMENT_TARGET = "15.0"


def set_log(path: Path) -> None:
"""Tee all subsequent run()/_emit output to a build log file."""
Expand Down Expand Up @@ -139,14 +144,29 @@ def build(
# and upstream's iOS PY_STDLIB_MOD_SET_NA (3.14+); a no-op if the string isn't
# present.
configure = src / "configure"
configure.write_text(
configure_text = (
configure.read_text()
.replace(
"py_cv_module__multiprocessing=n/a", "py_cv_module__multiprocessing=yes"
)
.replace("py_cv_module__posixshmem=n/a", "py_cv_module__posixshmem=yes")
)

# Raise the iOS deployment target. The Apple tool configures an unversioned
# --host=arm64-apple-ios, so configure falls back to its hardcoded 13.0 default
# (and overwrites IPHONEOS_DEPLOYMENT_TARGET from the environment), then derives
# -mios[-simulator]-version-min, the framework's MinimumOSVersion and the
# sysconfig platform tag from it. The same line exists in 3.14+ upstream, 3.13
# upstream and the 3.12 back-port patch; fail loudly if upstream ever changes it.
default_target = "IPHONEOS_DEPLOYMENT_TARGET:=13.0}"
if configure_text.count(default_target) != 1:
raise SystemExit(f"Expected exactly one {default_target!r} in {configure}")
configure.write_text(
configure_text.replace(
default_target, f"IPHONEOS_DEPLOYMENT_TARGET:={IOS_DEPLOYMENT_TARGET}}}"
)
)

# iOS's SDK declares pipe2/dup3 in headers but doesn't provide them, and on recent
# SDKs configure mis-detects them (esp. the simulator), then the build fails with
# "call to undeclared function 'pipe2'". The Apple tool doesn't expose configure
Expand Down Expand Up @@ -182,8 +202,8 @@ def _strip(paths: list) -> None:

def _embed_ios_version_in_host_triple(sysconfig_path: Path) -> None:
"""Rewrite HOST_GNU_TYPE in a _sysconfigdata file to embed IPHONEOS_DEPLOYMENT_TARGET
(e.g. ``aarch64-apple-ios`` -> ``aarch64-apple-ios13.0``,
``...-ios-simulator`` -> ``...-ios13.0-simulator``)."""
(e.g. ``aarch64-apple-ios`` -> ``aarch64-apple-ios15.0``,
``...-ios-simulator`` -> ``...-ios15.0-simulator``)."""
text = sysconfig_path.read_text()
m = re.search(r"'IPHONEOS_DEPLOYMENT_TARGET':\s*'([^']+)'", text)
if not m:
Expand Down Expand Up @@ -235,11 +255,24 @@ def reshape(version: str, short: str, src: Path, root: Path) -> None:
# the _sysconfigdata lacks the iOS deployment version. mobile-forge's crossenv recovers
# the iOS release by splitting HOST_GNU_TYPE (when _PYTHON_HOST_PLATFORM isn't set), so a
# version-less triple yields an empty release and pip's iOS-tag code crashes. Embed the
# version (e.g. aarch64-apple-ios -> aarch64-apple-ios13.0), matching beeware. Fixing the
# version (e.g. aarch64-apple-ios -> aarch64-apple-ios15.0), matching beeware. Fixing the
# xcframework copies here means the dart-install + platform-config copies inherit it.
for sc in dst_xcf.glob(f"*/lib-*/{pyver}/_sysconfigdata__ios_*.py"):
_embed_ios_version_in_host_triple(sc)

# The Apple tool also ships its testbed's dylib Info.plist template inside the
# xcframework (build/iOS-dylib-Info-template.plist), with MinimumOSVersion
# hardcoded to 13.0. Keep it consistent with the slices.
dylib_plist_template = dst_xcf / "build" / "iOS-dylib-Info-template.plist"
if dylib_plist_template.is_file():
dylib_plist_template.write_text(
re.sub(
r"(<key>MinimumOSVersion</key>\s*<string>)[^<]*(</string>)",
rf"\g<1>{IOS_DEPLOYMENT_TARGET}\g<2>",
dylib_plist_template.read_text(),
)
)

shared_stdlib = dst_xcf / "lib" / pyver
if not (shared_stdlib / "os.py").exists():
raise SystemExit(f"Expected shared stdlib at {shared_stdlib}")
Expand Down Expand Up @@ -277,7 +310,11 @@ def reshape(version: str, short: str, src: Path, root: Path) -> None:

support = root / "support" / short / "iOS"
support.mkdir(parents=True, exist_ok=True)
lines = [f"Python version: {version}", "Build: custom", "Min iOS version: 13.0"]
lines = [
f"Python version: {version}",
"Build: custom",
f"Min iOS version: {IOS_DEPLOYMENT_TARGET}",
]
if deps:
lines.append("---------------------")
lines += [f"{name}: {ver}" for name, ver in sorted(deps.items())]
Expand Down
2 changes: 1 addition & 1 deletion darwin/build_macos.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
DEFAULT_XZ_VERSION = "5.8.3"
# zstd backs the _zstd module added in CPython 3.14 (PEP 784); built only for 3.14+.
DEFAULT_ZSTD_VERSION = "1.5.7"
DEFAULT_DEPLOYMENT_TARGET = "11.0"
DEFAULT_DEPLOYMENT_TARGET = "12.0"
ARCHES = ("x86_64", "arm64")

# A minimal PATH keeps Homebrew / user Python installs from leaking into the build.
Expand Down
47 changes: 47 additions & 0 deletions darwin/ios_patches/3.12/Python.patch
Original file line number Diff line number Diff line change
Expand Up @@ -9282,6 +9282,53 @@ index 7ca3dc62c01..a1e28beff42 100644
-rm -f python-config.py python-config

# Make things extra clean, before making a distribution:
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index 91ca136..364cf9e 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -1339,14 +1339,14 @@ _get_peer_alt_names (_sslmodulestate *state, X509 *certificate) {
}
PyTuple_SET_ITEM(t, 0, v);

- if (name->d.ip->length == 4) {
- unsigned char *p = name->d.ip->data;
+ if (ASN1_STRING_length(name->d.ip) == 4) {
+ const unsigned char *p = ASN1_STRING_get0_data(name->d.ip);
v = PyUnicode_FromFormat(
"%d.%d.%d.%d",
p[0], p[1], p[2], p[3]
);
- } else if (name->d.ip->length == 16) {
- unsigned char *p = name->d.ip->data;
+ } else if (ASN1_STRING_length(name->d.ip) == 16) {
+ const unsigned char *p = ASN1_STRING_get0_data(name->d.ip);
v = PyUnicode_FromFormat(
"%X:%X:%X:%X:%X:%X:%X:%X",
p[0] << 8 | p[1],
@@ -1477,8 +1477,9 @@ _get_aia_uri(X509 *certificate, int nid) {
continue;
}
uri = ad->location->d.uniformResourceIdentifier;
- ostr = PyUnicode_FromStringAndSize((char *)uri->data,
- uri->length);
+ ostr = PyUnicode_FromStringAndSize(
+ (const char *)ASN1_STRING_get0_data(uri),
+ ASN1_STRING_length(uri));
if (ostr == NULL) {
goto fail;
}
@@ -1544,8 +1545,9 @@ _get_crl_dp(X509 *certificate) {
continue;
}
uri = gn->d.uniformResourceIdentifier;
- ouri = PyUnicode_FromStringAndSize((char *)uri->data,
- uri->length);
+ ouri = PyUnicode_FromStringAndSize(
+ (const char *)ASN1_STRING_get0_data(uri),
+ ASN1_STRING_length(uri));
if (ouri == NULL)
goto done;

diff --git a/Modules/getpath.c b/Modules/getpath.c
index 0a310000751..83a2bc469ae 100644
--- a/Modules/getpath.c
Expand Down
24 changes: 15 additions & 9 deletions darwin/ios_patches/3.13/Python.patch
Original file line number Diff line number Diff line change
Expand Up @@ -7102,16 +7102,16 @@ index 0df4751..cf32adf 100755


# checks for alternative programs
@@ -4709,6 +4907,16 @@ case $ac_sys_system in #(
@@ -4714,6 +4912,16 @@ case $host in #(
as_fn_append CFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"
as_fn_append LDFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"
;; #(
+ tvOS) :
+ *-apple-tvos*) :
+
+ as_fn_append CFLAGS " -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}"
+ as_fn_append LDFLAGS " -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}"
+ ;; #(
+ watchOS) :
+ *-apple-watchos*) :
+
+ as_fn_append CFLAGS " -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}"
+ as_fn_append LDFLAGS " -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}"
Expand Down Expand Up @@ -7785,25 +7785,31 @@ index a016a43..698553b 100644

# checks for alternative programs

@@ -944,11 +1127,19 @@ AS_CASE([$host],
@@ -944,15 +1127,25 @@ AS_CASE([$host],
],
)

-dnl Add the compiler flag for the iOS minimum supported OS version.
+dnl Add the compiler flag for the iOS/tvOS/watchOS minimum supported OS
+dnl version. visionOS doesn't use an explicit -mxros-version-min option -
+dnl it encodes the min version into the target triple.
AS_CASE([$ac_sys_system],
[iOS], [
AS_CASE([$host],
[*-apple-ios*-simulator], [
AS_VAR_APPEND([CFLAGS], [" -mios-simulator-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"])
AS_VAR_APPEND([LDFLAGS], [" -mios-simulator-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"])
],
[*-apple-ios*], [
AS_VAR_APPEND([CFLAGS], [" -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"])
AS_VAR_APPEND([LDFLAGS], [" -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"])
+ ],[tvOS], [
],
+ [*-apple-tvos*], [
+ AS_VAR_APPEND([CFLAGS], [" -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}"])
+ AS_VAR_APPEND([LDFLAGS], [" -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}"])
+ ],[watchOS], [
+ ],
+ [*-apple-watchos*], [
+ AS_VAR_APPEND([CFLAGS], [" -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}"])
+ AS_VAR_APPEND([LDFLAGS], [" -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}"])
],
+ ],
)

@@ -1136,6 +1327,9 @@ AC_MSG_CHECKING([for multiarch])
Expand Down
8 changes: 8 additions & 0 deletions darwin/ios_patches/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,13 @@ When that happens:
built nor shipped — that is exactly why the 3.12 patch here has had its
`Lib/test/test_genericpath.py` hunk removed for 3.12.13).

The 3.12 patch also carries one fix that isn't about iOS: `Modules/_ssl.c` reads
`ASN1_STRING` fields through `ASN1_STRING_length()` / `ASN1_STRING_get0_data()` instead of
the struct members, as 3.13+ does upstream. OpenSSL 4 made `ASN1_STRING` opaque, and the
native build-python that the Apple tool compiles first picks up whatever OpenSSL Homebrew
links (`openssl@4` on current GitHub macOS runners), so without it the 3.12 build fails in
`_ssl.c`. The accessors exist since OpenSSL 1.1.0, so the iOS slices (OpenSSL 3.x) are
unaffected.

A minor-version bump (e.g. adding 3.15) means refreshing from the corresponding upstream
back-port branch.
2 changes: 1 addition & 1 deletion darwin/xcframework_utils.sh
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ create_plist() {
<key>DTXcodeBuild</key>
<string>$xcode_build</string>
<key>MinimumOSVersion</key>
<string>13.0</string>
<string>15.0</string>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
Expand Down
14 changes: 7 additions & 7 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
{
"default_python_version": "3.14",
"dart_bridge_version": "1.10.0",
"dart_bridge_version": "1.11.0",
"pythons": {
"3.12": {
"full_version": "3.12.14",
"standalone_release_date": "20260901",
"full_version": "3.12.15",
"standalone_release_date": "20261001",
"pyodide_version": "0.27.8",
"pyodide_platform_tag": "pyodide-2024.0-wasm32",
"android_abis": ["arm64-v8a", "x86_64", "armeabi-v7a"],
"prerelease": false
},
"3.13": {
"full_version": "3.13.15",
"standalone_release_date": "20260901",
"full_version": "3.13.16",
"standalone_release_date": "20261001",
"pyodide_version": "0.29.5",
"pyodide_platform_tag": "pyemscripten-2025.0-wasm32",
"android_abis": ["arm64-v8a", "x86_64", "armeabi-v7a"],
"prerelease": false
},
"3.14": {
"full_version": "3.14.7",
"standalone_release_date": "20260901",
"full_version": "3.14.8",
"standalone_release_date": "20261001",
"pyodide_version": "314.0.7",
"pyodide_platform_tag": "pyemscripten-2026.0-wasm32",
"android_abis": ["arm64-v8a", "x86_64", "armeabi-v7a"],
Expand Down
Loading