aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside-tools/deploy_lib/android/android_helper.py
diff options
context:
space:
mode:
authorShyamnath Premnadh <Shyamnath.Premnadh@qt.io>2023-05-17 10:04:18 +0200
committerShyamnath Premnadh <Shyamnath.Premnadh@qt.io>2023-09-08 09:05:06 +0200
commitf14077be7a60751599e786fe647d823dc3f91ee3 (patch)
tree2bca5135d68c2ecbc393a3fa615d28aed8daa550 /sources/pyside-tools/deploy_lib/android/android_helper.py
parent9b3d266aab68e28cd230b1587f233d74af84e629 (diff)
Android Deployment: find PySide and Qt dependencies
- Use llvm-readelf to recursively find the dependencies of a dependent Qt binary. All the Qt dependencies are loaded at startup when loading the Android application. - Parse the revelant Python files of the project into ast, and find the used Python modules. Once the Python file is parsed into an ast, we find the imports of the following form: from PySide6 import Qt<module> from PySide6.Qt<module> import <classname> This is then used to identify the module used, and we try to load the binaries of this module. If the modules does not exist in Qt for Android, then an error is thrown. - The easiest way to find the relevant Python files in the project is using a .pyproject file which lists all the relevant files. If this is not there, then we find all the Python files in the project folder excluding the following folders: [".hg", ".svn", ".git", ".tox", "__pycache__", "env", "venv", "deployment",".buildozer"] - A new cli argument --extra-ignore-dirs, that lists the extra directories to ignore when searching for all the relevant python files in the project. - A new cli argument --extra-modules, that lists the extra modules to be added manually to the application incase they are not found by `pyside6-android-deploy` automatically. Adding a module using this argument means that the module binary is loaded by the Android application on startup. - sdk and ndk cli options are now mandatory to find the dependencies. These two options will be removed later when pyside6-android-deploy can automatically download them. Task-number: PYSIDE-1612 Change-Id: Ifbdc20cbc70ab0935a23157ccc8cb7fde6992df2 Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Diffstat (limited to 'sources/pyside-tools/deploy_lib/android/android_helper.py')
-rw-r--r--sources/pyside-tools/deploy_lib/android/android_helper.py73
1 files changed, 71 insertions, 2 deletions
diff --git a/sources/pyside-tools/deploy_lib/android/android_helper.py b/sources/pyside-tools/deploy_lib/android/android_helper.py
index 321fc3734..514ac8b5d 100644
--- a/sources/pyside-tools/deploy_lib/android/android_helper.py
+++ b/sources/pyside-tools/deploy_lib/android/android_helper.py
@@ -2,10 +2,15 @@
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
import logging
-from pathlib import Path
-from jinja2 import Environment, FileSystemLoader
+import zipfile
from zipfile import ZipFile
from dataclasses import dataclass
+from typing import Set
+
+from pathlib import Path
+from jinja2 import Environment, FileSystemLoader
+
+from .. import run_command
@dataclass
@@ -62,3 +67,67 @@ def get_wheel_android_arch(wheel: Path):
return arch
return None
+
+
+def get_llvm_readobj(ndk_path: Path) -> Path:
+ '''
+ Return the path to llvm_readobj from the Android Ndk
+ '''
+ if not ndk_path:
+ # fetch ndk path from buildozer
+ raise FileNotFoundError("[DEPLOY] Unable to find Ndk path. Please pass the Ndk path either"
+ " from the CLI or from pysidedeploy.spec")
+
+ # TODO: Requires change if Windows platform supports Android Deployment or if we
+ # support host other than linux-x86_64
+ return (ndk_path / "toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-readobj")
+
+
+def find_lib_dependencies(llvm_readobj: Path, lib_path: Path, used_dependencies: Set[str] = None,
+ dry_run: bool = False):
+ """
+ Find all the Qt dependencies of a library using llvm_readobj
+ """
+ if lib_path.name in used_dependencies:
+ return
+
+ command = [str(llvm_readobj), "--needed-libs", str(lib_path)]
+ _, output = run_command(command=command, dry_run=dry_run, fetch_output=True)
+
+ dependencies = set()
+ neededlibraries_found = False
+ for line in output.splitlines():
+ line = line.decode("utf-8").lstrip()
+ if line.startswith("NeededLibraries") and not neededlibraries_found:
+ neededlibraries_found = True
+ if neededlibraries_found and line.startswith("libQt"):
+ dependencies.add(line)
+ used_dependencies.add(line)
+ dependent_lib_path = lib_path.parent / line
+ find_lib_dependencies(llvm_readobj, dependent_lib_path, used_dependencies, dry_run)
+
+ if dependencies:
+ logging.info(f"[DEPLOY] Following dependencies found for {lib_path.stem}: {dependencies}")
+ else:
+ logging.info(f"[DEPLOY] No Qt dependencies found for {lib_path.stem}")
+
+
+def find_qtlibs_in_wheel(wheel_pyside: Path):
+ """
+ Find the path to Qt/lib folder inside the wheel.
+ """
+ archive = ZipFile(wheel_pyside)
+ qt_libs_path = wheel_pyside / "PySide6/Qt/lib"
+ qt_libs_path = zipfile.Path(archive, at=qt_libs_path)
+ if not qt_libs_path.exists():
+ for file in archive.namelist():
+ # the dependency files are inside the libs folder
+ if file.endswith("android-dependencies.xml"):
+ qt_libs_path = zipfile.Path(archive, at=file).parent
+ # all dependency files are in the same path
+ break
+
+ if not qt_libs_path:
+ raise FileNotFoundError("[DEPLOY] Unable to find Qt libs folder inside the wheel")
+
+ return qt_libs_path