Stamp Xcode build-provenance keys into generated framework Info.plists - #36
Merged
Merged
Conversation
create_plist() emitted ten hand-written keys and not a single DT* key, so the frameworks built from lib-dynload .so's did not look to Apple's tooling like anything Xcode had produced. Now emits the same key set Xcode stamps into a real framework: BuildMachineOSBuild, DTCompiler, DTPlatformBuild, DTPlatformName, DTPlatformVersion, DTSDKBuild, DTSDKName, DTXcode, DTXcodeBuild, UIDeviceFamily and (device slice only) UIRequiredDeviceCapabilities. Values are resolved once per run from the SDK actually in use — sw_vers, xcrun --show-sdk-version/--show-sdk-build-version, and xcodebuild -version. Also fixes the simulator slice, which previously claimed CFBundleSupportedPlatforms=iPhoneOS. create_plist() now takes the platform as its fourth argument and labels each slice correctly. Context: flet-dev/flet#6724 (ITMS-91065 on _ssl/_hashlib). The missing DT* keys are the most substantive difference we could find between these frameworks and third-party OpenSSL xcframeworks the App Store accepts. Whether that is what Apple's scan reads is unverified — a source signature was ruled out experimentally (codesign -f at export destroys everything but the identifier) and the privacy manifest was ruled out (the accepted third-party package ships an empty stub).
FeodorFitsner
added a commit
to flet-dev/serious-python
that referenced
this pull request
Jul 27, 2026
…ts (#242) * Bump to 4.4.2: Xcode build-provenance keys in iOS framework Info.plists Re-pins the bundled python-build snapshot to 20260727, which stamps the DT* build-provenance keys Xcode writes into a real framework's Info.plist into every framework generated from a lib-dynload .so, and labels the simulator slice as iPhoneSimulator instead of iPhoneOS (flet-dev/python-build#36). No versions moved: Python 3.12.13 / 3.13.14 / 3.14.6, Pyodide, and dart_bridge 1.6.1 are all unchanged from 20260726. This is the next hypothesis under test for the ITMS-91065 rejection in flet-dev/flet#6724, not a confirmed fix — see the serious_python_darwin 4.4.2 entry for what was ruled out experimentally and what remains open. * iOS: namespace framework bundle identifiers under the host app Every Python C-extension ships as its own embedded framework, and each carried a fixed `org.python.<module>` CFBundleIdentifier -- org.python.ssl, org.python.hashlib, ... -- byte-identical in every app ever built with serious_python. A framework's bundle identifier also becomes its CODE SIGNING identifier, and that is the one field that survives the `codesign -f` Xcode applies at embed and again at exportArchive (everything else -- certificate chain, team identifier, secure timestamp -- is destroyed). A globally-shared identifier on a framework Apple fingerprints as a listed third-party SDK is the leading explanation for the ITMS-91065 rejection in flet-dev/flet#6724. Setting SERIOUS_PYTHON_BUNDLE_ID rewrites them to `<app id>.<module>` with underscores mapped to hyphens, matching CPython's own iOS support (Platforms/Apple/testbed/Python.xcframework/build/utils.sh). The leading hyphen for underscore-prefixed modules (`_ssl` -> `<app>.-ssl`) is deliberate: it is what keeps `_ssl` distinct from `ssl`, and it is the form CPython ships. The rewrite runs before reconcile_framework_install_names, whose ad-hoc re-sign reseals the modified plists; the stdlib xcframeworks are unsigned at that point so editing them invalidates nothing. Module names come from arbitrary wheels, so anything outside CFBundleIdentifier's [A-Za-z0-9.-] is mapped to a hyphen, and an invalid SERIOUS_PYTHON_BUNDLE_ID leaves the defaults in place with a warning rather than emitting a plist that fails late at export. flet passes the value from flet-dev/flet#6731. * iOS: namespace Python.framework's bundle id too Python.xcframework is staged straight out of dist/xcframeworks by stage_spm.sh and vendored from there by the podspec, so it never passes through site-xcframeworks and kept a shared `org.python.python` -- identical in every app, the same class of identifier the previous commit fixed for the extension frameworks. Verified inert at runtime before renaming: `org.python.python` appears in no shipped binary, and neither Python.framework nor App.framework contains a CFBundleGetBundleWithIdentifier / bundleWithIdentifier lookup. serious_python sets PYTHONHOME explicitly and resolves extensions through the .fwork/.origin path mechanism, not bundle identifiers. rewrite_framework_bundle_ids takes an optional skip list so dart_bridge keeps `dev.flet.dartbridge` -- a vendor-owned reverse-DNS identifier is the shape we're aiming for, not the problem we're fixing. Confirmed against a real build (flet playground abc1.xcarchive): 93 of 98 frameworks were already namespaced, no duplicate identifiers; this covers Python.framework, leaving only Flutter's own three (App, Flutter, objective_c) and dart_bridge. * examples: pin jni to 1.0.0 (1.0.1 breaks aarch64 Linux builds) jni 1.0.1, published 2026-07-27T01:10Z, added global_jni_env.c, which passes a `va_list` where a `void *` is expected. On x86_64 va_list is an array type that decays to a pointer so it compiles; on aarch64 it is a struct, so it is a hard error and `flutter build linux` fails with 19 of them. Tracked upstream at dart-lang/native#3498. This surfaced on the release PR rather than as a scheduled failure because the examples depend on the serious_python packages by path: bumping their version invalidates the committed pubspec.lock, pub re-resolves the whole graph, and picks up the newest transitive versions. Any PR touching package versions after 2026-07-27T01:10Z would have hit it. jni itself arrives via path_provider -> path_provider_android -> jni_flutter -> jni. jni_flutter 1.0.1 declares `jni: ^1.0.0`, so 1.0.0 satisfies it -- the override narrows within the allowed range rather than overriding a constraint. Applied to all three examples; only bridge_example runs in CI, but the other two break identically for anyone building them on an ARM64 Linux host. The flask_example / run_example locks also pick up the current path-dependency version (4.0.0 -> 4.4.1); they were simply stale.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
create_plist()indarwin/xcframework_utils.shwrote ten hand-authored keys and not a singleDT*key. To Apple's static analysis, the frameworks we build fromlib-dynload.sos don't look like anything Xcode produced.It now emits the same key set Xcode stamps into a real framework, resolved once per run from the SDK actually in use (
sw_vers -buildVersion,xcrun --sdk iphoneos --show-sdk-version/--show-sdk-build-version,xcodebuild -version):The generated key set is now identical to krzyzanowskim/OpenSSL's
OpenSSL.framework— verified by diffing both plists.Also fixes a latent bug: the simulator slice previously claimed
CFBundleSupportedPlatforms=iPhoneOS.create_plist()takes the platform as a fourth argument (defaultiphoneos) and now labels each slice correctly —iPhoneSimulator/DTPlatformName=iphonesimulator/DTSDKName=iphonesimulator26.5, and noarm64device capability on a fat arm64+x86_64 binary.Why
flet-dev/flet#6724 —
ITMS-91065: Missing signatureon_ssl.frameworkand_hashlib.frameworkfor a first App Store submission.These missing keys are the most substantive difference we could find between our frameworks and third-party OpenSSL xcframeworks Apple accepts. The other candidates were ruled out by experiment:
exportArchivedoes (codesign -f --preserve-metadata=identifier,flags,runtime,launch-constraints,library-constraints) destroys the certificate chain, team identifier and secure timestamp; only the identifier string survives, and that's derived fromCFBundleIdentifierfor an unsigned framework anyway.NSPrivacyAccessedAPITypesas<array/>, and our frameworks already carry a manifest (Apple emits noITMS-91061alongside the rejection).Whether this is what Apple's scan reads is unverified. It's the cheapest remaining hypothesis and needs one App Store submission of a new app to test — the check only fires for new apps or updates that newly add a listed SDK, which is why existing Flet apps are unaffected.
Verification
Device slice output:
Needs a python-build release and a snapshot re-pin in
flet-dev/serious-pythonbefore it reaches an app build.