Skip to content
Open
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
4 changes: 4 additions & 0 deletions src/serious_python_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 4.1.1

* Fix the embedded interpreter crashing on startup on a **non-primary ABI** (e.g. an x86_64 emulator when `arm64-v8a` is the primary ABI) with `ModuleNotFoundError: No module named '_sysconfigdata__android_<arch>-linux-android'`. The ABI-common `stdlib.zip` was built from the primary ABI only, dropping every other ABI's arch-specific `_sysconfigdata__android_<arch>` module — which CPython imports at startup via `sysconfig` (pulled in by `ctypes`). The primary `splitStdlib` task now also harvests each other ABI's `_sysconfigdata__android_<arch>` into `stdlib.zip` (only that arch-specific module, so the generic ABI-identical `_sysconfigdata__linux_` shipped by some versions like 3.12 isn't duplicated).

## 4.1.0

* Run the `extractAsset` / `unzipAsset` / `loadLibrary` method-channel handlers on a background `Executor` (posting the `Result` back on the main looper) instead of inline on the platform main thread. The first-launch asset unpack and the pyjnius native-library load no longer block Android's `Choreographer`, so Flutter's vsync isn't starved and on-screen animations (e.g. a boot/splash spinner) stay smooth while the app starts.
Expand Down
43 changes: 43 additions & 0 deletions src/serious_python_android/android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,16 @@ for (abi in abis) {
// (+ .soref markers in stdlib.zip), stdlib/* -> stdlib.zip root, then delete it.
tasks.register("splitStdlib_$abi") {
dependsOn("untarFile_$abi")
// The ABI-common stdlib.zip is built once (from the primary ABI) but must
// also carry every OTHER ABI's arch-specific `_sysconfigdata__<arch>` (see
// the harvest in doLast). Make the primary task depend on the other ABIs'
// untar so their bundles exist to read, and hold non-primary tasks until
// the primary has read them (each task deletes its own bundle at the end).
if (isPrimary) {
abis.forEach { other -> if (other != abi) dependsOn("untarFile_$other") }
} else {
mustRunAfter("splitStdlib_$primaryAbi")
}
// The doLast rewrites jniLibs/<abi> (mangled libs in, bundle out); declare it as a
// tracked output and always re-run so AGP's native-libs merge re-packages.
outputs.dir(jniDir)
Expand Down Expand Up @@ -292,6 +302,39 @@ for (abi in abis) {
}
}
}
// `_sysconfigdata__android_<arch>` is ARCH-SPECIFIC: each ABI ships its
// own (e.g. `_sysconfigdata__android_x86_64-linux-android`) and CPython
// imports the one matching the running device at startup (sysconfig,
// pulled in by ctypes). Since stdlib.zip is ABI-common and built from the
// primary ABI only, harvest every other ABI's into it — otherwise a
// non-primary ABI (e.g. an x86_64 emulator when arm64-v8a is primary)
// crashes with `ModuleNotFoundError: _sysconfigdata__android_...`.
//
// Match ONLY the `_sysconfigdata__android_<arch>` files (unique per ABI),
// not the generic, ABI-identical `_sysconfigdata__linux_` that some
// versions (e.g. 3.12) also ship — the primary already added that via the
// stdlib loop above, so harvesting it again would be a duplicate zip entry.
if (isPrimary) {
abis.filter { it != abi }.forEach { otherAbi ->
val otherBundle = File(file("src/main/jniLibs/$otherAbi"), "libpythonbundle.so")
if (otherBundle.exists()) {
ZipFile(otherBundle).use { ozf ->
val oen = ozf.entries()
while (oen.hasMoreElements()) {
val oe = oen.nextElement()
if (!oe.isDirectory &&
oe.name.startsWith("stdlib/_sysconfigdata__android")
) {
zip?.add(
oe.name.removePrefix("stdlib/"),
ozf.getInputStream(oe).readBytes(),
)
}
}
}
}
}
}
zip?.add("_sp_bootstrap.py", bootstrapPy.readBytes()) // finder at zip root
// The dart-bridge Android shim (F) installs the finder before `site`. A
// sitecustomize fallback can be re-enabled for bridges without that shim:
Expand Down
Loading