From 7a0c9354b36d8d9e85cd28774560f87bfec6b524 Mon Sep 17 00:00:00 2001 From: Julian Date: Sun, 30 Jul 2023 17:14:47 +1000 Subject: [PATCH] Factor out dependency checking. Use modern version handling (#2866) LooseVersion used again Handle bad SDK versions --- pythonforandroid/checkdependencies.py | 70 +++++++++++++++ pythonforandroid/toolchain.py | 122 ++++++++------------------ 2 files changed, 109 insertions(+), 83 deletions(-) create mode 100644 pythonforandroid/checkdependencies.py diff --git a/pythonforandroid/checkdependencies.py b/pythonforandroid/checkdependencies.py new file mode 100644 index 0000000000..c53115de7a --- /dev/null +++ b/pythonforandroid/checkdependencies.py @@ -0,0 +1,70 @@ +from importlib import import_module +from os import environ +import sys + +from packaging.version import Version + +from pythonforandroid.prerequisites import ( + check_and_install_default_prerequisites, +) + + +def check_python_dependencies(): + """ + Check if the Python requirements are installed. This must appears + before other imports because otherwise they're imported elsewhere. + + Using the ok check instead of failing immediately so that all + errors are printed at once. + """ + + ok = True + + modules = [("colorama", "0.3.3"), "appdirs", ("sh", "1.10"), "jinja2"] + + for module in modules: + if isinstance(module, tuple): + module, version = module + else: + version = None + + try: + import_module(module) + except ImportError: + if version is None: + print( + "ERROR: The {} Python module could not be found, please " + "install it.".format(module) + ) + ok = False + else: + print( + "ERROR: The {} Python module could not be found, " + "please install version {} or higher".format( + module, version + ) + ) + ok = False + else: + if version is None: + continue + try: + cur_ver = sys.modules[module].__version__ + except AttributeError: # this is sometimes not available + continue + if Version(cur_ver) < Version(version): + print( + "ERROR: {} version is {}, but python-for-android needs " + "at least {}.".format(module, cur_ver, version) + ) + ok = False + + if not ok: + print("python-for-android is exiting due to the errors logged above") + exit(1) + + +def check(): + if not environ.get("SKIP_PREREQUISITES_CHECK", "0") == "1": + check_and_install_default_prerequisites() + check_python_dependencies() diff --git a/pythonforandroid/toolchain.py b/pythonforandroid/toolchain.py index 85404a2359..38c700740c 100644 --- a/pythonforandroid/toolchain.py +++ b/pythonforandroid/toolchain.py @@ -6,96 +6,43 @@ This module defines the entry point for command line and programmatic use. """ +from appdirs import user_data_dir +import argparse +from functools import wraps +import glob +import logging +import os from os import environ -from pythonforandroid import __version__ -from pythonforandroid.pythonpackage import get_dep_names_of_package -from pythonforandroid.recommendations import ( - RECOMMENDED_NDK_API, RECOMMENDED_TARGET_API, print_recommendations) -from pythonforandroid.util import BuildInterruptingException, load_source -from pythonforandroid.entrypoints import main -from pythonforandroid.prerequisites import check_and_install_default_prerequisites - - -def check_python_dependencies(): - # Check if the Python requirements are installed. This appears - # before the imports because otherwise they're imported elsewhere. - - # Using the ok check instead of failing immediately so that all - # errors are printed at once - - from distutils.version import LooseVersion - from importlib import import_module - import sys - - ok = True - - modules = [('colorama', '0.3.3'), 'appdirs', ('sh', '1.10'), 'jinja2'] - - for module in modules: - if isinstance(module, tuple): - module, version = module - else: - version = None - - try: - import_module(module) - except ImportError: - if version is None: - print('ERROR: The {} Python module could not be found, please ' - 'install it.'.format(module)) - ok = False - else: - print('ERROR: The {} Python module could not be found, ' - 'please install version {} or higher'.format( - module, version)) - ok = False - else: - if version is None: - continue - try: - cur_ver = sys.modules[module].__version__ - except AttributeError: # this is sometimes not available - continue - if LooseVersion(cur_ver) < LooseVersion(version): - print('ERROR: {} version is {}, but python-for-android needs ' - 'at least {}.'.format(module, cur_ver, version)) - ok = False - - if not ok: - print('python-for-android is exiting due to the errors logged above') - exit(1) - - -if not environ.get('SKIP_PREREQUISITES_CHECK', '0') == '1': - check_and_install_default_prerequisites() -check_python_dependencies() - - -import sys -from sys import platform from os.path import (join, dirname, realpath, exists, expanduser, basename) -import os -import glob -import shutil import re import shlex -from functools import wraps +import shutil +import sys +from sys import platform -import argparse +# This must be imported and run before other third-party or p4a +# packages. +from pythonforandroid.checkdependencies import check +check() + +from packaging.version import Version, InvalidVersion import sh -from appdirs import user_data_dir -import logging -from distutils.version import LooseVersion -from pythonforandroid.recipe import Recipe -from pythonforandroid.logger import (logger, info, warning, setup_color, - Out_Style, Out_Fore, - info_notify, info_main, shprint) -from pythonforandroid.util import current_directory +from pythonforandroid import __version__ from pythonforandroid.bootstrap import Bootstrap +from pythonforandroid.build import Context, build_recipes from pythonforandroid.distribution import Distribution, pretty_log_dists +from pythonforandroid.entrypoints import main from pythonforandroid.graph import get_recipe_order_and_bootstrap -from pythonforandroid.build import Context, build_recipes +from pythonforandroid.logger import (logger, info, warning, setup_color, + Out_Style, Out_Fore, + info_notify, info_main, shprint) +from pythonforandroid.pythonpackage import get_dep_names_of_package +from pythonforandroid.recipe import Recipe +from pythonforandroid.recommendations import ( + RECOMMENDED_NDK_API, RECOMMENDED_TARGET_API, print_recommendations) +from pythonforandroid.util import ( + current_directory, BuildInterruptingException, load_source) user_dir = dirname(realpath(os.path.curdir)) toolchain_dir = dirname(__file__) @@ -1068,13 +1015,22 @@ def _build_package(self, args, package_type): self.hook("before_apk_assemble") build_tools_versions = os.listdir(join(ctx.sdk_dir, 'build-tools')) - build_tools_versions = sorted(build_tools_versions, - key=LooseVersion) + + def sort_key(version_text): + try: + # Historically, Android build release candidates have had + # spaces in the version number. + return Version(version_text.replace(" ", "")) + except InvalidVersion: + # Put badly named versions at worst position. + return Version("0") + + build_tools_versions.sort(key=sort_key) build_tools_version = build_tools_versions[-1] info(('Detected highest available build tools ' 'version to be {}').format(build_tools_version)) - if build_tools_version < '25.0': + if Version(build_tools_version.replace(" ", "")) < Version('25.0'): raise BuildInterruptingException( 'build_tools >= 25 is required, but %s is installed' % build_tools_version) if not exists("gradlew"):