Browse Source

dist: Android fixes, esp. for Python 3.13

This changes the use of sys.executable to sys.platlibdir, which requires Python 3.9+ (but that's probably okay since Python 3.8 is EOL)

Python 3.13 has proper official Android support, including built-in stdout/stderr redirection, but it only does so if sys.executable is not set
rdb 1 year ago
parent
commit
d6acaaffb0

+ 19 - 1
direct/src/dist/FreezeTool.py

@@ -925,7 +925,22 @@ class Freezer:
             if sys.version_info < (3, 8):
                 abi_flags += 'm'
 
-            if 'linux' in self.platform:
+            if 'android' in self.platform:
+                arch = self.platform.split('_', 1)[1]
+                if arch in ('arm64', 'aarch64'):
+                    suffixes.append(('.cpython-{0}{1}-aarch64-linux-android.so'.format(abi_version, abi_flags), 'rb', 3))
+                elif arch in ('arm', 'armv7l'):
+                    suffixes.append(('.cpython-{0}{1}-arm-linux-androideabi.so'.format(abi_version, abi_flags), 'rb', 3))
+                elif arch in ('x86_64', 'amd64'):
+                    suffixes.append(('.cpython-{0}{1}-x86_64-linux-android.so'.format(abi_version, abi_flags), 'rb', 3))
+                elif arch in ('i386', 'i686'):
+                    suffixes.append(('.cpython-{0}{1}-i686-linux-android.so'.format(abi_version, abi_flags), 'rb', 3))
+
+                suffixes += [
+                    ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
+                    ('.so', 'rb', 3),
+                ]
+            elif 'linux' in self.platform:
                 suffixes += [
                     ('.cpython-{0}{1}-x86_64-linux-gnu.so'.format(abi_version, abi_flags), 'rb', 3),
                     ('.cpython-{0}{1}-i686-linux-gnu.so'.format(abi_version, abi_flags), 'rb', 3),
@@ -1150,6 +1165,9 @@ class Freezer:
             self.modules['_frozen_importlib'] = self.ModuleDef('importlib._bootstrap', implicit = True)
             self.modules['_frozen_importlib_external'] = self.ModuleDef('importlib._bootstrap_external', implicit = True)
 
+            if self.platform.startswith('android'):
+                self.modules['_android_support'] = self.ModuleDef('_android_support', implicit = True)
+
             for moduleName in startupModules:
                 if moduleName not in self.modules:
                     self.addModule(moduleName, implicit = True)

+ 21 - 19
direct/src/dist/commands.py

@@ -188,11 +188,26 @@ FrozenImporter.get_data = get_data
 """
 
 SITE_PY_ANDROID = """
+# Define this first, before we import anything that might import an extension
+# module.
 import sys, os
+from importlib import _bootstrap, _bootstrap_external
+
+class AndroidExtensionFinder:
+    @classmethod
+    def find_spec(cls, fullname, path=None, target=None):
+        soname = 'libpy.' + fullname + '.so'
+        path = os.path.join(sys.platlibdir, soname)
+
+        if os.path.exists(path):
+            loader = _bootstrap_external.ExtensionFileLoader(fullname, path)
+            return _bootstrap.ModuleSpec(fullname, loader, origin=path)
+
+
+sys.meta_path.append(AndroidExtensionFinder)
+
+
 from _frozen_importlib import _imp, FrozenImporter
-from importlib import _bootstrap_external
-from importlib.abc import Loader, MetaPathFinder
-from importlib.machinery import ModuleSpec
 from io import RawIOBase, TextIOWrapper
 
 from android_log import write as android_log_write
@@ -242,8 +257,9 @@ class AndroidLogStream:
     def writable(self):
         return True
 
-sys.stdout = AndroidLogStream(2, 'Python')
-sys.stderr = AndroidLogStream(3, 'Python')
+if sys.version_info < (3, 13):
+    sys.stdout = AndroidLogStream(4, 'python.stdout')
+    sys.stderr = AndroidLogStream(5, 'python.stderr')
 
 
 # Alter FrozenImporter to give a __file__ property to frozen modules.
@@ -262,20 +278,6 @@ def get_data(path):
 
 FrozenImporter.find_spec = find_spec
 FrozenImporter.get_data = get_data
-
-
-class AndroidExtensionFinder(MetaPathFinder):
-    @classmethod
-    def find_spec(cls, fullname, path=None, target=None):
-        soname = 'libpy.' + fullname + '.so'
-        path = os.path.join(os.path.dirname(sys.executable), soname)
-
-        if os.path.exists(path):
-            loader = _bootstrap_external.ExtensionFileLoader(fullname, path)
-            return ModuleSpec(fullname, loader, origin=path)
-
-
-sys.meta_path.append(AndroidExtensionFinder)
 """
 
 

+ 7 - 6
pandatool/src/deploy-stub/android_main.cxx

@@ -161,8 +161,6 @@ void android_main(struct android_app *app) {
     std::string dtool_name = std::string(libdir) + "/libp3dtool.so";
     ExecutionEnvironment::set_dtool_name(dtool_name);
     android_cat.info() << "Path to dtool: " << dtool_name << "\n";
-
-    env->ReleaseStringUTFChars(libdir_jstr, libdir);
   }
 
   // Get the path to the APK.
@@ -184,6 +182,7 @@ void android_main(struct android_app *app) {
 
   // Map the blob to memory
   void *blob = map_blob(lib_path, (off_t)blobinfo.blob_offset, (size_t)blobinfo.blob_size);
+  env->ReleaseStringUTFChars(lib_path_jstr, lib_path);
   assert(blob != NULL);
 
   assert(blobinfo.num_pointers <= MAX_NUM_POINTERS);
@@ -242,14 +241,16 @@ void android_main(struct android_app *app) {
   preconfig.utf8_mode = 1;
   PyStatus status = Py_PreInitialize(&preconfig);
   if (PyStatus_Exception(status)) {
-      Py_ExitStatusException(status);
-      return;
+    env->ReleaseStringUTFChars(libdir_jstr, libdir);
+    Py_ExitStatusException(status);
+    return;
   }
 
   // Register the android_log module.
   if (PyImport_AppendInittab("android_log", &PyInit_android_log) < 0) {
     android_cat.error()
       << "Failed to register android_log module.\n";
+    env->ReleaseStringUTFChars(libdir_jstr, libdir);
     return;
   }
 
@@ -259,8 +260,8 @@ void android_main(struct android_app *app) {
   config.buffered_stdio = 0;
   config.configure_c_stdio = 0;
   config.write_bytecode = 0;
-  PyConfig_SetBytesString(&config, &config.executable, lib_path);
-  env->ReleaseStringUTFChars(lib_path_jstr, lib_path);
+  PyConfig_SetBytesString(&config, &config.platlibdir, libdir);
+  env->ReleaseStringUTFChars(libdir_jstr, libdir);
 
   status = Py_InitializeFromConfig(&config);
   PyConfig_Clear(&config);