From cd3ca7dc570e2044e11bbfb658c0903fdad16e9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20P=C3=B6hn?= Date: Wed, 29 Jan 2025 14:05:52 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=AA=97=20get=20auto=20NDK=20installs=20wo?= =?UTF-8?q?rking=20in=20vm=20builds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fdroidserver/__main__.py | 1 + fdroidserver/install_ndk.py | 75 +++++++++++++++++++++++++++++++++++++ tests/test_install_ndk.py | 68 +++++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 fdroidserver/install_ndk.py create mode 100755 tests/test_install_ndk.py diff --git a/fdroidserver/__main__.py b/fdroidserver/__main__.py index e9737014..12b7ec21 100755 --- a/fdroidserver/__main__.py +++ b/fdroidserver/__main__.py @@ -68,6 +68,7 @@ COMMANDS_INTERNAL = [ "build_local_run", "destroy", "exec", + "install_ndk", "prepare_source", "pull", "push", diff --git a/fdroidserver/install_ndk.py b/fdroidserver/install_ndk.py new file mode 100644 index 00000000..5ce0c12d --- /dev/null +++ b/fdroidserver/install_ndk.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +# +# install_ndk.py - part of the F-Droid server tools +# Copyright (C) 2024-2025 Michael Pöhn +# Copyright (C) 2025 Hans-Christoph Steiner +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +"""Read the "ndk:" field from the build metadata and install the right NDK packages. + +For more info, see: +https://f-droid.org/docs/Build_Metadata_Reference/#build_ndk + +""" + +import argparse +import logging +import os + +from fdroidserver import common, exception, metadata + + +def install_ndk_wrapper(build, ndk_paths=dict()): + """Make sure the requested NDK version is or gets installed. + + Parameters + ---------- + build + metadata.Build instance entry that may contain the + requested NDK version + ndk_paths + dictionary holding the currently installed NDKs + """ + ndk_path = build.ndk_path() + if build.ndk or (build.buildjni and build.buildjni != ['no']): + if not ndk_path: + for k, v in ndk_paths.items(): + if k.endswith("_orig"): + continue + common.auto_install_ndk(build) + ndk_path = build.ndk_path() + if not os.path.isdir(ndk_path): + logging.critical("Android NDK '%s' is not installed!" % ndk_path) + raise exception.FDroidException() + return ndk_path + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + common.setup_global_opts(parser) + parser.add_argument( + "APPID:VERCODE", + help="Application ID with Version Code in the form APPID:VERCODE", + ) + + options = common.parse_args(parser) + config = common.get_config() + appid, versionCode = common.split_pkg_arg(options.__dict__['APPID:VERCODE']) + app, build = metadata.get_single_build(appid, versionCode) + install_ndk_wrapper(build, config.get('ndk_paths', dict())) + + +if __name__ == "__main__": + main() diff --git a/tests/test_install_ndk.py b/tests/test_install_ndk.py new file mode 100755 index 00000000..500a0341 --- /dev/null +++ b/tests/test_install_ndk.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 + +import os +import unittest + +from pathlib import Path +from unittest import mock + +from fdroidserver import common, metadata, install_ndk +from .shared_test_code import mkdtemp, APPID, VERCODE, APPID_VERCODE + +NDK_RELEASE = 'r24' +NDK_REVISION = '24.0.8215888' + + +class InstallNdkTest(unittest.TestCase): + basedir = Path(__file__).resolve().parent + + def setUp(self): + self._td = mkdtemp() + self.testdir = self._td.name + os.chdir(self.testdir) + common.config = {'ndk_paths': {}, 'sdk_path': self.testdir} + + def tearDown(self): + self._td.cleanup() + common.config = None + + +def mock_sdkmanager_install(to_install, android_home=None): + path = f'{android_home}/{to_install.replace(";", "/")}' + ndk_dir = Path(path) + ndk_dir.mkdir(parents=True) + (ndk_dir / 'source.properties').write_text(f'Pkg.Revision = {NDK_REVISION}\n') + + +@mock.patch('sdkmanager.build_package_list', lambda use_net: None) +class InstallNdk_wrapper(InstallNdkTest): + @mock.patch('sdkmanager.install') + def test_with_ndk(self, sdkmanager_install): + sdkmanager_install.side_effect = mock_sdkmanager_install + build = metadata.Build({'versionCode': VERCODE, 'ndk': NDK_RELEASE}) + install_ndk.install_ndk_wrapper(build) + sdkmanager_install.assert_called_once() + + @mock.patch('fdroidserver.common.auto_install_ndk') + def test_without_ndk(self, auto_install_ndk): + build = metadata.Build({'versionCode': VERCODE}) + install_ndk.install_ndk_wrapper(build) + auto_install_ndk.assert_not_called() + + +@mock.patch('sys.argv', ['fdroid ndk', APPID_VERCODE]) +@mock.patch('sdkmanager.build_package_list', lambda use_net: None) +@mock.patch('sdkmanager.install') +class InstallNdk_main(InstallNdkTest): + def setUp(self): + super().setUp() + metadatapath = Path(common.get_metadatapath(APPID)) + metadatapath.parent.mkdir() + metadatapath.write_text( + f'Builds:\n - versionCode: {VERCODE}\n ndk: {NDK_RELEASE}\n' + ) + + def test_ndk_main(self, sdkmanager_install): + sdkmanager_install.side_effect = mock_sdkmanager_install + install_ndk.main() + sdkmanager_install.assert_called_once()