diff options
author | Shyamnath Premnadh <[email protected]> | 2024-12-06 09:48:34 +0100 |
---|---|---|
committer | Shyamnath Premnadh <[email protected]> | 2025-01-02 16:44:42 +0100 |
commit | ab8b330123cb557f66306b237f4c3aa8c0004f39 (patch) | |
tree | 9acae54ed4529e3c7b206c12befc071d178472a8 | |
parent | a067880f70c09f84bfeb24b4944c9e013c0fb93f (diff) |
Android Deployment: Auto download Android NDK
- In order to prevent code duplication, a symlink to
tools/cross_compile_android/android_utilities.py is created under
sources/pyside-tools/deploy_lib/android.
When running the script sources/pyside-tools/android_deploy.py,
this works without any issues.
When packaging the tools, the symlink is resolved into the actual file
and the actual file is packaged into the wheels.
- Remove global variable to __init__.py and remove the ones that are
not used
- Add tqdm to requirements.txt
- Adapt tests
- Additionally, include several new test cases to cover error scenarios
more comprehensively
Pick-to: 6.8
Task-number: PYSIDE-1612
Change-Id: I74728be30a2b8214b9a379b0b906fdacbb105833
Reviewed-by: Cristian Maureira-Fredes <[email protected]>
Reviewed-by: Friedemann Kleint <[email protected]>
6 files changed, 77 insertions, 16 deletions
diff --git a/sources/pyside-tools/CMakeLists.txt b/sources/pyside-tools/CMakeLists.txt index 7808eeee1..124ecc780 100644 --- a/sources/pyside-tools/CMakeLists.txt +++ b/sources/pyside-tools/CMakeLists.txt @@ -66,6 +66,7 @@ else() # pyside6-rcc, pyside6-uic, pyside6-designer, shiboken and pyside6-lupdate entrypoints foreach(file ${files}) if(EXISTS ${file}) + message(STATUS "Installing tool: ${file}") install(FILES "${file}" DESTINATION bin PERMISSIONS @@ -83,6 +84,18 @@ else() FILE_PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ - WORLD_EXECUTE WORLD_READ) + WORLD_EXECUTE WORLD_READ + PATTERN "android_utilities.py" EXCLUDE) # excluding the symlink endforeach() + + # dealing with android_utilities.py + set(ANDROID_UTILITIES_REALPATH + "${CMAKE_CURRENT_SOURCE_DIR}/../../tools/cross_compile_android/android_utilities.py") + message(STATUS "Installing ${ANDROID_UTILITIES_REALPATH}") + install(FILES "${ANDROID_UTILITIES_REALPATH}" + DESTINATION bin/deploy_lib/android + PERMISSIONS + OWNER_EXECUTE OWNER_WRITE OWNER_READ + GROUP_EXECUTE GROUP_READ + WORLD_EXECUTE WORLD_READ) endif() diff --git a/sources/pyside-tools/deploy_lib/android/__init__.py b/sources/pyside-tools/deploy_lib/android/__init__.py index 80ba6dee3..27d4d7b60 100644 --- a/sources/pyside-tools/deploy_lib/android/__init__.py +++ b/sources/pyside-tools/deploy_lib/android/__init__.py @@ -1,6 +1,7 @@ # Copyright (C) 2023 The Qt Company Ltd. # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only from __future__ import annotations +from pathlib import Path # maps instruction set to Android platform names platform_map = {"aarch64": "arm64-v8a", @@ -11,6 +12,8 @@ platform_map = {"aarch64": "arm64-v8a", "armeabi-v7a": "armeabi-v7a", "x86": "x86"} +ANDROID_DEPLOY_CACHE = Path.home() / ".pyside6_android_deploy" + from .android_helper import (create_recipe, extract_and_copy_jar, get_wheel_android_arch, AndroidData, get_llvm_readobj, find_lib_dependencies, find_qtlibs_in_wheel) diff --git a/sources/pyside-tools/deploy_lib/android/android_config.py b/sources/pyside-tools/deploy_lib/android/android_config.py index 503fa6085..6b9386b8a 100644 --- a/sources/pyside-tools/deploy_lib/android/android_config.py +++ b/sources/pyside-tools/deploy_lib/android/android_config.py @@ -12,12 +12,11 @@ from pathlib import Path from pkginfo import Wheel from . import (extract_and_copy_jar, get_wheel_android_arch, find_lib_dependencies, - get_llvm_readobj, find_qtlibs_in_wheel, platform_map, create_recipe) + get_llvm_readobj, find_qtlibs_in_wheel, platform_map, create_recipe, + ANDROID_DEPLOY_CACHE) from .. import (Config, get_all_pyside_modules, MAJOR_VERSION) - -ANDROID_NDK_VERSION = "26b" -ANDROID_NDK_VERSION_NUMBER_SUFFIX = "10909125" -ANDROID_DEPLOY_CACHE = Path.home() / ".pyside6_android_deploy" +from .android_utilities import (ANDROID_NDK_VERSION, ANDROID_NDK_VERSION_NUMBER_SUFFIX, + download_android_ndk) class AndroidConfig(Config): @@ -52,7 +51,7 @@ class AndroidConfig(Config): if android_data.ndk_path: # from cli self.ndk_path = android_data.ndk_path - else: + elif not existing_config_file: # from config ndk_path_temp = self.get_value("buildozer", "ndk_path") if ndk_path_temp: @@ -67,12 +66,14 @@ class AndroidConfig(Config): ) if ndk_path_temp.exists(): self.ndk_path = ndk_path_temp + else: + # download NDK + if not ANDROID_DEPLOY_CACHE.exists(): + ANDROID_DEPLOY_CACHE.mkdir() + logging.info(f"Cache created at {str(ANDROID_DEPLOY_CACHE.resolve())}") - if self.ndk_path: - print(f"Using Android NDK: {str(self.ndk_path)}") - else: - raise FileNotFoundError("[DEPLOY] Unable to find Android NDK. Please pass the NDK " - "path either from the CLI or from pysidedeploy.spec") + logging.info("[DEPLOY] Downloading Android NDK") + self.ndk_path = download_android_ndk(ANDROID_DEPLOY_CACHE) self.sdk_path = None if android_data.sdk_path: diff --git a/sources/pyside-tools/deploy_lib/android/android_utilities.py b/sources/pyside-tools/deploy_lib/android/android_utilities.py new file mode 120000 index 000000000..d8a91b3e7 --- /dev/null +++ b/sources/pyside-tools/deploy_lib/android/android_utilities.py @@ -0,0 +1 @@ +../../../../tools/cross_compile_android/android_utilities.py
\ No newline at end of file diff --git a/sources/pyside-tools/requirements-android.txt b/sources/pyside-tools/requirements-android.txt index 1169fd663..9ed5d8427 100644 --- a/sources/pyside-tools/requirements-android.txt +++ b/sources/pyside-tools/requirements-android.txt @@ -1,2 +1,3 @@ jinja2 pkginfo +tqdm diff --git a/sources/pyside6/tests/tools/pyside6-android-deploy/test_pyside6_android_deploy.py b/sources/pyside6/tests/tools/pyside6-android-deploy/test_pyside6_android_deploy.py index a0187deee..120c54af3 100644 --- a/sources/pyside6/tests/tools/pyside6-android-deploy/test_pyside6_android_deploy.py +++ b/sources/pyside6/tests/tools/pyside6-android-deploy/test_pyside6_android_deploy.py @@ -169,11 +169,26 @@ class TestPySide6AndroidDeployWidgets(DeployTestBase): self.buildozer_config.unlink() def test_errors(self, mock_extract_jar): - # test if error raises for non existing NDK - with self.assertRaises(FileNotFoundError) as context: + # test when no shiboken wheel is passed + with self.assertRaises(RuntimeError) as context: + self.android_deploy.main(name="android_app", pyside_wheel=self.pyside_wheel, + ndk_path=self.ndk_path, force=True) + self.assertTrue("Unable to find shiboken6 Android wheel" in str(context.exception)) + + # test when no PySide wheel is passed + with self.assertRaises(RuntimeError) as context: self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel, - pyside_wheel=self.pyside_wheel, force=True) - self.assertTrue("Unable to find Android NDK" in str(context.exception)) + ndk_path=self.ndk_path, force=True) + self.assertTrue("Unable to find PySide6 Android wheel" in str(context.exception)) + + # test if wheel name is correct + pyside_wheel_temp = Path("/tmp/" + "PySide6-6.8.0.1+commercial-6.8.0.1-cp311-cp311-android_fake.whl") + with self.assertRaises(RuntimeError) as context: + self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel, + pyside_wheel=pyside_wheel_temp, ndk_path=self.ndk_path, + force=True) + self.assertTrue("PySide wheel corrupted" in str(context.exception)) # test when cwd() is not project_dir os.chdir(self.current_dir) @@ -182,6 +197,33 @@ class TestPySide6AndroidDeployWidgets(DeployTestBase): pyside_wheel=self.pyside_wheel, init=True, force=True) self.assertTrue("For Android deployment to work" in str(context.exception)) + @patch("deploy_lib.android.buildozer.BuildozerConfig._BuildozerConfig__find_jars") + @patch("deploy_lib.android.android_config.AndroidConfig.recipes_exist") + @patch("deploy_lib.android.android_config.AndroidConfig._find_dependent_qt_modules") + @patch("deploy_lib.android.android_config.find_qtlibs_in_wheel") + @patch("deploy_lib.android.android_config.download_android_ndk") + def test_no_ndk_passed(self, mock_ndk, mock_qtlibs, mock_extraqtmodules, mock_recipes_exist, + mock_find_jars, mock_extract_jar): + jar_dir = "tmp/jar/PySide6/jar" + mock_extract_jar.return_value = Path(jar_dir) + mock_qtlibs.return_value = self.pyside_wheel / "PySide6/Qt/lib" + mock_extraqtmodules.return_value = [] + mock_recipes_exist.return_value = True + jars, init_classes = ["/tmp/jar/PySide6/jar/Qt6Android.jar", + "/tmp/jar/PySide6/jar/Qt6AndroidBindings.jar"], [] + mock_find_jars.return_value = jars, init_classes + mock_ndk.return_value = Path("/tmp/android_ndk") + + self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel, + pyside_wheel=self.pyside_wheel, + init=True, force=True, + keep_deployment_files=True) + + # test config file contents + config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file) + self.assertEqual(config_obj.get_value("buildozer", "sdk_path"), '') + self.assertEqual(config_obj.get_value("buildozer", "ndk_path"), "/tmp/android_ndk") + @patch("deploy_lib.config.run_qmlimportscanner") @patch("deploy_lib.android.android_config.extract_and_copy_jar") |