Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
78f76cb
Use `pip install` instead of `setup.py install` in `recipe.py`
clayote Oct 17, 2025
2780fc3
Import from setuptools and not distutils in the `android` recipe
clayote Oct 22, 2025
6d1b9d3
Remove freetype's distutils "fallback"
clayote Oct 22, 2025
b8eaa1d
Bump `freetype` version to match that of `freetype-py`
clayote Oct 22, 2025
39dff1f
On second thought, use the latest freetype version
clayote Oct 22, 2025
a752329
Merge remote-tracking branch 'origin/develop' into no-distutils
clayote Oct 22, 2025
9df2658
Use the latest *released* freetype-py version
clayote Oct 22, 2025
9286612
Tell setuptools what freetype-py version we have
clayote Oct 22, 2025
8102b33
Turn `FreetypePyRecipe` into a `PyProjectRecipe`
clayote Oct 22, 2025
e812a42
Put the `setuptools-scm` version hack back in
clayote Oct 22, 2025
fbdf8c2
Fix `FreetypePyRecipe.get_recipe_env`
clayote Oct 23, 2025
0d0d9f3
Switch to pythonhosted FreetypePy
clayote Oct 23, 2025
d07f168
Update apsw recipe, use pythonhosted package
clayote Oct 23, 2025
16008f9
Fix `LibffiRecipe.get_include_dirs` for `arch=None`
clayote Oct 23, 2025
e4cf526
Use recent libffi and cffi
clayote Oct 23, 2025
344d6b8
Require setuptools for every recipe
clayote Oct 23, 2025
d93861c
`python`: update to `3.14.0`
T-Dynamos Oct 15, 2025
94cecf8
Use `pip install` instead of `setup.py install` in `recipe.py`
clayote Oct 17, 2025
f0e3a95
Import from setuptools and not distutils in the `android` recipe
clayote Oct 22, 2025
ebe2e2f
Remove freetype's distutils "fallback"
clayote Oct 22, 2025
3ee745b
Bump `freetype` version to match that of `freetype-py`
clayote Oct 22, 2025
3123829
On second thought, use the latest freetype version
clayote Oct 22, 2025
619287b
Use the latest *released* freetype-py version
clayote Oct 22, 2025
3acc704
Tell setuptools what freetype-py version we have
clayote Oct 22, 2025
cc75b37
Turn `FreetypePyRecipe` into a `PyProjectRecipe`
clayote Oct 22, 2025
220198e
Put the `setuptools-scm` version hack back in
clayote Oct 22, 2025
c9408f9
Fix `FreetypePyRecipe.get_recipe_env`
clayote Oct 23, 2025
1972bc1
Switch to pythonhosted FreetypePy
clayote Oct 23, 2025
602fd42
Update apsw recipe, use pythonhosted package
clayote Oct 23, 2025
0cbea12
Require setuptools for every recipe
clayote Oct 23, 2025
01b4a3e
`python`: update to `3.14.0`
T-Dynamos Oct 15, 2025
09cbdcb
Merge remote-tracking branch 'origin/no-distutils' into no-distutils
clayote Oct 23, 2025
9ed9de8
Remove 'six' from `build_order` in `TestToolchainCL.test_create`
clayote Oct 23, 2025
15cd866
Add 'six' to `python_modules` in `TestToolchainCL.test_create`
clayote Oct 23, 2025
b1e9cd2
Remove 'setuptools' from 'build_order' in `TestToolchainCL.test_create`
clayote Oct 23, 2025
9ede614
Revert the change to `modules_build_dir` in `Python3Recipe.create_pyt…
clayote Oct 23, 2025
5b573d8
Remove old versions from `modules_build_dir` expected value in `TestP…
clayote Oct 23, 2025
a2cfb31
Roll back to an earlier apsw version
clayote Oct 24, 2025
2bc156c
Merge branch 'kivy:develop' into no-distutils
clayote Oct 26, 2025
2479813
only load libpython once
T-Dynamos Oct 26, 2025
29d4b55
fix pip install path
T-Dynamos Oct 26, 2025
db59efa
fix build
T-Dynamos Oct 26, 2025
1b52e2a
shift foundPython check
T-Dynamos Oct 26, 2025
cfe1534
Merge branch 'develop' into no-distutils
T-Dynamos Oct 26, 2025
8629260
fix newline
T-Dynamos Oct 26, 2025
c87f361
remove unnecessary test
T-Dynamos Oct 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ protected static ArrayList<String> 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" : ""));
}

