From d6acaaffb0354f6078fcdc23fa2cefbb086d1bc0 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 27 Nov 2024 00:10:37 +0100 Subject: [PATCH] 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 --- direct/src/dist/FreezeTool.py | 20 ++++++++++- direct/src/dist/commands.py | 40 ++++++++++++---------- pandatool/src/deploy-stub/android_main.cxx | 13 +++---- 3 files changed, 47 insertions(+), 26 deletions(-) diff --git a/direct/src/dist/FreezeTool.py b/direct/src/dist/FreezeTool.py index 678f8c0888..aad6dc4b24 100644 --- a/direct/src/dist/FreezeTool.py +++ b/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) diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py index 5f12b7fe4b..8a7112d4c6 100644 --- a/direct/src/dist/commands.py +++ b/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) """ diff --git a/pandatool/src/deploy-stub/android_main.cxx b/pandatool/src/deploy-stub/android_main.cxx index b29889764e..a99c48eb66 100644 --- a/pandatool/src/deploy-stub/android_main.cxx +++ b/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);