|
38 | 38 |
|
39 | 39 | _LOG = None # open file handle; set via set_log() |
40 | 40 |
|
| 41 | +# Minimum iOS version for every slice. Xcode 27 rejects deployment targets below 15.0, |
| 42 | +# and Flutter 3.47 / Flet 1.1.0 use the same floor. CPython's configure hardcodes |
| 43 | +# 13.0 as its default (see build()), so it's rewritten to this value. |
| 44 | +IOS_DEPLOYMENT_TARGET = "15.0" |
| 45 | + |
41 | 46 |
|
42 | 47 | def set_log(path: Path) -> None: |
43 | 48 | """Tee all subsequent run()/_emit output to a build log file.""" |
@@ -139,14 +144,29 @@ def build( |
139 | 144 | # and upstream's iOS PY_STDLIB_MOD_SET_NA (3.14+); a no-op if the string isn't |
140 | 145 | # present. |
141 | 146 | configure = src / "configure" |
142 | | - configure.write_text( |
| 147 | + configure_text = ( |
143 | 148 | configure.read_text() |
144 | 149 | .replace( |
145 | 150 | "py_cv_module__multiprocessing=n/a", "py_cv_module__multiprocessing=yes" |
146 | 151 | ) |
147 | 152 | .replace("py_cv_module__posixshmem=n/a", "py_cv_module__posixshmem=yes") |
148 | 153 | ) |
149 | 154 |
|
| 155 | + # Raise the iOS deployment target. The Apple tool configures an unversioned |
| 156 | + # --host=arm64-apple-ios, so configure falls back to its hardcoded 13.0 default |
| 157 | + # (and overwrites IPHONEOS_DEPLOYMENT_TARGET from the environment), then derives |
| 158 | + # -mios[-simulator]-version-min, the framework's MinimumOSVersion and the |
| 159 | + # sysconfig platform tag from it. The same line exists in 3.14+ upstream, 3.13 |
| 160 | + # upstream and the 3.12 back-port patch; fail loudly if upstream ever changes it. |
| 161 | + default_target = "IPHONEOS_DEPLOYMENT_TARGET:=13.0}" |
| 162 | + if configure_text.count(default_target) != 1: |
| 163 | + raise SystemExit(f"Expected exactly one {default_target!r} in {configure}") |
| 164 | + configure.write_text( |
| 165 | + configure_text.replace( |
| 166 | + default_target, f"IPHONEOS_DEPLOYMENT_TARGET:={IOS_DEPLOYMENT_TARGET}}}" |
| 167 | + ) |
| 168 | + ) |
| 169 | + |
150 | 170 | # iOS's SDK declares pipe2/dup3 in headers but doesn't provide them, and on recent |
151 | 171 | # SDKs configure mis-detects them (esp. the simulator), then the build fails with |
152 | 172 | # "call to undeclared function 'pipe2'". The Apple tool doesn't expose configure |
@@ -182,8 +202,8 @@ def _strip(paths: list) -> None: |
182 | 202 |
|
183 | 203 | def _embed_ios_version_in_host_triple(sysconfig_path: Path) -> None: |
184 | 204 | """Rewrite HOST_GNU_TYPE in a _sysconfigdata file to embed IPHONEOS_DEPLOYMENT_TARGET |
185 | | - (e.g. ``aarch64-apple-ios`` -> ``aarch64-apple-ios13.0``, |
186 | | - ``...-ios-simulator`` -> ``...-ios13.0-simulator``).""" |
| 205 | + (e.g. ``aarch64-apple-ios`` -> ``aarch64-apple-ios15.0``, |
| 206 | + ``...-ios-simulator`` -> ``...-ios15.0-simulator``).""" |
187 | 207 | text = sysconfig_path.read_text() |
188 | 208 | m = re.search(r"'IPHONEOS_DEPLOYMENT_TARGET':\s*'([^']+)'", text) |
189 | 209 | if not m: |
@@ -235,11 +255,24 @@ def reshape(version: str, short: str, src: Path, root: Path) -> None: |
235 | 255 | # the _sysconfigdata lacks the iOS deployment version. mobile-forge's crossenv recovers |
236 | 256 | # the iOS release by splitting HOST_GNU_TYPE (when _PYTHON_HOST_PLATFORM isn't set), so a |
237 | 257 | # version-less triple yields an empty release and pip's iOS-tag code crashes. Embed the |
238 | | - # version (e.g. aarch64-apple-ios -> aarch64-apple-ios13.0), matching beeware. Fixing the |
| 258 | + # version (e.g. aarch64-apple-ios -> aarch64-apple-ios15.0), matching beeware. Fixing the |
239 | 259 | # xcframework copies here means the dart-install + platform-config copies inherit it. |
240 | 260 | for sc in dst_xcf.glob(f"*/lib-*/{pyver}/_sysconfigdata__ios_*.py"): |
241 | 261 | _embed_ios_version_in_host_triple(sc) |
242 | 262 |
|
| 263 | + # The Apple tool also ships its testbed's dylib Info.plist template inside the |
| 264 | + # xcframework (build/iOS-dylib-Info-template.plist), with MinimumOSVersion |
| 265 | + # hardcoded to 13.0. Keep it consistent with the slices. |
| 266 | + dylib_plist_template = dst_xcf / "build" / "iOS-dylib-Info-template.plist" |
| 267 | + if dylib_plist_template.is_file(): |
| 268 | + dylib_plist_template.write_text( |
| 269 | + re.sub( |
| 270 | + r"(<key>MinimumOSVersion</key>\s*<string>)[^<]*(</string>)", |
| 271 | + rf"\g<1>{IOS_DEPLOYMENT_TARGET}\g<2>", |
| 272 | + dylib_plist_template.read_text(), |
| 273 | + ) |
| 274 | + ) |
| 275 | + |
243 | 276 | shared_stdlib = dst_xcf / "lib" / pyver |
244 | 277 | if not (shared_stdlib / "os.py").exists(): |
245 | 278 | raise SystemExit(f"Expected shared stdlib at {shared_stdlib}") |
@@ -277,7 +310,11 @@ def reshape(version: str, short: str, src: Path, root: Path) -> None: |
277 | 310 |
|
278 | 311 | support = root / "support" / short / "iOS" |
279 | 312 | support.mkdir(parents=True, exist_ok=True) |
280 | | - lines = [f"Python version: {version}", "Build: custom", "Min iOS version: 13.0"] |
| 313 | + lines = [ |
| 314 | + f"Python version: {version}", |
| 315 | + "Build: custom", |
| 316 | + f"Min iOS version: {IOS_DEPLOYMENT_TARGET}", |
| 317 | + ] |
281 | 318 | if deps: |
282 | 319 | lines.append("---------------------") |
283 | 320 | lines += [f"{name}: {ver}" for name, ver in sorted(deps.items())] |
|
0 commit comments