Expand All @@ -63,6 +63,7 @@ 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 {
System.loadLibrary(lib);
Expand Down
27 changes: 10 additions & 17 deletions pythonforandroid/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -883,7 +883,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
Expand Down Expand Up @@ -1025,18 +1025,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', '--target',
self.ctx.get_python_install_dir(arch.arch),
_env=hpenv, *self.setup_extra_args
)

def get_hostrecipe_env(self, arch=None):
env = environ.copy()
Expand All @@ -1053,8 +1046,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',
'--install-lib=Lib/site-packages',
shprint(real_hostpython, '-m', 'pip', 'install', '.',
'--compile',
'--root={}'.format(self._host_recipe.site_root),
_env=env, *self.setup_extra_args)

Expand Down Expand Up @@ -1100,7 +1093,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()
Expand Down Expand Up @@ -1149,7 +1142,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)
Expand Down
2 changes: 1 addition & 1 deletion pythonforandroid/recipes/android/src/setup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from distutils.core import setup, Extension
from setuptools import setup, Extension
from Cython.Build import cythonize
import os

Expand Down
30 changes: 7 additions & 23 deletions pythonforandroid/recipes/apsw/__init__.py
Original file line number Diff line number Diff line change
@@ -1,32 +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-r1'
url = 'https://github.com/rogerbinns/apsw/archive/{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

Expand Down
13 changes: 9 additions & 4 deletions pythonforandroid/recipes/freetype-py/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
from pythonforandroid.recipe import PythonRecipe
from pythonforandroid.recipe import PyProjectRecipe


class FreetypePyRecipe(PythonRecipe):
version = '2.2.0'
class FreetypePyRecipe(PyProjectRecipe):
version = '2.5.1'
url = 'https://github.com/rougier/freetype-py/archive/refs/tags/v{version}.tar.gz'
patches = ["fix_import.patch"]
depends = ['freetype']
patches = ['fall-back-to-distutils.patch']
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()
15 changes: 0 additions & 15 deletions pythonforandroid/recipes/freetype-py/fall-back-to-distutils.patch

This file was deleted.

8 changes: 8 additions & 0 deletions pythonforandroid/recipes/freetype-py/fix_import.patch
Original file line number Diff line number Diff line change
@@ -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
3 changes: 2 additions & 1 deletion pythonforandroid/recipes/freetype/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class FreetypeRecipe(Recipe):
https://sourceforge.net/projects/freetype/files/freetype2/2.5.3/
"""

version = '2.10.1'
version = '2.14.1'
url = 'https://download.savannah.gnu.org/releases/freetype/freetype-{version}.tar.gz' # noqa
built_libraries = {'libfreetype.so': 'objs/.libs'}

Expand Down Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion pythonforandroid/recipes/hostpython3/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 5 additions & 12 deletions pythonforandroid/recipes/python3/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -388,19 +388,12 @@ 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 = 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
Expand Down
10 changes: 0 additions & 10 deletions pythonforandroid/recipes/six/__init__.py

This file was deleted.

11 changes: 0 additions & 11 deletions pythonforandroid/recipes/sqlite3/Android.mk

This file was deleted.

57 changes: 24 additions & 33 deletions pythonforandroid/recipes/sqlite3/__init__.py
Original file line number Diff line number Diff line change
@@ -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()
60 changes: 0 additions & 60 deletions tests/recipes/test_python3.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,63 +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 = join(
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 '',
self.arch.command_prefix.split('-')[0],
self.recipe.major_minor_version_string
))
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()
4 changes: 2 additions & 2 deletions tests/test_toolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ def test_create(self):
]
build_order = [
'hostpython3', 'libffi', 'openssl', 'sqlite3', 'python3',
'genericndkbuild', 'setuptools', 'six', 'pyjnius', 'android',
'genericndkbuild', 'pyjnius', 'android',
]
python_modules = []
python_modules = ['six']
context = mock.ANY
project_dir = None
assert m_build_recipes.call_args_list == [
Expand Down
Loading