diff options
| author | Shyamnath Premnadh <Shyamnath.Premnadh@qt.io> | 2023-05-17 10:04:18 +0200 |
|---|---|---|
| committer | Shyamnath Premnadh <Shyamnath.Premnadh@qt.io> | 2023-09-08 09:05:06 +0200 |
| commit | f14077be7a60751599e786fe647d823dc3f91ee3 (patch) | |
| tree | 2bca5135d68c2ecbc393a3fa615d28aed8daa550 /sources/pyside-tools/deploy_lib/android/android_helper.py | |
| parent | 9b3d266aab68e28cd230b1587f233d74af84e629 (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.py | 73 |
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 |
