diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 597293a9d7..741210775b 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -3,9 +3,9 @@ name: Unit tests & build apps on: ['push', 'pull_request'] env: - APK_ARTIFACT_FILENAME: bdist_unit_tests_app-debug-1.1-.apk - AAB_ARTIFACT_FILENAME: bdist_unit_tests_app-release-1.1-.aab - AAR_ARTIFACT_FILENAME: bdist_unit_tests_app-release-1.1-.aar + APK_ARTIFACT_FILENAME: bdist_unit_tests_app-debug-1.1.apk + AAB_ARTIFACT_FILENAME: bdist_unit_tests_app-release-1.1.aab + AAR_ARTIFACT_FILENAME: bdist_unit_tests_app-release-1.1.aar PYTHONFORANDROID_PREREQUISITES_INSTALL_INTERACTIVE: 0 jobs: diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e37bdc5cc..2393dc204e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,48 @@ # Changelog +## [v2022.09.04](https://github.com/kivy/python-for-android/tree/v2022.09.04) (2022-09-04) + +[Full Changelog](https://github.com/kivy/python-for-android/compare/v2022.07.20...v2022.09.04) + +**Fixed bugs:** + +- Matplotlib failed to import properly on an APK from Buildozer and Kivy [\#2643](https://github.com/kivy/python-for-android/issues/2643) + +**Closed issues:** + +- KeyError: Matplotlib with kivy android [\#2658](https://github.com/kivy/python-for-android/issues/2658) +- KeyError: Matplotlib [\#2659](https://github.com/kivy/python-for-android/issues/2659) +- Upgrade from NDK 19b to 23b causes problems with Pandas library [\#2654](https://github.com/kivy/python-for-android/issues/2654) +- Update Dockerfile for ARM [\#2653](https://github.com/kivy/python-for-android/issues/2653) +- Apple M2 chip doesn't generate apk: compiling error on liblzma [\#2652](https://github.com/kivy/python-for-android/issues/2652) +- aiohttp/\_http\_parser.pyx:46:0: '\_headers.pxi' not found [\#2651](https://github.com/kivy/python-for-android/issues/2651) +- \[Question\] Pip SSL ? [\#2649](https://github.com/kivy/python-for-android/issues/2649) +- Colab gives me as error "No module named 'typing\_extensions' ", even if before with the same compilation it worked [\#2648](https://github.com/kivy/python-for-android/issues/2648) +- \[Question\] Java Files [\#2646](https://github.com/kivy/python-for-android/issues/2646) +- Using foreground services will cause wired behaviour on Android 8 [\#2641](https://github.com/kivy/python-for-android/issues/2641) +- Can't apply patches with relative paths for local recipe [\#2623](https://github.com/kivy/python-for-android/issues/2623) +- Compile for x86 on MacOS [\#2215](https://github.com/kivy/python-for-android/issues/2215) +- splash always loading [\#1907](https://github.com/kivy/python-for-android/issues/1907) +- python-for-android.readthedocs.io has problems updating, apparently [\#1709](https://github.com/kivy/python-for-android/issues/1709) +- Webview apps not working on Android [\#1644](https://github.com/kivy/python-for-android/issues/1644) + +**Merged pull requests:** + +- `liblzma`: Use `p4a_install` instead of `install`, as a file named `INSTALL` is already present. [\#2663](https://github.com/kivy/python-for-android/pull/2663) ([misl6](https://github.com/misl6)) +- Force `--platform=linux/amd64` in Dockerfile [\#2660](https://github.com/kivy/python-for-android/pull/2660) ([misl6](https://github.com/misl6)) +- Remove six and enum34 dependency [\#2657](https://github.com/kivy/python-for-android/pull/2657) ([misl6](https://github.com/misl6)) +- Update supported Python versions [\#2656](https://github.com/kivy/python-for-android/pull/2656) ([misl6](https://github.com/misl6)) +- Fixes some E275 - assert is a keyword. [\#2647](https://github.com/kivy/python-for-android/pull/2647) ([misl6](https://github.com/misl6)) +- Updates matplotlib, fixes an issue related to shared libc++ [\#2645](https://github.com/kivy/python-for-android/pull/2645) ([misl6](https://github.com/misl6)) +- RTSP support for ffmpeg [\#2644](https://github.com/kivy/python-for-android/pull/2644) ([alicakici1234](https://github.com/alicakici1234)) +- Fixes TypeError: str.join\(\) takes exactly one argument \(2 given\) in hostpython3/\_\_init\_\_.py", line 69 [\#2642](https://github.com/kivy/python-for-android/pull/2642) ([Furtif](https://github.com/Furtif)) +- Resolve absolute path to local recipes [\#2640](https://github.com/kivy/python-for-android/pull/2640) ([dbnicholson](https://github.com/dbnicholson)) +- Merges master into develop after release 2022.07.20 [\#2639](https://github.com/kivy/python-for-android/pull/2639) ([misl6](https://github.com/misl6)) +- Fix webview Back button behaviour [\#2636](https://github.com/kivy/python-for-android/pull/2636) ([interlark](https://github.com/interlark)) +- Add icon-bg and icon-fg to fix\_args [\#2633](https://github.com/kivy/python-for-android/pull/2633) ([danigm](https://github.com/danigm)) +- Remove stray - in output file name [\#2581](https://github.com/kivy/python-for-android/pull/2581) ([dbnicholson](https://github.com/dbnicholson)) +- Add option for adding files to res/xml without touching manifest [\#2330](https://github.com/kivy/python-for-android/pull/2330) ([rambo](https://github.com/rambo)) + ## [v2022.07.20](https://github.com/kivy/python-for-android/tree/v2022.07.20) (2022-07-20) [Full Changelog](https://github.com/kivy/python-for-android/compare/v2022.03.13...v2022.07.20) diff --git a/Dockerfile b/Dockerfile index e83737b1df..3b48c508c9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,10 @@ # Use 'docker run' without '--rm' flag for keeping the container and use # 'docker commit ' to extend the original image -FROM ubuntu:20.04 +# If platform is not specified, by default the target platform of the build request is used. +# This is not what we want, as Google doesn't provide a linux/arm64 compatible NDK. +# See: https://docs.docker.com/engine/reference/builder/#from +FROM --platform=linux/amd64 ubuntu:20.04 # configure locale RUN apt -y update -qq > /dev/null \ diff --git a/Makefile b/Makefile index b0d3da883e..97f502219e 100644 --- a/Makefile +++ b/Makefile @@ -106,17 +106,17 @@ docker/run/command: docker/build docker/run/make/with-artifact/apk/%: docker/build docker run --name p4a-latest --env-file=.env $(DOCKER_IMAGE) make $* - docker cp p4a-latest:/home/user/app/testapps/on_device_unit_tests/bdist_unit_tests_app-debug-1.1-.apk ./apks + docker cp p4a-latest:/home/user/app/testapps/on_device_unit_tests/bdist_unit_tests_app-debug-1.1.apk ./apks docker rm -fv p4a-latest docker/run/make/with-artifact/aar/%: docker/build docker run --name p4a-latest --env-file=.env $(DOCKER_IMAGE) make $* - docker cp p4a-latest:/home/user/app/testapps/on_device_unit_tests/bdist_unit_tests_app-release-1.1-.aar ./aars + docker cp p4a-latest:/home/user/app/testapps/on_device_unit_tests/bdist_unit_tests_app-release-1.1.aar ./aars docker rm -fv p4a-latest docker/run/make/with-artifact/aab/%: docker/build docker run --name p4a-latest --env-file=.env $(DOCKER_IMAGE) make $* - docker cp p4a-latest:/home/user/app/testapps/on_device_unit_tests/bdist_unit_tests_app-release-1.1-.aab ./aabs + docker cp p4a-latest:/home/user/app/testapps/on_device_unit_tests/bdist_unit_tests_app-release-1.1.aab ./aabs docker rm -fv p4a-latest docker/run/make/rebuild_updated_recipes: docker/build diff --git a/pythonforandroid/__init__.py b/pythonforandroid/__init__.py index e014c4ff87..f39a847cb5 100644 --- a/pythonforandroid/__init__.py +++ b/pythonforandroid/__init__.py @@ -1 +1 @@ -__version__ = '2022.07.20' +__version__ = '2022.09.04' diff --git a/pythonforandroid/bootstraps/common/build/build.py b/pythonforandroid/bootstraps/common/build/build.py index dcb6d2ac3b..6885a333df 100644 --- a/pythonforandroid/bootstraps/common/build/build.py +++ b/pythonforandroid/bootstraps/common/build/build.py @@ -508,12 +508,20 @@ def make_package(args): url_scheme = 'kivy' # Copy backup rules file if specified and update the argument + res_xml_dir = join(res_dir, 'xml') if args.backup_rules: - res_xml_dir = join(res_dir, 'xml') ensure_dir(res_xml_dir) shutil.copy(join(args.private, args.backup_rules), res_xml_dir) args.backup_rules = split(args.backup_rules)[1][:-4] + # Copy res_xml files to src/main/res/xml + if args.res_xmls: + ensure_dir(res_xml_dir) + for xmlpath in args.res_xmls: + if not os.path.exists(xmlpath): + xmlpath = join(args.private, xmlpath) + shutil.copy(xmlpath, res_xml_dir) + # Render out android manifest: manifest_path = "src/main/AndroidManifest.xml" render_args = { @@ -803,6 +811,8 @@ def parse_args_and_make_package(args=None): 'filename containing xml. The filename should be ' 'located relative to the python-for-android ' 'directory')) + ap.add_argument('--res_xml', dest='res_xmls', action='append', default=[], + help='Add files to res/xml directory (for example device-filters)', nargs='+') ap.add_argument('--with-billing', dest='billing_pubkey', help='If set, the billing service will be added (not implemented)') ap.add_argument('--add-source', dest='extra_source_dirs', action='append', @@ -901,6 +911,9 @@ def _read_configuration(): if args.permissions and isinstance(args.permissions[0], list): args.permissions = [p for perm in args.permissions for p in perm] + if args.res_xmls and isinstance(args.res_xmls[0], list): + args.res_xmls = [x for res in args.res_xmls for x in res] + if args.try_system_python_compile: # Hardcoding python2.7 is okay for now, as python3 skips the # compilation anyway diff --git a/pythonforandroid/bootstraps/service_only/build/src/main/java/org/kivy/android/PythonActivity.java b/pythonforandroid/bootstraps/service_only/build/src/main/java/org/kivy/android/PythonActivity.java index 919c42b0ea..87ea061c41 100644 --- a/pythonforandroid/bootstraps/service_only/build/src/main/java/org/kivy/android/PythonActivity.java +++ b/pythonforandroid/bootstraps/service_only/build/src/main/java/org/kivy/android/PythonActivity.java @@ -177,19 +177,22 @@ public void loadLibraries() { new File(getApplicationInfo().nativeLibraryDir)); } - long lastBackClick = SystemClock.elapsedRealtime(); + long lastBackClick = 0; @Override public boolean onKeyDown(int keyCode, KeyEvent event) { - // If it wasn't the Back key or there's no web page history, bubble up to the default - // system behavior (probably exit the activity) - if (SystemClock.elapsedRealtime() - lastBackClick > 2000){ + // Check if the key event was the Back button + if (keyCode == KeyEvent.KEYCODE_BACK) { + // If there's no web page history, bubble up to the default + // system behavior (probably exit the activity) + if (SystemClock.elapsedRealtime() - lastBackClick > 2000){ + lastBackClick = SystemClock.elapsedRealtime(); + Toast.makeText(this, "Tap again to close the app", Toast.LENGTH_LONG).show(); + return true; + } + lastBackClick = SystemClock.elapsedRealtime(); - Toast.makeText(this, "Click again to close the app", - Toast.LENGTH_LONG).show(); - return true; } - lastBackClick = SystemClock.elapsedRealtime(); return super.onKeyDown(keyCode, event); } diff --git a/pythonforandroid/bootstraps/webview/build/src/main/java/org/kivy/android/PythonActivity.java b/pythonforandroid/bootstraps/webview/build/src/main/java/org/kivy/android/PythonActivity.java index b8499849da..8aa308b24a 100644 --- a/pythonforandroid/bootstraps/webview/build/src/main/java/org/kivy/android/PythonActivity.java +++ b/pythonforandroid/bootstraps/webview/build/src/main/java/org/kivy/android/PythonActivity.java @@ -32,6 +32,7 @@ import android.widget.AbsoluteLayout; import android.view.ViewGroup.LayoutParams; +import android.webkit.WebBackForwardList; import android.webkit.WebViewClient; import android.webkit.WebView; import android.webkit.CookieManager; @@ -269,24 +270,30 @@ public static ViewGroup getLayout() { return mLayout; } - long lastBackClick = SystemClock.elapsedRealtime(); + long lastBackClick = 0; @Override public boolean onKeyDown(int keyCode, KeyEvent event) { - // Check if the key event was the Back button and if there's history - if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) { - mWebView.goBack(); - return true; - } - // If it wasn't the Back key or there's no web page history, bubble up to the default - // system behavior (probably exit the activity) - if (SystemClock.elapsedRealtime() - lastBackClick > 2000){ + // Check if the key event was the Back button + if (keyCode == KeyEvent.KEYCODE_BACK) { + // Go back if there is web page history behind, + // but not to the start preloader + WebBackForwardList webViewBackForwardList = mWebView.copyBackForwardList(); + if (webViewBackForwardList.getCurrentIndex() > 1) { + mWebView.goBack(); + return true; + } + + // If there's no web page history, bubble up to the default + // system behavior (probably exit the activity) + if (SystemClock.elapsedRealtime() - lastBackClick > 2000){ + lastBackClick = SystemClock.elapsedRealtime(); + Toast.makeText(this, "Tap again to close the app", Toast.LENGTH_LONG).show(); + return true; + } + lastBackClick = SystemClock.elapsedRealtime(); - Toast.makeText(this, "Click again to close the app", - Toast.LENGTH_LONG).show(); - return true; } - lastBackClick = SystemClock.elapsedRealtime(); return super.onKeyDown(keyCode, event); } diff --git a/pythonforandroid/bootstraps/webview/build/src/main/res/mipmap-anydpi-v26/.gitkeep b/pythonforandroid/bootstraps/webview/build/src/main/res/mipmap-anydpi-v26/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pythonforandroid/graph.py b/pythonforandroid/graph.py index 0cb9984b8b..bdaca4349c 100644 --- a/pythonforandroid/graph.py +++ b/pythonforandroid/graph.py @@ -45,7 +45,7 @@ def get_dependency_tuple_list_for_recipe(recipe, blacklist=None): """ if blacklist is None: blacklist = set() - assert(type(blacklist) == set) + assert type(blacklist) == set if recipe.depends is None: dependencies = [] else: diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index c2639a0095..b4cd9bb3cf 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -1,7 +1,6 @@ from os.path import basename, dirname, exists, isdir, isfile, join, realpath, split import glob from shutil import rmtree -from six import with_metaclass import hashlib from re import match @@ -40,7 +39,7 @@ def __new__(cls, name, bases, dct): return super().__new__(cls, name, bases, dct) -class Recipe(with_metaclass(RecipeMeta)): +class Recipe(metaclass=RecipeMeta): _url = None '''The address from which the recipe may be downloaded. This is not essential, it may be omitted if the source is available some other @@ -123,8 +122,8 @@ class Recipe(with_metaclass(RecipeMeta)): """ need_stl_shared = False - '''Some libraries or python packages may need to be linked with android's - stl. We can automatically do this for any recipe if we set this property to + '''Some libraries or python packages may need the c++_shared in APK. + We can automatically do this for any recipe if we set this property to `True`''' stl_lib_name = 'c++_shared' @@ -492,20 +491,6 @@ def get_recipe_env(self, arch=None, with_flags_in_cc=True): if arch is None: arch = self.filtered_archs[0] env = arch.get_env(with_flags_in_cc=with_flags_in_cc) - - if self.need_stl_shared: - env['CPPFLAGS'] = env.get('CPPFLAGS', '') - env['CPPFLAGS'] += ' -I{}'.format(self.ctx.ndk.libcxx_include_dir) - - env['CXXFLAGS'] = env['CFLAGS'] + ' -frtti -fexceptions' - - if with_flags_in_cc: - env['CXX'] += ' -frtti -fexceptions' - - env['LDFLAGS'] += ' -L{}'.format(arch.ndk_lib_dir) - env['LIBS'] = env.get('LIBS', '') + " -l{}".format( - self.stl_lib_name - ) return env def prebuild_arch(self, arch): diff --git a/pythonforandroid/recipes/cppy/__init__.py b/pythonforandroid/recipes/cppy/__init__.py index 5ea065b669..f61e2c2516 100644 --- a/pythonforandroid/recipes/cppy/__init__.py +++ b/pythonforandroid/recipes/cppy/__init__.py @@ -3,10 +3,7 @@ class CppyRecipe(PythonRecipe): site_packages_name = 'cppy' - - # Pin to commit: `Nucleic migration and project documentation`, - # because the official releases are too old, at time of writing - version = '4e0b956' + version = '1.1.0' url = 'https://github.com/nucleic/cppy/archive/{version}.zip' call_hostpython_via_targetpython = False # to be detected by the matplotlib install script diff --git a/pythonforandroid/recipes/ffmpeg/__init__.py b/pythonforandroid/recipes/ffmpeg/__init__.py index d58f1e90aa..9414552f0b 100644 --- a/pythonforandroid/recipes/ffmpeg/__init__.py +++ b/pythonforandroid/recipes/ffmpeg/__init__.py @@ -82,7 +82,7 @@ def build_arch(self, arch): '--enable-parser=aac,ac3,h261,h264,mpegaudio,mpeg4video,mpegvideo,vc1', '--enable-decoder=aac,h264,mpeg4,mpegvideo', '--enable-muxer=h264,mov,mp4,mpeg2video', - '--enable-demuxer=aac,h264,m4v,mov,mpegvideo,vc1', + '--enable-demuxer=aac,h264,m4v,mov,mpegvideo,vc1,rtsp', ] # needed to prevent _ffmpeg.so: version node not found for symbol av_init_packet@LIBAVFORMAT_52 @@ -100,7 +100,7 @@ def build_arch(self, arch): # other flags: flags += [ '--enable-filter=aresample,resample,crop,adelay,volume,scale', - '--enable-protocol=file,http,hls', + '--enable-protocol=file,http,hls,udp,tcp', '--enable-small', '--enable-hwaccels', '--enable-pic', diff --git a/pythonforandroid/recipes/hostpython3/__init__.py b/pythonforandroid/recipes/hostpython3/__init__.py index 8cda969f3a..ef2324aea4 100644 --- a/pythonforandroid/recipes/hostpython3/__init__.py +++ b/pythonforandroid/recipes/hostpython3/__init__.py @@ -67,7 +67,7 @@ def get_recipe_env(self, arch=None): openssl_prereq = OpenSSLPrerequisite() if env.get("PKG_CONFIG_PATH", ""): env["PKG_CONFIG_PATH"] = os.pathsep.join( - openssl_prereq.pkg_config_location, env["PKG_CONFIG_PATH"] + [openssl_prereq.pkg_config_location, env["PKG_CONFIG_PATH"]] ) else: env["PKG_CONFIG_PATH"] = openssl_prereq.pkg_config_location diff --git a/pythonforandroid/recipes/kiwisolver/__init__.py b/pythonforandroid/recipes/kiwisolver/__init__.py index a65c4d7ee3..587c2b9a49 100644 --- a/pythonforandroid/recipes/kiwisolver/__init__.py +++ b/pythonforandroid/recipes/kiwisolver/__init__.py @@ -3,28 +3,9 @@ class KiwiSolverRecipe(CppCompiledComponentsPythonRecipe): site_packages_name = 'kiwisolver' - # Pin to commit `docs: attempt to fix doc building`, the latest one - # at the time of writing, just to be sure that we have te most up to date - # version, but it should be pinned to an official release once the c++ - # changes that we want to include are merged to master branch - # Note: the commit we want to include is - # `Cppy use update and c++11 compatibility` (4858730) - version = '0846189' + version = '1.3.2' url = 'https://github.com/nucleic/kiwi/archive/{version}.zip' depends = ['cppy'] - def get_recipe_env(self, arch=None, with_flags_in_cc=True): - env = super().get_recipe_env(arch, with_flags_in_cc) - if self.need_stl_shared: - # kiwisolver compile flags does not honor the standard flags: - # `CPPFLAGS` and `LDLIBS`, so we put in `CFLAGS` and `LDFLAGS` to - # correctly link with the `c++_shared` library - env['CFLAGS'] += f' -I{self.ctx.ndk.libcxx_include_dir}' - env['CFLAGS'] += ' -frtti -fexceptions' - - env['LDFLAGS'] += f' -L{arch.ndk_lib_dir}' - env['LDFLAGS'] += f' -l{self.stl_lib_name}' - return env - recipe = KiwiSolverRecipe() diff --git a/pythonforandroid/recipes/liblzma/__init__.py b/pythonforandroid/recipes/liblzma/__init__.py index 48812b04d4..0b880bc484 100644 --- a/pythonforandroid/recipes/liblzma/__init__.py +++ b/pythonforandroid/recipes/liblzma/__init__.py @@ -6,18 +6,18 @@ from pythonforandroid.archs import Arch from pythonforandroid.logger import shprint from pythonforandroid.recipe import Recipe -from pythonforandroid.util import current_directory, ensure_dir +from pythonforandroid.util import current_directory class LibLzmaRecipe(Recipe): version = '5.2.4' url = 'https://tukaani.org/xz/xz-{version}.tar.gz' - built_libraries = {'liblzma.so': 'install/lib'} + built_libraries = {'liblzma.so': 'p4a_install/lib'} def build_arch(self, arch: Arch) -> None: env = self.get_recipe_env(arch) - install_dir = join(self.get_build_dir(arch.arch), 'install') + install_dir = join(self.get_build_dir(arch.arch), 'p4a_install') with current_directory(self.get_build_dir(arch.arch)): if not exists('configure'): shprint(sh.Command('./autogen.sh'), _env=env) @@ -42,7 +42,6 @@ def build_arch(self, arch: Arch) -> None: _env=env ) - ensure_dir('install') shprint(sh.make, 'install', _env=env) def get_library_includes(self, arch: Arch) -> str: @@ -52,7 +51,7 @@ def get_library_includes(self, arch: Arch) -> str: variable `CPPFLAGS`. """ return " -I" + join( - self.get_build_dir(arch.arch), 'install', 'include', + self.get_build_dir(arch.arch), 'p4a_install', 'include', ) def get_library_ldflags(self, arch: Arch) -> str: diff --git a/pythonforandroid/recipes/matplotlib/__init__.py b/pythonforandroid/recipes/matplotlib/__init__.py index a5f2094db3..f79cde3483 100644 --- a/pythonforandroid/recipes/matplotlib/__init__.py +++ b/pythonforandroid/recipes/matplotlib/__init__.py @@ -1,29 +1,18 @@ - -from pythonforandroid.logger import info_notify from pythonforandroid.recipe import CppCompiledComponentsPythonRecipe from pythonforandroid.util import ensure_dir from os.path import join +import shutil class MatplotlibRecipe(CppCompiledComponentsPythonRecipe): - version = '3.1.3' + version = '3.5.2' url = 'https://github.com/matplotlib/matplotlib/archive/v{version}.zip' - depends = ['numpy', 'png', 'setuptools', 'freetype', 'kiwisolver'] - - python_depends = ['pyparsing', 'cycler', 'python-dateutil'] - - # We need to patch to: - # - make mpl install work without importing numpy - # - make mpl use shared libraries for freetype and png - # - make mpl link to png16, to match p4a library name for png - # - prevent mpl trying to build TkAgg, which wouldn't work - # on Android anyway but has build issues - patches = ['mpl_android_fixes.patch'] + depends = ['kiwisolver', 'numpy', 'pillow', 'setuptools', 'freetype'] - call_hostpython_via_targetpython = False + python_depends = ['cycler', 'fonttools', 'packaging', 'pyparsing', 'python-dateutil'] def generate_libraries_pc_files(self, arch): """ @@ -42,10 +31,9 @@ def generate_libraries_pc_files(self, arch): # version for freetype, but we have our recipe named without # the version...so we add it in here for our pc file 'freetype': 'freetype2.pc', - 'png': 'png.pc', } - for lib_name in {'freetype', 'png'}: + for lib_name in {'freetype'}: pc_template_file = join( self.get_recipe_dir(), f'lib{lib_name}.pc.template' @@ -67,83 +55,26 @@ def generate_libraries_pc_files(self, arch): with open(pc_dest_file, 'w') as pc_file: pc_file.write(text_buffer) - def download_web_backend_dependencies(self, arch): - """ - During building, host needs to download the jquery-ui package (in order - to make it work the mpl web backend). This operation seems to fail - in our CI tests, so we download this package at the expected location - by the mpl install script which is defined by the environ variable - `XDG_CACHE_HOME` (we modify that one in our `get_recipe_env` so it will - be the same regardless of the host platform). - """ - - env = self.get_recipe_env(arch) - - info_notify('Downloading jquery-ui for matplatlib web backend') - # We use the same jquery-ui version than mpl's setup.py script, - # inside function `_download_jquery_to` - jquery_sha = ( - 'f8233674366ab36b2c34c577ec77a3d70cac75d2e387d8587f3836345c0f624d' - ) - url = "https://jqueryui.com/resources/download/jquery-ui-1.12.1.zip" - target_file = join(env['XDG_CACHE_HOME'], 'matplotlib', jquery_sha) - - info_notify(f'Will download into {env["XDG_CACHE_HOME"]}') - ensure_dir(join(env['XDG_CACHE_HOME'], 'matplotlib')) - self.download_file(url, target_file) - def prebuild_arch(self, arch): - with open(join(self.get_recipe_dir(), 'setup.cfg.template')) as fileh: - setup_cfg = fileh.read() - - with open(join(self.get_build_dir(arch), 'setup.cfg'), 'w') as fileh: - fileh.write(setup_cfg.format( - ndk_sysroot_usr=join(self.ctx.ndk.sysroot, 'usr'))) - + shutil.copyfile( + join(self.get_recipe_dir(), "setup.cfg.template"), + join(self.get_build_dir(arch), "mplsetup.cfg"), + ) self.generate_libraries_pc_files(arch) - self.download_web_backend_dependencies(arch) def get_recipe_env(self, arch=None, with_flags_in_cc=True): env = super().get_recipe_env(arch, with_flags_in_cc) - if self.need_stl_shared: - # matplotlib compile flags does not honor the standard flags: - # `CPPFLAGS` and `LDLIBS`, so we put in `CFLAGS` and `LDFLAGS` to - # correctly link with the `c++_shared` library - env['CFLAGS'] += ' -I{}'.format(self.ctx.ndk.libcxx_include_dir) - env['CFLAGS'] += ' -frtti -fexceptions' - - env['LDFLAGS'] += ' -L{}'.format(arch.ndk_lib_dir) - env['LDFLAGS'] += ' -l{}'.format(self.stl_lib_name) - - # we modify `XDG_CACHE_HOME` to download `jquery-ui` into that folder, - # or mpl install will fail when trying to download/install it, but if - # we have the proper package already downloaded, it will use the cached - # package to successfully finish the installation. - # Note: this may not be necessary for some local systems, but it is - # for our CI provider: `gh-actions`, which will - # fail trying to download the `jquery-ui` package - env['XDG_CACHE_HOME'] = join(self.get_build_dir(arch), 'p4a_files') + # we make use of the same directory than `XDG_CACHE_HOME`, for our # custom library pc files, so we have all the install files that we # generate at the same place + env['XDG_CACHE_HOME'] = join(self.get_build_dir(arch), 'p4a_files') env['PKG_CONFIG_PATH'] = env['XDG_CACHE_HOME'] - # We set a new environ variable `NUMPY_INCLUDES` to be able to tell - # the matplotlib script where to find our numpy without importing it - # (which will fail, because numpy isn't installed in our hostpython) - env['NUMPY_INCLUDES'] = join( - self.ctx.get_site_packages_dir(arch), - 'numpy', 'core', 'include', - ) - # creating proper *.pc files for our libraries does not seem enough to # success with our build (without depending on system development # libraries), but if we tell the compiler where to find our libraries # and includes, then the install success :) - png = self.get_recipe('png', self.ctx) - env['CFLAGS'] += f' -I{png.get_build_dir(arch)}' - env['LDFLAGS'] += f' -L{join(png.get_build_dir(arch.arch), ".libs")}' - freetype = self.get_recipe('freetype', self.ctx) free_lib_dir = join(freetype.get_build_dir(arch.arch), 'objs', '.libs') free_inc_dir = join(freetype.get_build_dir(arch.arch), 'include') diff --git a/pythonforandroid/recipes/matplotlib/libpng.pc.template b/pythonforandroid/recipes/matplotlib/libpng.pc.template deleted file mode 100644 index 4c7a85bbeb..0000000000 --- a/pythonforandroid/recipes/matplotlib/libpng.pc.template +++ /dev/null @@ -1,10 +0,0 @@ -prefix=path_to_built -exec_prefix=${prefix} -includedir=${prefix} -libdir=${exec_prefix}/.libs - -Name: libpng -Description: The png library -Version: library_version -Cflags: -I${includedir} -Libs: -L${libdir} -lpng16 diff --git a/pythonforandroid/recipes/matplotlib/mpl_android_fixes.patch b/pythonforandroid/recipes/matplotlib/mpl_android_fixes.patch deleted file mode 100644 index 675956ac12..0000000000 --- a/pythonforandroid/recipes/matplotlib/mpl_android_fixes.patch +++ /dev/null @@ -1,60 +0,0 @@ ---- matplotlib-3.1.1/setupext.py.orig 2019-12-31 01:25:00.000000000 +0100 -+++ matplotlib-3.1.1/setupext.py 2020-03-01 21:12:41.493350250 +0100 -@@ -604,8 +604,7 @@ class Numpy(SetupPackage): - name = "numpy" - - def add_flags(self, ext): -- import numpy as np -- ext.include_dirs.append(np.get_include()) -+ ext.include_dirs.append(os.environ['NUMPY_INCLUDES']) - ext.define_macros.extend([ - # Ensure that PY_ARRAY_UNIQUE_SYMBOL is uniquely defined for each - # extension. -@@ -617,7 +616,7 @@ class Numpy(SetupPackage): - ]) - - def get_setup_requires(self): -- return ['numpy>=1.11'] -+ return [] # don't need it for p4a, due to above changes - - def get_install_requires(self): - return ['numpy>=1.11'] -@@ -674,7 +673,7 @@ class FreeType(SetupPackage): - if sys.platform == 'win32': - libfreetype = 'libfreetype.lib' - else: -- libfreetype = 'libfreetype.a' -+ libfreetype = 'libfreetype.so' - ext.extra_objects.insert( - 0, os.path.join(src_path, 'objs', '.libs', libfreetype)) - ext.define_macros.append(('FREETYPE_BUILD_TYPE', 'local')) -@@ -701,7 +700,7 @@ class FreeType(SetupPackage): - if sys.platform == 'win32': - libfreetype = 'libfreetype.lib' - else: -- libfreetype = 'libfreetype.a' -+ libfreetype = 'libfreetype.so' - - # bailing because it is already built - if os.path.isfile(os.path.join( -@@ -830,7 +829,7 @@ class Png(SetupPackage): - ext, 'libpng', - atleast_version='1.2', - alt_exec=['libpng-config', '--ldflags'], -- default_libraries=['png', 'z']) -+ default_libraries=['png16', 'z']) # adapted to p4a's png library - Numpy().add_flags(ext) - return ext - -@@ -957,9 +956,10 @@ class BackendAgg(OptionalBackendPackage) - - class BackendTkAgg(OptionalBackendPackage): - name = "tkagg" -- force = True -+ force = False - - def check(self): -+ raise CheckFailed("Disabled by patching during Android build") # tk doesn't work on Android but causes build problems - return "installing; run-time loading from Python Tcl/Tk" - - def get_extension(self): diff --git a/pythonforandroid/recipes/matplotlib/setup.cfg.template b/pythonforandroid/recipes/matplotlib/setup.cfg.template index 68ff2f7782..96ef80d4d2 100644 --- a/pythonforandroid/recipes/matplotlib/setup.cfg.template +++ b/pythonforandroid/recipes/matplotlib/setup.cfg.template @@ -1,93 +1,38 @@ -# Rename this file to setup.cfg to modify Matplotlib's -# build options. - -[egg_info] - -[directories] -# Uncomment to override the default basedir in setupext.py. -# This can be a single directory or a comma-delimited list of directories. -basedirlist = {ndk_sysroot_usr} - -[test] -# If you plan to develop Matplotlib and run or add to the test suite, -# set this to True. It will download and build a specific version of -# FreeType, and then use that to build the ft2font extension. This -# ensures that test images are exactly reproducible. -# local_freetype = True - -[status] -# To suppress display of the dependencies and their versions -# at the top of the build log, uncomment the following line: -#suppress = True +# Rename this file to mplsetup.cfg to modify Matplotlib's build options. + +[libs] +# By default, Matplotlib builds with LTO, which may be slow if you re-compile +# often, and don't need the space saving/speedup. +enable_lto = False +# By default, Matplotlib downloads and builds its own copies of FreeType and of +# Qhull. You may set the following to True to instead link against a system +# FreeType/Qhull. As an exception, Matplotlib defaults to the system version +# of FreeType on AIX. +system_freetype = True +#system_qhull = False [packages] -# There are a number of subpackages of Matplotlib that are considered -# optional. All except tests are installed by default, but that can -# be changed here. -# -tests = False -sample_data = False -#toolkits = True -# Tests for the toolkits are only automatically installed -# if the tests and toolkits packages are also getting installed. -toolkits_tests = False +# There are a number of data subpackages from Matplotlib that are +# considered optional. All except 'tests' data (meaning the baseline +# image files) are installed by default, but that can be changed here. +#tests = False [gui_support] -# Matplotlib supports multiple GUI toolkits, including -# GTK3, MacOSX, Qt4, Qt5, Tk, and WX. Support for many of -# these toolkits requires AGG, the Anti-Grain Geometry library, -# which is provided by Matplotlib and built by default. -# -# Some backends are written in pure Python, and others require -# extension code to be compiled. By default, Matplotlib checks for -# these GUI toolkits during installation and, if present, compiles the -# required extensions to support the toolkit. -# -# - Tk support requires Tk development headers and Tkinter. -# - Mac OSX backend requires the Cocoa headers included with XCode. -# - Windowing is MS-Windows specific, and requires the "windows.h" -# header. +# Matplotlib supports multiple GUI toolkits, known as backends. +# The MacOSX backend requires the Cocoa headers included with XCode. +# You can select whether to build it by uncommenting the following line. +# It is never built on Linux or Windows, regardless of the config value. # -# The other GUI toolkits do not require any extension code, and can be -# used as long as the libraries are installed on your system -- -# therefore they are installed unconditionally. -# -# You can uncomment any the following lines to change this -# behavior. Acceptable values are: -# -# True: build the extension. Exits with a warning if the -# required dependencies are not available -# False: do not build the extension -# auto: build if the required dependencies are available, -# otherwise skip silently. This is the default -# behavior -# -agg = True -cairo = False -gtk3agg = False -gtk3cairo = False macosx = False -pyside = False -qt4agg = False -tkagg = False -windowing = False -wxagg = False [rc_options] # User-configurable options # -# Default backend, one of: Agg, Cairo, GTK3Agg, GTK3Cairo, MacOSX, Pdf, Ps, -# Qt4Agg, Qt5Agg, SVG, TkAgg, WX, WXAgg. +# Default backend, one of: Agg, Cairo, GTK3Agg, GTK3Cairo, GTK4Agg, GTK4Cairo, +# MacOSX, Pdf, Ps, QtAgg, QtCairo, SVG, TkAgg, WX, WXAgg. # -# The Agg, Ps, Pdf and SVG backends do not require external dependencies. Do -# not choose MacOSX, or TkAgg if you have disabled the relevant extension -# modules. Agg will be used by default. -# -backend = Agg -# - -[package_data] -# Package additional files found in the lib/matplotlib directories. +# The Agg, Ps, Pdf and SVG backends do not require external dependencies. Do +# not choose MacOSX if you have disabled the relevant extension modules. The +# default is determined by fallback. # -# On Windows, package DLL files. -#dlls = True +#backend = Agg \ No newline at end of file diff --git a/pythonforandroid/toolchain.py b/pythonforandroid/toolchain.py index 7e19aef2b7..1b81aa923c 100644 --- a/pythonforandroid/toolchain.py +++ b/pythonforandroid/toolchain.py @@ -29,8 +29,7 @@ def check_python_dependencies(): ok = True - modules = [('colorama', '0.3.3'), 'appdirs', ('sh', '1.10'), 'jinja2', - 'six'] + modules = [('colorama', '0.3.3'), 'appdirs', ('sh', '1.10'), 'jinja2'] for module in modules: if isinstance(module, tuple): @@ -720,7 +719,7 @@ def add_parser(subparsers, *args, **kwargs): self._archs = args.arch - self.ctx.local_recipes = args.local_recipes + self.ctx.local_recipes = realpath(args.local_recipes) self.ctx.copy_libs = args.copy_libs self.ctx.activity_class_name = args.activity_class_name @@ -990,7 +989,8 @@ def _fix_args(args): """ fix_args = ('--dir', '--private', '--add-jar', '--add-source', - '--whitelist', '--blacklist', '--presplash', '--icon') + '--whitelist', '--blacklist', '--presplash', '--icon', + '--icon-bg', '--icon-fg') unknown_args = args.unknown_args for asset in args.assets: @@ -1147,7 +1147,7 @@ def _finish_package(self, args, output, build_args, package_type, output_dir): if package_add_version: info('# Add version number to android package') package_name = basename(package_file)[:-len(package_extension)] - package_file_dest = "{}-{}-{}".format( + package_file_dest = "{}-{}{}".format( package_name, build_args.version, package_extension) info('# Android package renamed to {}'.format(package_file_dest)) shprint(sh.cp, package_file, package_file_dest) diff --git a/setup.py b/setup.py index 18bd3e4e45..fb3f24d535 100644 --- a/setup.py +++ b/setup.py @@ -20,8 +20,8 @@ # must be a single statement since buildozer is currently parsing it, refs: # https://github.com/kivy/buildozer/issues/722 install_reqs = [ - 'appdirs', 'colorama>=0.3.3', 'jinja2', 'six', - 'enum34; python_version<"3.4"', 'sh>=1.10; sys_platform!="nt"', + 'appdirs', 'colorama>=0.3.3', 'jinja2', + 'sh>=1.10; sys_platform!="nt"', 'pep517<0.7.0', 'toml', ] # (pep517 and toml are used by pythonpackage.py) @@ -89,7 +89,7 @@ def recursively_include(results, directory, patterns): description='Android APK packager for Python scripts and apps', long_description=long_description, long_description_content_type='text/markdown', - python_requires=">=3.6.0", + python_requires=">=3.7.0", author='The Kivy team', author_email='kivy-dev@googlegroups.com', url='https://github.com/kivy/python-for-android', @@ -117,9 +117,10 @@ def recursively_include(results, directory, patterns): 'Operating System :: Android', 'Programming Language :: C', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', 'Topic :: Software Development', 'Topic :: Utilities', ], diff --git a/tests/recipes/test_liblzma.py b/tests/recipes/test_liblzma.py index 90d2496b7f..9f4f6ce0dc 100644 --- a/tests/recipes/test_liblzma.py +++ b/tests/recipes/test_liblzma.py @@ -15,7 +15,7 @@ def test_get_library_includes(self): recipe_build_dir = self.recipe.get_build_dir(self.arch.arch) self.assertEqual( self.recipe.get_library_includes(self.arch), - f" -I{join(recipe_build_dir, 'install/include')}", + f" -I{join(recipe_build_dir, 'p4a_install/include')}", ) def test_get_library_ldflags(self): @@ -25,7 +25,7 @@ def test_get_library_ldflags(self): recipe_build_dir = self.recipe.get_build_dir(self.arch.arch) self.assertEqual( self.recipe.get_library_ldflags(self.arch), - f" -L{join(recipe_build_dir, 'install/lib')}", + f" -L{join(recipe_build_dir, 'p4a_install/lib')}", ) def test_link_libs_flags(self): @@ -33,3 +33,19 @@ def test_link_libs_flags(self): Test :meth:`~pythonforandroid.recipes.liblzma.get_library_libs_flag`. """ self.assertEqual(self.recipe.get_library_libs_flag(), " -llzma") + + def test_install_dir_not_named_install(self): + """ + Tests that the install directory is not named ``install``. + + liblzma already have a file named ``INSTALL`` in its source directory. + On case-insensitive filesystems, using a folder named ``install`` will + cause a conflict. (See issue: #2343). + + WARNING: This test is quite flaky, but should be enough to + ensure that someone in the future will not accidentally rename + the install directory without seeing this test to fail. + """ + liblzma_install_dir = self.recipe.built_libraries["liblzma.so"] + + self.assertNotIn("install", liblzma_install_dir.split("/")) diff --git a/tests/test_graph.py b/tests/test_graph.py index 9cc44fc041..f7647bcac7 100644 --- a/tests/test_graph.py +++ b/tests/test_graph.py @@ -115,7 +115,7 @@ def test_get_dependency_tuple_list_for_recipe(monkeypatch): dep_list = get_dependency_tuple_list_for_recipe( r, blacklist={"libffi"} ) - assert(dep_list == [("pillow",)]) + assert dep_list == [("pillow",)] @pytest.mark.parametrize('names,bootstrap', valid_combinations) diff --git a/tests/test_recipe.py b/tests/test_recipe.py index 5194289d60..666d089caa 100644 --- a/tests/test_recipe.py +++ b/tests/test_recipe.py @@ -279,25 +279,6 @@ def test_get_recipe_env_with( ) self.assertIsInstance(env, dict) - # check `CPPFLAGS` - expected_cppflags = { - '-I{libcxx_include}'.format(libcxx_include=self.ctx.ndk.libcxx_include_dir) - } - self.assertIn('CPPFLAGS', env) - for flags in expected_cppflags: - self.assertIn(flags, env['CPPFLAGS']) - - # check `LIBS` - self.assertIn('LDFLAGS', env) - self.assertIn('-L' + arch.ndk_lib_dir, env['LDFLAGS']) - self.assertIn('LIBS', env) - self.assertIn('-lc++_shared', env['LIBS']) - - # check `CXXFLAGS` and `CXX` - for flag in {'CXXFLAGS', 'CXX'}: - self.assertIn(flag, env) - self.assertIn('-frtti -fexceptions', env[flag]) - @mock.patch('pythonforandroid.recipe.Recipe.install_libs') @mock.patch('pythonforandroid.recipe.isfile') @mock.patch('pythonforandroid.build.ensure_dir') diff --git a/tests/test_toolchain.py b/tests/test_toolchain.py index 23d0d3ff9e..0cc2b1a7f5 100644 --- a/tests/test_toolchain.py +++ b/tests/test_toolchain.py @@ -1,4 +1,5 @@ import io +import os import sys import pytest from unittest import mock @@ -136,3 +137,34 @@ def test_recipes(self): assert expected_string in m_stdout.getvalue() # deletes static attribute to not mess with other tests del Recipe.recipes + + def test_local_recipes_dir(self): + """ + Checks the `local_recipes` attribute in the Context is absolute. + """ + cwd = os.path.realpath(os.getcwd()) + common_args = [ + 'toolchain.py', + 'recommendations', + ] + + # Check the default ./p4a-recipes becomes absolute. + argv = common_args + with patch_sys_argv(argv): + toolchain = ToolchainCL() + expected_local_recipes = os.path.join(cwd, 'p4a-recipes') + assert toolchain.ctx.local_recipes == expected_local_recipes + + # Check a supplied relative directory becomes absolute. + argv = common_args + ['--local-recipes=foo'] + with patch_sys_argv(argv): + toolchain = ToolchainCL() + expected_local_recipes = os.path.join(cwd, 'foo') + assert toolchain.ctx.local_recipes == expected_local_recipes + + # An absolute directory should remain unchanged. + local_recipes = os.path.join(cwd, 'foo') + argv = common_args + ['--local-recipes={}'.format(local_recipes)] + with patch_sys_argv(argv): + toolchain = ToolchainCL() + assert toolchain.ctx.local_recipes == local_recipes