From 78f76cb5d1677ac80157b14bf7aa8a896dd6a809 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Fri, 17 Oct 2025 20:06:03 +1300 Subject: [PATCH 01/42] Use `pip install` instead of `setup.py install` in `recipe.py` On Python 3.14, many recipes, notably `msgpack`, no longer byte- compile with `setup.py install`, because the way that command byte-compiles relies on `distutils`, which is gone. --- pythonforandroid/recipe.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 5ca2d91a7..2b399bee1 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -1020,18 +1020,11 @@ def install_python_package(self, arch, name=None, env=None, is_dir=True): hostpython = sh.Command(self.hostpython_location) hpenv = env.copy() with current_directory(self.get_build_dir(arch.arch)): - - if isfile("setup.py"): - shprint(hostpython, 'setup.py', 'install', '-O2', - '--root={}'.format(self.ctx.get_python_install_dir(arch.arch)), - '--install-lib=.', - _env=hpenv, *self.setup_extra_args) - - # If asked, also install in the hostpython build dir - if self.install_in_hostpython: - self.install_hostpython_package(arch) - else: - warning("`PythonRecipe.install_python_package` called without `setup.py` file!") + shprint(hostpython, '-m', 'pip', 'install', '.', + '--compile', + f'--root={self.ctx.get_python_install_dir(arch.arch)}', + _env=hpenv, *self.setup_extra_args + ) def get_hostrecipe_env(self, arch=None): env = environ.copy() @@ -1048,7 +1041,8 @@ def hostpython_site_dir(self): def install_hostpython_package(self, arch): env = self.get_hostrecipe_env(arch) real_hostpython = sh.Command(self.real_hostpython_location) - shprint(real_hostpython, 'setup.py', 'install', '-O2', + shprint(real_hostpython, '-m', 'pip', 'install', '.', + '--compile', '--install-lib=Lib/site-packages', '--root={}'.format(self._host_recipe.site_root), _env=env, *self.setup_extra_args) @@ -1095,7 +1089,7 @@ class CompiledComponentsPythonRecipe(PythonRecipe): def build_arch(self, arch): '''Build any cython components, then install the Python module by - calling setup.py install with the target Python dir. + calling pip install with the target Python dir. ''' Recipe.build_arch(self, arch) self.install_hostpython_prerequisites() @@ -1144,7 +1138,7 @@ class CythonRecipe(PythonRecipe): def build_arch(self, arch): '''Build any cython components, then install the Python module by - calling setup.py install with the target Python dir. + calling pip install with the target Python dir. ''' Recipe.build_arch(self, arch) self.build_cython_components(arch) From 2780fc32673b78ff0155faa32cb4dfad2d8520f3 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Wed, 22 Oct 2025 14:23:42 +1300 Subject: [PATCH 02/42] Import from setuptools and not distutils in the `android` recipe --- pythonforandroid/recipes/android/src/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/recipes/android/src/setup.py b/pythonforandroid/recipes/android/src/setup.py index 8bf4512e0..8182ba9c5 100755 --- a/pythonforandroid/recipes/android/src/setup.py +++ b/pythonforandroid/recipes/android/src/setup.py @@ -1,4 +1,4 @@ -from distutils.core import setup, Extension +from setuptools import setup, Extension from Cython.Build import cythonize import os From 6d1b9d38260a56e58558b81038fea820e2997a96 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Wed, 22 Oct 2025 14:25:19 +1300 Subject: [PATCH 03/42] Remove freetype's distutils "fallback" It was actually using distutils every time, apparently? --- pythonforandroid/recipes/freetype-py/__init__.py | 1 - .../freetype-py/fall-back-to-distutils.patch | 15 --------------- 2 files changed, 16 deletions(-) delete mode 100644 pythonforandroid/recipes/freetype-py/fall-back-to-distutils.patch diff --git a/pythonforandroid/recipes/freetype-py/__init__.py b/pythonforandroid/recipes/freetype-py/__init__.py index 7be2f2e10..2e737f37b 100644 --- a/pythonforandroid/recipes/freetype-py/__init__.py +++ b/pythonforandroid/recipes/freetype-py/__init__.py @@ -5,7 +5,6 @@ class FreetypePyRecipe(PythonRecipe): version = '2.2.0' url = 'https://github.com/rougier/freetype-py/archive/refs/tags/v{version}.tar.gz' depends = ['freetype'] - patches = ['fall-back-to-distutils.patch'] site_packages_name = 'freetype' diff --git a/pythonforandroid/recipes/freetype-py/fall-back-to-distutils.patch b/pythonforandroid/recipes/freetype-py/fall-back-to-distutils.patch deleted file mode 100644 index 0f06f1854..000000000 --- a/pythonforandroid/recipes/freetype-py/fall-back-to-distutils.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff -ruN freetype-py.orig/setup.py freetype-py/setup.py ---- freetype-py.orig/setup.py 2020-07-09 20:58:51.000000000 +0700 -+++ freetype-py/setup.py 2022-03-02 19:28:17.948831134 +0700 -@@ -12,7 +12,10 @@ - from io import open - from os import path - --from setuptools import setup -+try: -+ from setuptools import setup -+except ImportError: -+ from distutils.core import setup - - if os.environ.get("FREETYPEPY_BUNDLE_FT"): - print("# Will build and bundle FreeType.") From b8eaa1d8bc682d44d84ad092cc4f0d3194f5f6d4 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Wed, 22 Oct 2025 14:57:47 +1300 Subject: [PATCH 04/42] Bump `freetype` version to match that of `freetype-py` --- pythonforandroid/recipes/freetype/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/recipes/freetype/__init__.py b/pythonforandroid/recipes/freetype/__init__.py index e5ddfe142..5e226336e 100644 --- a/pythonforandroid/recipes/freetype/__init__.py +++ b/pythonforandroid/recipes/freetype/__init__.py @@ -24,7 +24,7 @@ class FreetypeRecipe(Recipe): https://sourceforge.net/projects/freetype/files/freetype2/2.5.3/ """ - version = '2.10.1' + version = '2.2.0' url = 'https://download.savannah.gnu.org/releases/freetype/freetype-{version}.tar.gz' # noqa built_libraries = {'libfreetype.so': 'objs/.libs'} From 39dff1fd5f986e745139848fcf3d00292d442542 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Wed, 22 Oct 2025 15:25:32 +1300 Subject: [PATCH 05/42] On second thought, use the latest freetype version --- pythonforandroid/recipes/freetype-py/__init__.py | 2 +- pythonforandroid/recipes/freetype/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonforandroid/recipes/freetype-py/__init__.py b/pythonforandroid/recipes/freetype-py/__init__.py index 2e737f37b..e882deb53 100644 --- a/pythonforandroid/recipes/freetype-py/__init__.py +++ b/pythonforandroid/recipes/freetype-py/__init__.py @@ -2,7 +2,7 @@ class FreetypePyRecipe(PythonRecipe): - version = '2.2.0' + version = '2.14.1' url = 'https://github.com/rougier/freetype-py/archive/refs/tags/v{version}.tar.gz' depends = ['freetype'] site_packages_name = 'freetype' diff --git a/pythonforandroid/recipes/freetype/__init__.py b/pythonforandroid/recipes/freetype/__init__.py index 5e226336e..7772b4ffe 100644 --- a/pythonforandroid/recipes/freetype/__init__.py +++ b/pythonforandroid/recipes/freetype/__init__.py @@ -24,7 +24,7 @@ class FreetypeRecipe(Recipe): https://sourceforge.net/projects/freetype/files/freetype2/2.5.3/ """ - version = '2.2.0' + version = '2.14.1' url = 'https://download.savannah.gnu.org/releases/freetype/freetype-{version}.tar.gz' # noqa built_libraries = {'libfreetype.so': 'objs/.libs'} From 9df2658c445897687269fc528950c42ac0cd29fb Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Wed, 22 Oct 2025 15:35:31 +1300 Subject: [PATCH 06/42] Use the latest *released* freetype-py version --- pythonforandroid/recipes/freetype-py/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/recipes/freetype-py/__init__.py b/pythonforandroid/recipes/freetype-py/__init__.py index e882deb53..805cb0a6c 100644 --- a/pythonforandroid/recipes/freetype-py/__init__.py +++ b/pythonforandroid/recipes/freetype-py/__init__.py @@ -2,7 +2,7 @@ class FreetypePyRecipe(PythonRecipe): - version = '2.14.1' + version = '2.5.1' url = 'https://github.com/rougier/freetype-py/archive/refs/tags/v{version}.tar.gz' depends = ['freetype'] site_packages_name = 'freetype' From 92866126c8c49bd5d67cec63da5a0e4d71a1677e Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Wed, 22 Oct 2025 16:11:33 +1300 Subject: [PATCH 07/42] Tell setuptools what freetype-py version we have --- pythonforandroid/recipes/freetype-py/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pythonforandroid/recipes/freetype-py/__init__.py b/pythonforandroid/recipes/freetype-py/__init__.py index 805cb0a6c..55bf66c31 100644 --- a/pythonforandroid/recipes/freetype-py/__init__.py +++ b/pythonforandroid/recipes/freetype-py/__init__.py @@ -7,5 +7,10 @@ class FreetypePyRecipe(PythonRecipe): depends = ['freetype'] site_packages_name = 'freetype' + def get_recipe_env(self, arch=None, with_flags_in_cc=True): + env = super().get_recipe_env(arch, with_flags_in_cc) + env["SETUPTOOLS_SCM_PRETEND_VERSION_FOR_freetype"] = self.version + return env + recipe = FreetypePyRecipe() From 8102b3348f1779df7eaddef2d729a02884159f16 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Thu, 23 Oct 2025 12:05:05 +1300 Subject: [PATCH 08/42] Turn `FreetypePyRecipe` into a `PyProjectRecipe` I don't think this will require the version hack --- pythonforandroid/recipes/freetype-py/__init__.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/pythonforandroid/recipes/freetype-py/__init__.py b/pythonforandroid/recipes/freetype-py/__init__.py index 55bf66c31..d4833bd7f 100644 --- a/pythonforandroid/recipes/freetype-py/__init__.py +++ b/pythonforandroid/recipes/freetype-py/__init__.py @@ -1,16 +1,11 @@ -from pythonforandroid.recipe import PythonRecipe +from pythonforandroid.recipe import PyProjectRecipe -class FreetypePyRecipe(PythonRecipe): +class FreetypePyRecipe(PyProjectRecipe): version = '2.5.1' url = 'https://github.com/rougier/freetype-py/archive/refs/tags/v{version}.tar.gz' depends = ['freetype'] site_packages_name = 'freetype' - def get_recipe_env(self, arch=None, with_flags_in_cc=True): - env = super().get_recipe_env(arch, with_flags_in_cc) - env["SETUPTOOLS_SCM_PRETEND_VERSION_FOR_freetype"] = self.version - return env - recipe = FreetypePyRecipe() From e812a42a29d1cdd76b5d17d44e99f4b52a99460d Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Thu, 23 Oct 2025 12:37:24 +1300 Subject: [PATCH 09/42] Put the `setuptools-scm` version hack back in --- pythonforandroid/recipes/freetype-py/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pythonforandroid/recipes/freetype-py/__init__.py b/pythonforandroid/recipes/freetype-py/__init__.py index d4833bd7f..dd3597081 100644 --- a/pythonforandroid/recipes/freetype-py/__init__.py +++ b/pythonforandroid/recipes/freetype-py/__init__.py @@ -7,5 +7,10 @@ class FreetypePyRecipe(PyProjectRecipe): depends = ['freetype'] site_packages_name = 'freetype' + def get_recipe_env(self, arch=None, with_flags_in_cc=True): + env = super().get_recipe_env(arch, with_flags_in_cc) + env["SETUPTOOLS_SCM_PRETEND_VERSION_FOR_freetype"] = self.version + return env + recipe = FreetypePyRecipe() From fbdf8c2e3c95ef72fb6c707eea042ce7549538ed Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Thu, 23 Oct 2025 13:18:22 +1300 Subject: [PATCH 10/42] Fix `FreetypePyRecipe.get_recipe_env` --- pythonforandroid/recipes/freetype-py/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonforandroid/recipes/freetype-py/__init__.py b/pythonforandroid/recipes/freetype-py/__init__.py index dd3597081..57ba47646 100644 --- a/pythonforandroid/recipes/freetype-py/__init__.py +++ b/pythonforandroid/recipes/freetype-py/__init__.py @@ -7,8 +7,8 @@ class FreetypePyRecipe(PyProjectRecipe): depends = ['freetype'] site_packages_name = 'freetype' - def get_recipe_env(self, arch=None, with_flags_in_cc=True): - env = super().get_recipe_env(arch, with_flags_in_cc) + def get_recipe_env(self, arch=None, **kwargs): + env = super().get_recipe_env(arch, **kwargs) env["SETUPTOOLS_SCM_PRETEND_VERSION_FOR_freetype"] = self.version return env From 0d0d9f317d571171ab26a170aac327fa61e0ac8f Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Thu, 23 Oct 2025 14:23:37 +1300 Subject: [PATCH 11/42] Switch to pythonhosted FreetypePy `setuptools-scm` can't deal with the github hosted one, it seems. --- pythonforandroid/recipes/freetype-py/__init__.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pythonforandroid/recipes/freetype-py/__init__.py b/pythonforandroid/recipes/freetype-py/__init__.py index 57ba47646..3d2d66e89 100644 --- a/pythonforandroid/recipes/freetype-py/__init__.py +++ b/pythonforandroid/recipes/freetype-py/__init__.py @@ -3,14 +3,11 @@ class FreetypePyRecipe(PyProjectRecipe): version = '2.5.1' - url = 'https://github.com/rougier/freetype-py/archive/refs/tags/v{version}.tar.gz' + url = ('https://files.pythonhosted.org/packages/d0/9c/' + '61ba17f846b922c2d6d101cc886b0e8fb597c109cedfcb39b8c5d2304b54/' + 'freetype-py-{version}.zip') depends = ['freetype'] site_packages_name = 'freetype' - def get_recipe_env(self, arch=None, **kwargs): - env = super().get_recipe_env(arch, **kwargs) - env["SETUPTOOLS_SCM_PRETEND_VERSION_FOR_freetype"] = self.version - return env - recipe = FreetypePyRecipe() From d07f168c8afc0465154bff185e3fedf086a2b038 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Thu, 23 Oct 2025 14:49:03 +1300 Subject: [PATCH 12/42] Update apsw recipe, use pythonhosted package --- pythonforandroid/recipes/apsw/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pythonforandroid/recipes/apsw/__init__.py b/pythonforandroid/recipes/apsw/__init__.py index 42ad3ba33..359b4e1c4 100644 --- a/pythonforandroid/recipes/apsw/__init__.py +++ b/pythonforandroid/recipes/apsw/__init__.py @@ -4,8 +4,10 @@ class ApswRecipe(PythonRecipe): - version = '3.15.0-r1' - url = 'https://github.com/rogerbinns/apsw/archive/{version}.tar.gz' + version = '3.50.4.0' + url = ('https://files.pythonhosted.org/packages/02/ea/' + '7469e89d75a07972255aac4c1b98675bfbc74df32a19dd5dc8ba87aa552b/' + 'apsw-{version}.tar.gz') depends = ['sqlite3', 'setuptools'] call_hostpython_via_targetpython = False site_packages_name = 'apsw' From 16008f9469c2cf06a77218720dd8ebb280ac8004 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Thu, 23 Oct 2025 15:06:13 +1300 Subject: [PATCH 13/42] Fix `LibffiRecipe.get_include_dirs` for `arch=None` --- pythonforandroid/recipes/libffi/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pythonforandroid/recipes/libffi/__init__.py b/pythonforandroid/recipes/libffi/__init__.py index 767881b79..79b59f756 100644 --- a/pythonforandroid/recipes/libffi/__init__.py +++ b/pythonforandroid/recipes/libffi/__init__.py @@ -35,6 +35,8 @@ def build_arch(self, arch): shprint(sh.make, '-j', str(cpu_count()), 'libffi.la', _env=env) def get_include_dirs(self, arch): + if arch is None: + arch = self.filtered_archs[0] return [join(self.get_build_dir(arch.arch), 'include')] From e4cf526c94147f8b094ceb4b74019d97ba830e8a Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Thu, 23 Oct 2025 15:30:22 +1300 Subject: [PATCH 14/42] Use recent libffi and cffi --- pythonforandroid/recipes/cffi/__init__.py | 2 +- pythonforandroid/recipes/libffi/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonforandroid/recipes/cffi/__init__.py b/pythonforandroid/recipes/cffi/__init__.py index f0c25a92c..bafc67277 100644 --- a/pythonforandroid/recipes/cffi/__init__.py +++ b/pythonforandroid/recipes/cffi/__init__.py @@ -7,7 +7,7 @@ class CffiRecipe(CompiledComponentsPythonRecipe): Extra system dependencies: autoconf, automake and libtool. """ name = 'cffi' - version = '1.15.1' + version = '2.0.0' url = 'https://pypi.python.org/packages/source/c/cffi/cffi-{version}.tar.gz' depends = ['setuptools', 'pycparser', 'libffi'] diff --git a/pythonforandroid/recipes/libffi/__init__.py b/pythonforandroid/recipes/libffi/__init__.py index 79b59f756..0aa75de5e 100644 --- a/pythonforandroid/recipes/libffi/__init__.py +++ b/pythonforandroid/recipes/libffi/__init__.py @@ -14,7 +14,7 @@ class LibffiRecipe(Recipe): - `libltdl-dev` which defines the `LT_SYS_SYMBOL_USCORE` macro """ name = 'libffi' - version = 'v3.4.2' + version = 'v3.5.2' url = 'https://github.com/libffi/libffi/archive/{version}.tar.gz' patches = ['remove-version-info.patch'] From 344d6b8e5ff66035790637be78305bf7ed6aecb9 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Thu, 23 Oct 2025 15:30:30 +1300 Subject: [PATCH 15/42] Require setuptools for every recipe --- pythonforandroid/recipe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 2b399bee1..5919651f5 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -878,7 +878,7 @@ class PythonRecipe(Recipe): on python2 or python3 which can break the dependency graph ''' - hostpython_prerequisites = [] + hostpython_prerequisites = ['setuptools'] '''List of hostpython packages required to build a recipe''' _host_recipe = None From d93861cd89bc63e13245d4c2b47e719f78ba3121 Mon Sep 17 00:00:00 2001 From: Ansh Dadwal Date: Wed, 15 Oct 2025 22:01:19 +0530 Subject: [PATCH 16/42] `python`: update to `3.14.0` --- .../src/main/java/org/kivy/android/PythonUtil.java | 2 +- pythonforandroid/recipes/hostpython3/__init__.py | 2 +- pythonforandroid/recipes/python3/__init__.py | 13 ++++--------- pythonforandroid/recipes/six/__init__.py | 10 ---------- 4 files changed, 6 insertions(+), 21 deletions(-) delete mode 100644 pythonforandroid/recipes/six/__init__.py diff --git a/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonUtil.java b/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonUtil.java index 065f43c3b..9e19a6327 100644 --- a/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonUtil.java +++ b/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonUtil.java @@ -51,7 +51,7 @@ protected static ArrayList getLibraries(File libsDir) { addLibraryIfExists(libsList, name, libsDir); } - for (int v = 5; v <= 14; v++) { + for (int v = 14; v >= 5; v--) { libsList.add("python3." + v + (v <= 7 ? "m" : "")); } diff --git a/pythonforandroid/recipes/hostpython3/__init__.py b/pythonforandroid/recipes/hostpython3/__init__.py index 094660fad..afc4df495 100644 --- a/pythonforandroid/recipes/hostpython3/__init__.py +++ b/pythonforandroid/recipes/hostpython3/__init__.py @@ -36,7 +36,7 @@ class HostPython3Recipe(Recipe): :class:`~pythonforandroid.python.HostPythonRecipe` ''' - version = '3.11.13' + version = '3.14.0' url = 'https://github.com/python/cpython/archive/refs/tags/v{version}.tar.gz' '''The default url to download our host python recipe. This url will diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index 81aee7c66..4c234ce8f 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -54,7 +54,7 @@ class Python3Recipe(TargetPythonRecipe): :class:`~pythonforandroid.python.GuestPythonRecipe` ''' - version = '3.11.13' + version = '3.14.0' _p_version = Version(version) url = 'https://github.com/python/cpython/archive/refs/tags/v{version}.tar.gz' name = 'python3' @@ -389,17 +389,12 @@ def create_python_bundle(self, dirn, arch): place. """ # Todo: find a better way to find the build libs folder - modules_build_dir = join( + modules_build_dir = glob.glob(join( self.get_build_dir(arch.arch), 'android-build', 'build', - 'lib.{}{}-{}-{}'.format( - # android is now supported platform - "android" if self._p_version.minor >= 13 else "linux", - '2' if self.version[0] == '2' else '', - arch.command_prefix.split('-')[0], - self.major_minor_version_string - )) + 'lib.*' + ))[0] # Compile to *.pyc the python modules self.compile_python_files(modules_build_dir) diff --git a/pythonforandroid/recipes/six/__init__.py b/pythonforandroid/recipes/six/__init__.py deleted file mode 100644 index 3be8ce757..000000000 --- a/pythonforandroid/recipes/six/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -from pythonforandroid.recipe import PythonRecipe - - -class SixRecipe(PythonRecipe): - version = '1.15.0' - url = 'https://pypi.python.org/packages/source/s/six/six-{version}.tar.gz' - depends = ['setuptools'] - - -recipe = SixRecipe() From 94cecf880298d3a71a0db74cbfbe64354c0dd1bc Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Fri, 17 Oct 2025 20:06:03 +1300 Subject: [PATCH 17/42] Use `pip install` instead of `setup.py install` in `recipe.py` On Python 3.14, many recipes, notably `msgpack`, no longer byte- compile with `setup.py install`, because the way that command byte-compiles relies on `distutils`, which is gone. --- pythonforandroid/recipe.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 5ca2d91a7..2b399bee1 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -1020,18 +1020,11 @@ def install_python_package(self, arch, name=None, env=None, is_dir=True): hostpython = sh.Command(self.hostpython_location) hpenv = env.copy() with current_directory(self.get_build_dir(arch.arch)): - - if isfile("setup.py"): - shprint(hostpython, 'setup.py', 'install', '-O2', - '--root={}'.format(self.ctx.get_python_install_dir(arch.arch)), - '--install-lib=.', - _env=hpenv, *self.setup_extra_args) - - # If asked, also install in the hostpython build dir - if self.install_in_hostpython: - self.install_hostpython_package(arch) - else: - warning("`PythonRecipe.install_python_package` called without `setup.py` file!") + shprint(hostpython, '-m', 'pip', 'install', '.', + '--compile', + f'--root={self.ctx.get_python_install_dir(arch.arch)}', + _env=hpenv, *self.setup_extra_args + ) def get_hostrecipe_env(self, arch=None): env = environ.copy() @@ -1048,7 +1041,8 @@ def hostpython_site_dir(self): def install_hostpython_package(self, arch): env = self.get_hostrecipe_env(arch) real_hostpython = sh.Command(self.real_hostpython_location) - shprint(real_hostpython, 'setup.py', 'install', '-O2', + shprint(real_hostpython, '-m', 'pip', 'install', '.', + '--compile', '--install-lib=Lib/site-packages', '--root={}'.format(self._host_recipe.site_root), _env=env, *self.setup_extra_args) @@ -1095,7 +1089,7 @@ class CompiledComponentsPythonRecipe(PythonRecipe): def build_arch(self, arch): '''Build any cython components, then install the Python module by - calling setup.py install with the target Python dir. + calling pip install with the target Python dir. ''' Recipe.build_arch(self, arch) self.install_hostpython_prerequisites() @@ -1144,7 +1138,7 @@ class CythonRecipe(PythonRecipe): def build_arch(self, arch): '''Build any cython components, then install the Python module by - calling setup.py install with the target Python dir. + calling pip install with the target Python dir. ''' Recipe.build_arch(self, arch) self.build_cython_components(arch) From f0e3a95cc27e89d2586e09c5ced14f3eb163a4bf Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Wed, 22 Oct 2025 14:23:42 +1300 Subject: [PATCH 18/42] Import from setuptools and not distutils in the `android` recipe --- pythonforandroid/recipes/android/src/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/recipes/android/src/setup.py b/pythonforandroid/recipes/android/src/setup.py index 8bf4512e0..8182ba9c5 100755 --- a/pythonforandroid/recipes/android/src/setup.py +++ b/pythonforandroid/recipes/android/src/setup.py @@ -1,4 +1,4 @@ -from distutils.core import setup, Extension +from setuptools import setup, Extension from Cython.Build import cythonize import os From ebe2e2f9379b8e1364f6982e2355ecd5703199bb Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Wed, 22 Oct 2025 14:25:19 +1300 Subject: [PATCH 19/42] Remove freetype's distutils "fallback" It was actually using distutils every time, apparently? --- pythonforandroid/recipes/freetype-py/__init__.py | 1 - .../freetype-py/fall-back-to-distutils.patch | 15 --------------- 2 files changed, 16 deletions(-) delete mode 100644 pythonforandroid/recipes/freetype-py/fall-back-to-distutils.patch diff --git a/pythonforandroid/recipes/freetype-py/__init__.py b/pythonforandroid/recipes/freetype-py/__init__.py index 7be2f2e10..2e737f37b 100644 --- a/pythonforandroid/recipes/freetype-py/__init__.py +++ b/pythonforandroid/recipes/freetype-py/__init__.py @@ -5,7 +5,6 @@ class FreetypePyRecipe(PythonRecipe): version = '2.2.0' url = 'https://github.com/rougier/freetype-py/archive/refs/tags/v{version}.tar.gz' depends = ['freetype'] - patches = ['fall-back-to-distutils.patch'] site_packages_name = 'freetype' diff --git a/pythonforandroid/recipes/freetype-py/fall-back-to-distutils.patch b/pythonforandroid/recipes/freetype-py/fall-back-to-distutils.patch deleted file mode 100644 index 0f06f1854..000000000 --- a/pythonforandroid/recipes/freetype-py/fall-back-to-distutils.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff -ruN freetype-py.orig/setup.py freetype-py/setup.py ---- freetype-py.orig/setup.py 2020-07-09 20:58:51.000000000 +0700 -+++ freetype-py/setup.py 2022-03-02 19:28:17.948831134 +0700 -@@ -12,7 +12,10 @@ - from io import open - from os import path - --from setuptools import setup -+try: -+ from setuptools import setup -+except ImportError: -+ from distutils.core import setup - - if os.environ.get("FREETYPEPY_BUNDLE_FT"): - print("# Will build and bundle FreeType.") From 3ee745b4b3d4a60d5e13e5d59698c8f3fe949ac3 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Wed, 22 Oct 2025 14:57:47 +1300 Subject: [PATCH 20/42] Bump `freetype` version to match that of `freetype-py` --- pythonforandroid/recipes/freetype/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/recipes/freetype/__init__.py b/pythonforandroid/recipes/freetype/__init__.py index e5ddfe142..5e226336e 100644 --- a/pythonforandroid/recipes/freetype/__init__.py +++ b/pythonforandroid/recipes/freetype/__init__.py @@ -24,7 +24,7 @@ class FreetypeRecipe(Recipe): https://sourceforge.net/projects/freetype/files/freetype2/2.5.3/ """ - version = '2.10.1' + version = '2.2.0' url = 'https://download.savannah.gnu.org/releases/freetype/freetype-{version}.tar.gz' # noqa built_libraries = {'libfreetype.so': 'objs/.libs'} From 312382984fd538766b3fada2de5a29024a022d41 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Wed, 22 Oct 2025 15:25:32 +1300 Subject: [PATCH 21/42] On second thought, use the latest freetype version --- pythonforandroid/recipes/freetype-py/__init__.py | 2 +- pythonforandroid/recipes/freetype/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonforandroid/recipes/freetype-py/__init__.py b/pythonforandroid/recipes/freetype-py/__init__.py index 2e737f37b..e882deb53 100644 --- a/pythonforandroid/recipes/freetype-py/__init__.py +++ b/pythonforandroid/recipes/freetype-py/__init__.py @@ -2,7 +2,7 @@ class FreetypePyRecipe(PythonRecipe): - version = '2.2.0' + version = '2.14.1' url = 'https://github.com/rougier/freetype-py/archive/refs/tags/v{version}.tar.gz' depends = ['freetype'] site_packages_name = 'freetype' diff --git a/pythonforandroid/recipes/freetype/__init__.py b/pythonforandroid/recipes/freetype/__init__.py index 5e226336e..7772b4ffe 100644 --- a/pythonforandroid/recipes/freetype/__init__.py +++ b/pythonforandroid/recipes/freetype/__init__.py @@ -24,7 +24,7 @@ class FreetypeRecipe(Recipe): https://sourceforge.net/projects/freetype/files/freetype2/2.5.3/ """ - version = '2.2.0' + version = '2.14.1' url = 'https://download.savannah.gnu.org/releases/freetype/freetype-{version}.tar.gz' # noqa built_libraries = {'libfreetype.so': 'objs/.libs'} From 619287bd3fa517712f2e2025ae61f963bd06964b Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Wed, 22 Oct 2025 15:35:31 +1300 Subject: [PATCH 22/42] Use the latest *released* freetype-py version --- pythonforandroid/recipes/freetype-py/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/recipes/freetype-py/__init__.py b/pythonforandroid/recipes/freetype-py/__init__.py index e882deb53..805cb0a6c 100644 --- a/pythonforandroid/recipes/freetype-py/__init__.py +++ b/pythonforandroid/recipes/freetype-py/__init__.py @@ -2,7 +2,7 @@ class FreetypePyRecipe(PythonRecipe): - version = '2.14.1' + version = '2.5.1' url = 'https://github.com/rougier/freetype-py/archive/refs/tags/v{version}.tar.gz' depends = ['freetype'] site_packages_name = 'freetype' From 3acc7048ea49b076511b3a91749957e5074c7d89 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Wed, 22 Oct 2025 16:11:33 +1300 Subject: [PATCH 23/42] Tell setuptools what freetype-py version we have --- pythonforandroid/recipes/freetype-py/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pythonforandroid/recipes/freetype-py/__init__.py b/pythonforandroid/recipes/freetype-py/__init__.py index 805cb0a6c..55bf66c31 100644 --- a/pythonforandroid/recipes/freetype-py/__init__.py +++ b/pythonforandroid/recipes/freetype-py/__init__.py @@ -7,5 +7,10 @@ class FreetypePyRecipe(PythonRecipe): depends = ['freetype'] site_packages_name = 'freetype' + def get_recipe_env(self, arch=None, with_flags_in_cc=True): + env = super().get_recipe_env(arch, with_flags_in_cc) + env["SETUPTOOLS_SCM_PRETEND_VERSION_FOR_freetype"] = self.version + return env + recipe = FreetypePyRecipe() From cc75b37d923129af90626a266e7f74f9c1668f06 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Thu, 23 Oct 2025 12:05:05 +1300 Subject: [PATCH 24/42] Turn `FreetypePyRecipe` into a `PyProjectRecipe` I don't think this will require the version hack --- pythonforandroid/recipes/freetype-py/__init__.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/pythonforandroid/recipes/freetype-py/__init__.py b/pythonforandroid/recipes/freetype-py/__init__.py index 55bf66c31..d4833bd7f 100644 --- a/pythonforandroid/recipes/freetype-py/__init__.py +++ b/pythonforandroid/recipes/freetype-py/__init__.py @@ -1,16 +1,11 @@ -from pythonforandroid.recipe import PythonRecipe +from pythonforandroid.recipe import PyProjectRecipe -class FreetypePyRecipe(PythonRecipe): +class FreetypePyRecipe(PyProjectRecipe): version = '2.5.1' url = 'https://github.com/rougier/freetype-py/archive/refs/tags/v{version}.tar.gz' depends = ['freetype'] site_packages_name = 'freetype' - def get_recipe_env(self, arch=None, with_flags_in_cc=True): - env = super().get_recipe_env(arch, with_flags_in_cc) - env["SETUPTOOLS_SCM_PRETEND_VERSION_FOR_freetype"] = self.version - return env - recipe = FreetypePyRecipe() From 220198eb5ed64dfeb1129c6ea328147d923cdfc4 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Thu, 23 Oct 2025 12:37:24 +1300 Subject: [PATCH 25/42] Put the `setuptools-scm` version hack back in --- pythonforandroid/recipes/freetype-py/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pythonforandroid/recipes/freetype-py/__init__.py b/pythonforandroid/recipes/freetype-py/__init__.py index d4833bd7f..dd3597081 100644 --- a/pythonforandroid/recipes/freetype-py/__init__.py +++ b/pythonforandroid/recipes/freetype-py/__init__.py @@ -7,5 +7,10 @@ class FreetypePyRecipe(PyProjectRecipe): depends = ['freetype'] site_packages_name = 'freetype' + def get_recipe_env(self, arch=None, with_flags_in_cc=True): + env = super().get_recipe_env(arch, with_flags_in_cc) + env["SETUPTOOLS_SCM_PRETEND_VERSION_FOR_freetype"] = self.version + return env + recipe = FreetypePyRecipe() From c9408f98de3efcdbb6c6466b299bae84ff2b5955 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Thu, 23 Oct 2025 13:18:22 +1300 Subject: [PATCH 26/42] Fix `FreetypePyRecipe.get_recipe_env` --- pythonforandroid/recipes/freetype-py/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonforandroid/recipes/freetype-py/__init__.py b/pythonforandroid/recipes/freetype-py/__init__.py index dd3597081..57ba47646 100644 --- a/pythonforandroid/recipes/freetype-py/__init__.py +++ b/pythonforandroid/recipes/freetype-py/__init__.py @@ -7,8 +7,8 @@ class FreetypePyRecipe(PyProjectRecipe): depends = ['freetype'] site_packages_name = 'freetype' - def get_recipe_env(self, arch=None, with_flags_in_cc=True): - env = super().get_recipe_env(arch, with_flags_in_cc) + def get_recipe_env(self, arch=None, **kwargs): + env = super().get_recipe_env(arch, **kwargs) env["SETUPTOOLS_SCM_PRETEND_VERSION_FOR_freetype"] = self.version return env From 1972bc177846a6ce26799a0698d0a995fe40f2e9 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Thu, 23 Oct 2025 14:23:37 +1300 Subject: [PATCH 27/42] Switch to pythonhosted FreetypePy `setuptools-scm` can't deal with the github hosted one, it seems. --- pythonforandroid/recipes/freetype-py/__init__.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pythonforandroid/recipes/freetype-py/__init__.py b/pythonforandroid/recipes/freetype-py/__init__.py index 57ba47646..3d2d66e89 100644 --- a/pythonforandroid/recipes/freetype-py/__init__.py +++ b/pythonforandroid/recipes/freetype-py/__init__.py @@ -3,14 +3,11 @@ class FreetypePyRecipe(PyProjectRecipe): version = '2.5.1' - url = 'https://github.com/rougier/freetype-py/archive/refs/tags/v{version}.tar.gz' + url = ('https://files.pythonhosted.org/packages/d0/9c/' + '61ba17f846b922c2d6d101cc886b0e8fb597c109cedfcb39b8c5d2304b54/' + 'freetype-py-{version}.zip') depends = ['freetype'] site_packages_name = 'freetype' - def get_recipe_env(self, arch=None, **kwargs): - env = super().get_recipe_env(arch, **kwargs) - env["SETUPTOOLS_SCM_PRETEND_VERSION_FOR_freetype"] = self.version - return env - recipe = FreetypePyRecipe() From 602fd42c714e003d43f2f59a494f41d2a6b34d82 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Thu, 23 Oct 2025 14:49:03 +1300 Subject: [PATCH 28/42] Update apsw recipe, use pythonhosted package --- pythonforandroid/recipes/apsw/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pythonforandroid/recipes/apsw/__init__.py b/pythonforandroid/recipes/apsw/__init__.py index 42ad3ba33..359b4e1c4 100644 --- a/pythonforandroid/recipes/apsw/__init__.py +++ b/pythonforandroid/recipes/apsw/__init__.py @@ -4,8 +4,10 @@ class ApswRecipe(PythonRecipe): - version = '3.15.0-r1' - url = 'https://github.com/rogerbinns/apsw/archive/{version}.tar.gz' + version = '3.50.4.0' + url = ('https://files.pythonhosted.org/packages/02/ea/' + '7469e89d75a07972255aac4c1b98675bfbc74df32a19dd5dc8ba87aa552b/' + 'apsw-{version}.tar.gz') depends = ['sqlite3', 'setuptools'] call_hostpython_via_targetpython = False site_packages_name = 'apsw' From 0cbea12d661414d2b6eab8efe11efb082fe488a0 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Thu, 23 Oct 2025 15:30:30 +1300 Subject: [PATCH 29/42] Require setuptools for every recipe --- pythonforandroid/recipe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 2b399bee1..5919651f5 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -878,7 +878,7 @@ class PythonRecipe(Recipe): on python2 or python3 which can break the dependency graph ''' - hostpython_prerequisites = [] + hostpython_prerequisites = ['setuptools'] '''List of hostpython packages required to build a recipe''' _host_recipe = None From 01b4a3e19b0888e79a5cf8d0fe8d71ae3aa1122c Mon Sep 17 00:00:00 2001 From: Ansh Dadwal Date: Wed, 15 Oct 2025 22:01:19 +0530 Subject: [PATCH 30/42] `python`: update to `3.14.0` --- .../src/main/java/org/kivy/android/PythonUtil.java | 2 +- pythonforandroid/recipes/hostpython3/__init__.py | 2 +- pythonforandroid/recipes/python3/__init__.py | 13 ++++--------- pythonforandroid/recipes/six/__init__.py | 10 ---------- 4 files changed, 6 insertions(+), 21 deletions(-) delete mode 100644 pythonforandroid/recipes/six/__init__.py diff --git a/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonUtil.java b/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonUtil.java index 065f43c3b..9e19a6327 100644 --- a/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonUtil.java +++ b/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonUtil.java @@ -51,7 +51,7 @@ protected static ArrayList getLibraries(File libsDir) { addLibraryIfExists(libsList, name, libsDir); } - for (int v = 5; v <= 14; v++) { + for (int v = 14; v >= 5; v--) { libsList.add("python3." + v + (v <= 7 ? "m" : "")); } diff --git a/pythonforandroid/recipes/hostpython3/__init__.py b/pythonforandroid/recipes/hostpython3/__init__.py index 094660fad..afc4df495 100644 --- a/pythonforandroid/recipes/hostpython3/__init__.py +++ b/pythonforandroid/recipes/hostpython3/__init__.py @@ -36,7 +36,7 @@ class HostPython3Recipe(Recipe): :class:`~pythonforandroid.python.HostPythonRecipe` ''' - version = '3.11.13' + version = '3.14.0' url = 'https://github.com/python/cpython/archive/refs/tags/v{version}.tar.gz' '''The default url to download our host python recipe. This url will diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index 81aee7c66..4c234ce8f 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -54,7 +54,7 @@ class Python3Recipe(TargetPythonRecipe): :class:`~pythonforandroid.python.GuestPythonRecipe` ''' - version = '3.11.13' + version = '3.14.0' _p_version = Version(version) url = 'https://github.com/python/cpython/archive/refs/tags/v{version}.tar.gz' name = 'python3' @@ -389,17 +389,12 @@ def create_python_bundle(self, dirn, arch): place. """ # Todo: find a better way to find the build libs folder - modules_build_dir = join( + modules_build_dir = glob.glob(join( self.get_build_dir(arch.arch), 'android-build', 'build', - 'lib.{}{}-{}-{}'.format( - # android is now supported platform - "android" if self._p_version.minor >= 13 else "linux", - '2' if self.version[0] == '2' else '', - arch.command_prefix.split('-')[0], - self.major_minor_version_string - )) + 'lib.*' + ))[0] # Compile to *.pyc the python modules self.compile_python_files(modules_build_dir) diff --git a/pythonforandroid/recipes/six/__init__.py b/pythonforandroid/recipes/six/__init__.py deleted file mode 100644 index 3be8ce757..000000000 --- a/pythonforandroid/recipes/six/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -from pythonforandroid.recipe import PythonRecipe - - -class SixRecipe(PythonRecipe): - version = '1.15.0' - url = 'https://pypi.python.org/packages/source/s/six/six-{version}.tar.gz' - depends = ['setuptools'] - - -recipe = SixRecipe() From 9ed9de829c5fb8469468d956e950071dc1e67248 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Thu, 23 Oct 2025 15:53:56 +1300 Subject: [PATCH 31/42] Remove 'six' from `build_order` in `TestToolchainCL.test_create` --- tests/test_toolchain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_toolchain.py b/tests/test_toolchain.py index 874453f98..391e6c073 100644 --- a/tests/test_toolchain.py +++ b/tests/test_toolchain.py @@ -84,7 +84,7 @@ def test_create(self): ] build_order = [ 'hostpython3', 'libffi', 'openssl', 'sqlite3', 'python3', - 'genericndkbuild', 'setuptools', 'six', 'pyjnius', 'android', + 'genericndkbuild', 'setuptools', 'pyjnius', 'android', ] python_modules = [] context = mock.ANY From 15cd8663ede1b38602b1163885e50c6317631863 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Thu, 23 Oct 2025 16:06:50 +1300 Subject: [PATCH 32/42] Add 'six' to `python_modules` in `TestToolchainCL.test_create` --- tests/test_toolchain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_toolchain.py b/tests/test_toolchain.py index 391e6c073..2eb1e9910 100644 --- a/tests/test_toolchain.py +++ b/tests/test_toolchain.py @@ -86,7 +86,7 @@ def test_create(self): 'hostpython3', 'libffi', 'openssl', 'sqlite3', 'python3', 'genericndkbuild', 'setuptools', 'pyjnius', 'android', ] - python_modules = [] + python_modules = ['six'] context = mock.ANY project_dir = None assert m_build_recipes.call_args_list == [ From b1e9cd2d9557bde816530c109a3edcd4ef64d5e1 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Thu, 23 Oct 2025 16:12:35 +1300 Subject: [PATCH 33/42] Remove 'setuptools' from 'build_order' in `TestToolchainCL.test_create` I guess because the hostpython already has it, there's no need to build it again? --- tests/test_toolchain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_toolchain.py b/tests/test_toolchain.py index 2eb1e9910..03b008fd3 100644 --- a/tests/test_toolchain.py +++ b/tests/test_toolchain.py @@ -84,7 +84,7 @@ def test_create(self): ] build_order = [ 'hostpython3', 'libffi', 'openssl', 'sqlite3', 'python3', - 'genericndkbuild', 'setuptools', 'pyjnius', 'android', + 'genericndkbuild', 'pyjnius', 'android', ] python_modules = ['six'] context = mock.ANY From 9ede6146fde56f943a80f001053a97654b2f819c Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Thu, 23 Oct 2025 16:18:55 +1300 Subject: [PATCH 34/42] Revert the change to `modules_build_dir` in `Python3Recipe.create_python_bundle` --- pythonforandroid/recipes/python3/__init__.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index 4c234ce8f..488272f83 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -389,12 +389,17 @@ def create_python_bundle(self, dirn, arch): place. """ # Todo: find a better way to find the build libs folder - modules_build_dir = glob.glob(join( + modules_build_dir = join( self.get_build_dir(arch.arch), 'android-build', 'build', - 'lib.*' - ))[0] + 'lib.{}{}-{}-{}'.format( + # android is now supported platform + "android" if self._p_version.minor >= 13 else "linux", + '2' if self.version[0] == '2' else '', + arch.command_prefix.split('-')[0], + self.major_minor_version_string + )) # Compile to *.pyc the python modules self.compile_python_files(modules_build_dir) From 5b573d8100c4177e2117031f15648ddd43535f29 Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Thu, 23 Oct 2025 16:56:47 +1300 Subject: [PATCH 35/42] Remove old versions from `modules_build_dir` expected value in `TestPython3Recipe.test_create_python_bundle` --- tests/recipes/test_python3.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/recipes/test_python3.py b/tests/recipes/test_python3.py index 01d58f7d2..d3a4c22ea 100644 --- a/tests/recipes/test_python3.py +++ b/tests/recipes/test_python3.py @@ -178,9 +178,7 @@ def test_create_python_bundle( recipe_build_dir, 'android-build', 'build', - 'lib.{}{}-{}-{}'.format( - 'android' if self.recipe.version[2] >= "3" else 'linux', - '2' if self.recipe.version[0] == '2' else '', + 'lib.android-{}-{}'.format( self.arch.command_prefix.split('-')[0], self.recipe.major_minor_version_string )) From a2cfb3182408f8cae229342db5c90800743388bd Mon Sep 17 00:00:00 2001 From: Zachary Spector Date: Fri, 24 Oct 2025 20:26:20 +1300 Subject: [PATCH 36/42] Roll back to an earlier apsw version --- pythonforandroid/recipes/apsw/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/recipes/apsw/__init__.py b/pythonforandroid/recipes/apsw/__init__.py index 359b4e1c4..024357a0e 100644 --- a/pythonforandroid/recipes/apsw/__init__.py +++ b/pythonforandroid/recipes/apsw/__init__.py @@ -4,7 +4,7 @@ class ApswRecipe(PythonRecipe): - version = '3.50.4.0' + version = '3.15.0' url = ('https://files.pythonhosted.org/packages/02/ea/' '7469e89d75a07972255aac4c1b98675bfbc74df32a19dd5dc8ba87aa552b/' 'apsw-{version}.tar.gz') From 24798136f0a4d3fb68403f4024ce832c0331f9a7 Mon Sep 17 00:00:00 2001 From: Ansh Dadwal Date: Sun, 26 Oct 2025 11:06:45 +0530 Subject: [PATCH 37/42] only load libpython once --- .../common/build/src/main/java/org/kivy/android/PythonUtil.java | 1 + 1 file changed, 1 insertion(+) diff --git a/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonUtil.java b/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonUtil.java index 9e19a6327..291bf107a 100644 --- a/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonUtil.java +++ b/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonUtil.java @@ -65,6 +65,7 @@ public static void loadLibraries(File filesDir, File libsDir) { for (String lib : getLibraries(libsDir)) { Log.v(TAG, "Loading library: " + lib); try { + if (lib.startsWith("python") && foundPython) {continue;} System.loadLibrary(lib); if (lib.startsWith("python")) { foundPython = true; From 29d4b558b33dc9e6e7e964ca681976eb88e6504b Mon Sep 17 00:00:00 2001 From: Ansh Dadwal Date: Sun, 26 Oct 2025 11:30:28 +0530 Subject: [PATCH 38/42] fix pip install path --- pythonforandroid/recipe.py | 5 ++--- pythonforandroid/recipes/python3/__init__.py | 12 +++--------- tests/recipes/test_python3.py | 9 ++++----- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 76f1102d5..23b1d6ab7 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -1026,8 +1026,8 @@ def install_python_package(self, arch, name=None, env=None, is_dir=True): hpenv = env.copy() with current_directory(self.get_build_dir(arch.arch)): shprint(hostpython, '-m', 'pip', 'install', '.', - '--compile', - f'--root={self.ctx.get_python_install_dir(arch.arch)}', + '--compile', '--target', + self.ctx.get_python_install_dir(arch.arch), _env=hpenv, *self.setup_extra_args ) @@ -1048,7 +1048,6 @@ def install_hostpython_package(self, arch): real_hostpython = sh.Command(self.real_hostpython_location) shprint(real_hostpython, '-m', 'pip', 'install', '.', '--compile', - '--install-lib=Lib/site-packages', '--root={}'.format(self._host_recipe.site_root), _env=env, *self.setup_extra_args) diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index 488272f83..1c32c73c7 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -389,18 +389,12 @@ def create_python_bundle(self, dirn, arch): place. """ # Todo: find a better way to find the build libs folder - modules_build_dir = join( + modules_build_dir = glob.glob(join( self.get_build_dir(arch.arch), 'android-build', 'build', - 'lib.{}{}-{}-{}'.format( - # android is now supported platform - "android" if self._p_version.minor >= 13 else "linux", - '2' if self.version[0] == '2' else '', - arch.command_prefix.split('-')[0], - self.major_minor_version_string - )) - + 'lib.*' + ))[0] # Compile to *.pyc the python modules self.compile_python_files(modules_build_dir) # Compile to *.pyc the standard python library diff --git a/tests/recipes/test_python3.py b/tests/recipes/test_python3.py index d3a4c22ea..3cc7afdc4 100644 --- a/tests/recipes/test_python3.py +++ b/tests/recipes/test_python3.py @@ -1,4 +1,5 @@ import unittest +import glob from os.path import join from unittest import mock @@ -174,14 +175,12 @@ def test_create_python_bundle( self.recipe.create_python_bundle(fake_compile_dir, self.arch) recipe_build_dir = self.recipe.get_build_dir(self.arch.arch) - modules_build_dir = join( + modules_build_dir = glob.glob(join( recipe_build_dir, 'android-build', 'build', - 'lib.android-{}-{}'.format( - self.arch.command_prefix.split('-')[0], - self.recipe.major_minor_version_string - )) + 'lib.*' + ))[0] expected_sp_paths = [ modules_build_dir, join(recipe_build_dir, 'Lib'), From db59efacfb9d265b6ecf74f3fd17144301c11fe4 Mon Sep 17 00:00:00 2001 From: Ansh Dadwal Date: Sun, 26 Oct 2025 13:15:43 +0530 Subject: [PATCH 39/42] fix build --- pythonforandroid/recipes/apsw/__init__.py | 32 +++-------- .../recipes/freetype-py/__init__.py | 10 +++- .../recipes/freetype-py/fix_import.patch | 8 +++ pythonforandroid/recipes/freetype/__init__.py | 1 + pythonforandroid/recipes/sqlite3/Android.mk | 11 ---- pythonforandroid/recipes/sqlite3/__init__.py | 57 ++++++++----------- 6 files changed, 47 insertions(+), 72 deletions(-) create mode 100644 pythonforandroid/recipes/freetype-py/fix_import.patch delete mode 100644 pythonforandroid/recipes/sqlite3/Android.mk diff --git a/pythonforandroid/recipes/apsw/__init__.py b/pythonforandroid/recipes/apsw/__init__.py index 024357a0e..825d5ced4 100644 --- a/pythonforandroid/recipes/apsw/__init__.py +++ b/pythonforandroid/recipes/apsw/__init__.py @@ -1,34 +1,16 @@ -from pythonforandroid.recipe import PythonRecipe -from pythonforandroid.toolchain import current_directory, shprint -import sh +from pythonforandroid.recipe import PyProjectRecipe -class ApswRecipe(PythonRecipe): - version = '3.15.0' - url = ('https://files.pythonhosted.org/packages/02/ea/' - '7469e89d75a07972255aac4c1b98675bfbc74df32a19dd5dc8ba87aa552b/' - 'apsw-{version}.tar.gz') - depends = ['sqlite3', 'setuptools'] - call_hostpython_via_targetpython = False +class ApswRecipe(PyProjectRecipe): + version = '3.50.4.0' + url = 'https://github.com/rogerbinns/apsw/releases/download/{version}/apsw-{version}.tar.gz' + depends = ['sqlite3'] site_packages_name = 'apsw' - def build_arch(self, arch): - env = self.get_recipe_env(arch) - with current_directory(self.get_build_dir(arch.arch)): - # Build python bindings - hostpython = sh.Command(self.hostpython_location) - shprint(hostpython, - 'setup.py', - 'build_ext', - '--enable=fts4', _env=env) - # Install python bindings - super().build_arch(arch) - - def get_recipe_env(self, arch): - env = super().get_recipe_env(arch) + def get_recipe_env(self, arch, **kwargs): + env = super().get_recipe_env(arch, **kwargs) sqlite_recipe = self.get_recipe('sqlite3', self.ctx) env['CFLAGS'] += ' -I' + sqlite_recipe.get_build_dir(arch.arch) - env['LDFLAGS'] += ' -L' + sqlite_recipe.get_lib_dir(arch) env['LIBS'] = env.get('LIBS', '') + ' -lsqlite3' return env diff --git a/pythonforandroid/recipes/freetype-py/__init__.py b/pythonforandroid/recipes/freetype-py/__init__.py index 3d2d66e89..0967cfb5d 100644 --- a/pythonforandroid/recipes/freetype-py/__init__.py +++ b/pythonforandroid/recipes/freetype-py/__init__.py @@ -3,11 +3,15 @@ class FreetypePyRecipe(PyProjectRecipe): version = '2.5.1' - url = ('https://files.pythonhosted.org/packages/d0/9c/' - '61ba17f846b922c2d6d101cc886b0e8fb597c109cedfcb39b8c5d2304b54/' - 'freetype-py-{version}.zip') + url = 'https://github.com/rougier/freetype-py/archive/refs/tags/v{version}.tar.gz' + patches = ["fix_import.patch"] depends = ['freetype'] site_packages_name = 'freetype' + def get_recipe_env(self, arch, **kwargs): + env = super().get_recipe_env(arch, **kwargs) + env["SETUPTOOLS_SCM_PRETEND_VERSION_FOR_freetype_py"] = self.version + return env + recipe = FreetypePyRecipe() diff --git a/pythonforandroid/recipes/freetype-py/fix_import.patch b/pythonforandroid/recipes/freetype-py/fix_import.patch new file mode 100644 index 000000000..03ebcae77 --- /dev/null +++ b/pythonforandroid/recipes/freetype-py/fix_import.patch @@ -0,0 +1,8 @@ +diff '--color=auto' -uNr freetype-py-2.5.1/MANIFEST.in freetype-py-2.5.1.mod/MANIFEST.in +--- freetype-py-2.5.1/MANIFEST.in 2024-08-29 23:12:30.000000000 +0530 ++++ freetype-py-2.5.1.mod/MANIFEST.in 2025-10-26 11:54:45.052025521 +0530 +@@ -9,3 +9,4 @@ + include LICENSE.txt + include README.rst + include setup-build-freetype.py ++recursive-include _custom_build *.py diff --git a/pythonforandroid/recipes/freetype/__init__.py b/pythonforandroid/recipes/freetype/__init__.py index 7772b4ffe..c584cba02 100644 --- a/pythonforandroid/recipes/freetype/__init__.py +++ b/pythonforandroid/recipes/freetype/__init__.py @@ -77,6 +77,7 @@ def build_arch(self, arch, with_harfbuzz=False): '--host={}'.format(arch.command_prefix), '--prefix={}'.format(prefix_path), '--without-bzip2', + '--without-brotli', '--with-png=no', } if not harfbuzz_in_recipes: diff --git a/pythonforandroid/recipes/sqlite3/Android.mk b/pythonforandroid/recipes/sqlite3/Android.mk deleted file mode 100644 index 57bc81573..000000000 --- a/pythonforandroid/recipes/sqlite3/Android.mk +++ /dev/null @@ -1,11 +0,0 @@ -LOCAL_PATH := $(call my-dir)/.. - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := sqlite3.c - -LOCAL_MODULE := sqlite3 - -LOCAL_CFLAGS := -DSQLITE_ENABLE_FTS4 -D_FILE_OFFSET_BITS=32 -DSQLITE_ENABLE_JSON1 - -include $(BUILD_SHARED_LIBRARY) diff --git a/pythonforandroid/recipes/sqlite3/__init__.py b/pythonforandroid/recipes/sqlite3/__init__.py index 1f4292c1e..4265a0707 100644 --- a/pythonforandroid/recipes/sqlite3/__init__.py +++ b/pythonforandroid/recipes/sqlite3/__init__.py @@ -1,36 +1,27 @@ -from os.path import join -import shutil - -from pythonforandroid.recipe import NDKRecipe -from pythonforandroid.util import ensure_dir - - -class Sqlite3Recipe(NDKRecipe): - version = '3.35.5' - # Don't forget to change the URL when changing the version - url = 'https://www.sqlite.org/2021/sqlite-amalgamation-3350500.zip' - generated_libraries = ['sqlite3'] - - def should_build(self, arch): - return not self.has_libs(arch, 'libsqlite3.so') - - def prebuild_arch(self, arch): - super().prebuild_arch(arch) - # Copy the Android make file - ensure_dir(join(self.get_build_dir(arch.arch), 'jni')) - shutil.copyfile(join(self.get_recipe_dir(), 'Android.mk'), - join(self.get_build_dir(arch.arch), 'jni/Android.mk')) - - def build_arch(self, arch, *extra_args): - super().build_arch(arch) - # Copy the shared library - shutil.copyfile(join(self.get_build_dir(arch.arch), 'libs', arch.arch, 'libsqlite3.so'), - join(self.ctx.get_libs_dir(arch.arch), 'libsqlite3.so')) - - def get_recipe_env(self, arch): - env = super().get_recipe_env(arch) - env['NDK_PROJECT_PATH'] = self.get_build_dir(arch.arch) - return env +import sh +from pythonforandroid.logger import shprint +from pythonforandroid.util import current_directory +from pythonforandroid.recipe import Recipe +from multiprocessing import cpu_count + + +class Sqlite3Recipe(Recipe): + version = '3.50.4' + url = 'https://github.com/sqlite/sqlite/archive/refs/tags/version-{version}.tar.gz' + built_libraries = {'libsqlite3.so': '.'} + + def build_arch(self, arch): + env = self.get_recipe_env(arch) + build_dir = self.get_build_dir(arch.arch) + config_args = { + '--host={}'.format(arch.command_prefix), + '--prefix={}'.format(build_dir), + '--disable-tcl', + } + with current_directory(build_dir): + configure = sh.Command('./configure') + shprint(configure, *config_args, _env=env) + shprint(sh.make, '-j', str(cpu_count()), _env=env) recipe = Sqlite3Recipe() From 1b52e2ac45f76de4d1f936478766c737f0e38294 Mon Sep 17 00:00:00 2001 From: Ansh Dadwal Date: Sun, 26 Oct 2025 13:16:27 +0530 Subject: [PATCH 40/42] shift foundPython check --- .../common/build/src/main/java/org/kivy/android/PythonUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonUtil.java b/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonUtil.java index 291bf107a..abf21dd47 100644 --- a/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonUtil.java +++ b/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonUtil.java @@ -63,9 +63,9 @@ public static void loadLibraries(File filesDir, File libsDir) { boolean foundPython = false; for (String lib : getLibraries(libsDir)) { + if (lib.startsWith("python") && foundPython) {continue;} Log.v(TAG, "Loading library: " + lib); try { - if (lib.startsWith("python") && foundPython) {continue;} System.loadLibrary(lib); if (lib.startsWith("python")) { foundPython = true; From 862926062f9c3ffc15374f4dc5b4e36c452296bb Mon Sep 17 00:00:00 2001 From: Ansh Dadwal Date: Sun, 26 Oct 2025 13:36:08 +0530 Subject: [PATCH 41/42] fix newline --- pythonforandroid/recipes/cffi/__init__.py | 2 +- pythonforandroid/recipes/libffi/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonforandroid/recipes/cffi/__init__.py b/pythonforandroid/recipes/cffi/__init__.py index 66f23dfdb..11446df95 100644 --- a/pythonforandroid/recipes/cffi/__init__.py +++ b/pythonforandroid/recipes/cffi/__init__.py @@ -38,4 +38,4 @@ def get_recipe_env(self, arch=None, **kwargs): return env -recipe = CffiRecipe() \ No newline at end of file +recipe = CffiRecipe() diff --git a/pythonforandroid/recipes/libffi/__init__.py b/pythonforandroid/recipes/libffi/__init__.py index 32dae91ba..5636dffa7 100644 --- a/pythonforandroid/recipes/libffi/__init__.py +++ b/pythonforandroid/recipes/libffi/__init__.py @@ -38,4 +38,4 @@ def get_include_dirs(self, arch): return [join(self.get_build_dir(arch), 'include')] -recipe = LibffiRecipe() \ No newline at end of file +recipe = LibffiRecipe() From c87f361e1f35b5d70d7c5e90ddfeb13f07d85d07 Mon Sep 17 00:00:00 2001 From: Ansh Dadwal Date: Sun, 26 Oct 2025 13:53:42 +0530 Subject: [PATCH 42/42] remove unnecessary test --- pythonforandroid/recipes/python3/__init__.py | 3 +- tests/recipes/test_python3.py | 57 -------------------- 2 files changed, 1 insertion(+), 59 deletions(-) diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index 1c32c73c7..fb69b9a36 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -262,7 +262,7 @@ def add_flags(include_flags, link_dirs, link_libs): info('Activating flags for sqlite3') recipe = Recipe.get_recipe('sqlite3', self.ctx) add_flags(' -I' + recipe.get_build_dir(arch.arch), - ' -L' + recipe.get_lib_dir(arch), ' -lsqlite3') + ' -L' + recipe.get_build_dir(arch.arch), ' -lsqlite3') info('Activating flags for libffi') recipe = Recipe.get_recipe('libffi', self.ctx) @@ -388,7 +388,6 @@ def create_python_bundle(self, dirn, arch): copying all the modules and standard library to the right place. """ - # Todo: find a better way to find the build libs folder modules_build_dir = glob.glob(join( self.get_build_dir(arch.arch), 'android-build', diff --git a/tests/recipes/test_python3.py b/tests/recipes/test_python3.py index 3cc7afdc4..57b7ef8e0 100644 --- a/tests/recipes/test_python3.py +++ b/tests/recipes/test_python3.py @@ -1,5 +1,4 @@ import unittest -import glob from os.path import join from unittest import mock @@ -143,59 +142,3 @@ def test_build_arch_wrong_ndk_api(self): # restore recipe's ctx or we could get failures with other test, # since we share `self.recipe with all the tests of the class self.recipe.ctx.ndk_api = self.ctx.ndk_api - - @mock.patch('shutil.copystat') - @mock.patch('shutil.copyfile') - @mock.patch("pythonforandroid.util.chdir") - @mock.patch("pythonforandroid.util.makedirs") - @mock.patch("pythonforandroid.util.walk") - @mock.patch("pythonforandroid.recipes.python3.sh.find") - @mock.patch("pythonforandroid.recipes.python3.sh.cp") - @mock.patch("pythonforandroid.recipes.python3.sh.zip") - @mock.patch("pythonforandroid.recipes.python3.subprocess.call") - def test_create_python_bundle( - self, - mock_subprocess, - mock_sh_zip, - mock_sh_cp, - mock_sh_find, - mock_walk, - mock_makedirs, - mock_chdir, - mock_copyfile, - mock_copystat, - ): - fake_compile_dir = '/fake/compile/dir' - simulated_walk_result = [ - ["/fake_dir", ["__pycache__", "Lib"], ["README", "setup.py"]], - ["/fake_dir/Lib", ["ctypes"], ["abc.pyc", "abc.py"]], - ["/fake_dir/Lib/ctypes", [], ["util.pyc", "util.py"]], - ] - mock_walk.return_value = simulated_walk_result - self.recipe.create_python_bundle(fake_compile_dir, self.arch) - - recipe_build_dir = self.recipe.get_build_dir(self.arch.arch) - modules_build_dir = glob.glob(join( - recipe_build_dir, - 'android-build', - 'build', - 'lib.*' - ))[0] - expected_sp_paths = [ - modules_build_dir, - join(recipe_build_dir, 'Lib'), - self.ctx.get_python_install_dir(self.arch.arch), - ] - for n, (sp_call, kw) in enumerate(mock_subprocess.call_args_list): - self.assertEqual(sp_call[0][-1], expected_sp_paths[n]) - - # we expect two calls to `walk_valid_filens` - self.assertEqual(len(mock_walk.call_args_list), 2) - - mock_sh_zip.assert_called() - mock_sh_cp.assert_called() - mock_sh_find.assert_called() - mock_makedirs.assert_called() - mock_chdir.assert_called() - mock_copyfile.assert_called() - mock_copystat.assert_called()