From 3c42783433443bf6dca11fe8a2218e563f8d35e5 Mon Sep 17 00:00:00 2001 From: Ansh Dadwal Date: Sat, 14 Feb 2026 20:42:03 +0530 Subject: [PATCH 1/8] Add support for prebuilt wheels --- pythonforandroid/build.py | 46 +++++++++- pythonforandroid/recipe.py | 91 ++++++++++++++++--- .../recipes/hostpython3/__init__.py | 12 ++- pythonforandroid/toolchain.py | 36 ++++++++ tests/test_build.py | 30 +++++- 5 files changed, 194 insertions(+), 21 deletions(-) diff --git a/pythonforandroid/build.py b/pythonforandroid/build.py index 7758bcfd8c..1c2505c504 100644 --- a/pythonforandroid/build.py +++ b/pythonforandroid/build.py @@ -21,7 +21,7 @@ from pythonforandroid.archs import ArchARM, ArchARMv7_a, ArchAarch_64, Archx86, Archx86_64 from pythonforandroid.logger import (info, warning, info_notify, info_main, shprint, Out_Style, Out_Fore) from pythonforandroid.pythonpackage import get_package_name -from pythonforandroid.recipe import CythonRecipe, Recipe +from pythonforandroid.recipe import CythonRecipe, Recipe, PyProjectRecipe from pythonforandroid.recommendations import ( check_ndk_version, check_target_api, check_ndk_api, RECOMMENDED_NDK_API, RECOMMENDED_TARGET_API) @@ -101,6 +101,12 @@ class Context: java_build_tool = 'auto' + skip_prebuilt = False + + extra_index_urls = [] + + use_prebuilt_version_for = [] + @property def packages_path(self): '''Where packages are downloaded before being unpacked''' @@ -667,7 +673,17 @@ def is_wheel_platform_independent(whl_name): return all(tag.platform == "any" for tag in tags) -def process_python_modules(ctx, modules): +def is_wheel_compatible(whl_name, arch, ctx): + name, version, build, tags = parse_wheel_filename(whl_name) + supported_tags = PyProjectRecipe.get_wheel_platform_tag(None, arch.arch, ctx=ctx) + supported_tags.append("any") + result = all(tag.platform in supported_tags for tag in tags) + if not result: + warning(f"Incompatible module : {whl_name}") + return result + + +def process_python_modules(ctx, modules, arch): """Use pip --dry-run to resolve dependencies and filter for pure-Python packages """ modules = list(modules) @@ -702,6 +718,7 @@ def process_python_modules(ctx, modules): # setup hostpython recipe env = environ.copy() + host_recipe = None try: host_recipe = Recipe.get_recipe("hostpython3", ctx) _python_path = host_recipe.get_path_to_python() @@ -713,11 +730,28 @@ def process_python_modules(ctx, modules): # hostpython3 non available so we use system pip (like in tests) pip = sh.Command("pip") + # add platform tags + platforms = [] + tags = PyProjectRecipe.get_wheel_platform_tag(None, arch.arch, ctx=ctx) + for tag in tags: + platforms.append(f"--platform={tag}") + + if host_recipe is not None: + platforms.extend(["--python-version", host_recipe.version]) + else: + # tests? + platforms.extend(["--python-version", "3.13.4"]) + + indices = [] + # add extra index urls + for index in ctx.extra_index_urls: + indices.extend(["--extra-index-url", index]) try: shprint( pip, 'install', *modules, '--dry-run', '--break-system-packages', '--ignore-installed', - '--report', path, '-q', _env=env + '--disable-pip-version-check', '--only-binary=:all:', + '--report', path, '-q', *platforms, *indices, _env=env ) except Exception as e: warning(f"Auto module resolution failed: {e}") @@ -751,7 +785,9 @@ def process_python_modules(ctx, modules): filename = basename(module["download_info"]["url"]) pure_python = True - if (filename.endswith(".whl") and not is_wheel_platform_independent(filename)): + if ( + filename.endswith(".whl") and not is_wheel_compatible(filename, arch, ctx) + ): any_not_pure_python = True pure_python = False @@ -793,7 +829,7 @@ def run_pymodules_install(ctx, arch, modules, project_dir=None, info('*** PYTHON PACKAGE / PROJECT INSTALL STAGE FOR ARCH: {} ***'.format(arch)) - modules = process_python_modules(ctx, modules) + modules = process_python_modules(ctx, modules, arch) modules = [m for m in modules if ctx.not_has_package(m, arch)] diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 32bc2ec2b0..1285ca12d3 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -923,8 +923,7 @@ def real_hostpython_location(self): if host_name == 'hostpython3': return self._host_recipe.python_exe else: - python_recipe = self.ctx.python_recipe - return 'python{}'.format(python_recipe.version) + return 'python{}'.format(self.ctx.python_recipe.version) @property def hostpython_location(self): @@ -1248,6 +1247,55 @@ class PyProjectRecipe(PythonRecipe): extra_build_args = [] call_hostpython_via_targetpython = False + def get_pip_name(self): + name_str = self.name + if self.name not in self.ctx.use_prebuilt_version_for: + name_str += f"=={self.version}" + return name_str + + def get_pip_install_args(self, arch): + python_recipe = Recipe.get_recipe("python3", self.ctx) + opts = [ + "install", + self.get_pip_name(), + "--disable-pip-version-check", + "--python-version", + python_recipe.version, + "--only-binary=:all:", + ] + # add platform tags + tags = self.get_wheel_platform_tag(arch.arch) + for tag in tags: + opts.append(f"--platform={tag}") + + # add extra index urls + for index in self.ctx.extra_index_urls: + opts.extend(["--extra-index-url", index]) + + return opts + + def lookup_prebuilt(self, arch): + pip_options = self.get_pip_install_args(arch) + # do not install + pip_options.extend(["--dry-run"]) + pip_env = self.get_hostrecipe_env() + try: + shprint(self._host_recipe.pip, *pip_options, _env=pip_env) + except Exception as e: + warning(f"Lookup fail result: {e}") + return False + return True + + def check_prebuilt(self, arch, msg=""): + if self.ctx.skip_prebuilt: + return False + + if self.lookup_prebuilt(arch): + if msg != "": + info(f"Prebuilt pip wheel found, {msg}") + return True + return + def get_recipe_env(self, arch, **kwargs): # Custom hostpython self.ctx.python_recipe.python_exe = join( @@ -1259,24 +1307,40 @@ def get_recipe_env(self, arch, **kwargs): with open(build_opts, "w") as file: file.write("[bdist_wheel]\nplat_name={}".format( - self.get_wheel_platform_tag(arch) + self.get_wheel_platform_tag(arch.arch)[0] )) file.close() env["DIST_EXTRA_CONFIG"] = build_opts return env - def get_wheel_platform_tag(self, arch): + def get_wheel_platform_tag(self, arch, ctx=None): + if ctx is None: + ctx = self.ctx # https://peps.python.org/pep-0738/#packaging # official python only supports 64 bit: # android_21_arm64_v8a # android_21_x86_64 - return f"android_{self.ctx.ndk_api}_" + { - "arm64-v8a": "arm64_v8a", - "x86_64": "x86_64", - "armeabi-v7a": "arm", - "x86": "i686", - }[arch.arch] + _suffix = { + "arm64-v8a": ["arm64_v8a", "aarch64"], + "x86_64": ["x86_64"], + "armeabi-v7a": ["arm"], + "x86": ["i686"], + }[arch] + return [f"android_{ctx.ndk_api}_" + _ for _ in _suffix] + + def install_prebuilt_wheel(self, arch): + info("Installing prebuilt built wheel") + destination = self.ctx.get_python_install_dir(arch.arch) + pip_options = self.get_pip_install_args(arch) + pip_options.extend(["--target", destination]) + pip_options.append("--upgrade") + pip_env = self.get_hostrecipe_env() + try: + shprint(self._host_recipe.pip, *pip_options, _env=pip_env) + except Exception: + return False + return True def install_wheel(self, arch, built_wheels): with patch_wheel_setuptools_logging(): @@ -1287,7 +1351,7 @@ def install_wheel(self, arch, built_wheels): # Fix wheel platform tag wheel_tag = wheel_tags( _wheel, - platform_tags=self.get_wheel_platform_tag(arch), + platform_tags=self.get_wheel_platform_tag(arch.arch)[0], remove=True, ) selected_wheel = join(built_wheel_dir, wheel_tag) @@ -1305,6 +1369,11 @@ def install_wheel(self, arch, built_wheels): wf.close() def build_arch(self, arch): + if self.check_prebuilt(arch, "skipping build_arch") is not None: + result = self.install_prebuilt_wheel(arch) + if result: + return + warning("Failed to install prebuilt wheel, falling back to build_arch") build_dir = self.get_build_dir(arch.arch) if not (isfile(join(build_dir, "pyproject.toml")) or isfile(join(build_dir, "setup.py"))): diff --git a/pythonforandroid/recipes/hostpython3/__init__.py b/pythonforandroid/recipes/hostpython3/__init__.py index 199cdb50c3..4c31859bfe 100644 --- a/pythonforandroid/recipes/hostpython3/__init__.py +++ b/pythonforandroid/recipes/hostpython3/__init__.py @@ -6,7 +6,7 @@ from os.path import join from packaging.version import Version -from pythonforandroid.logger import shprint +from pythonforandroid.logger import shprint, error from pythonforandroid.recipe import Recipe from pythonforandroid.util import ( BuildInterruptingException, @@ -48,6 +48,16 @@ class HostPython3Recipe(Recipe): patches = ["fix_ensurepip.patch"] + # apply version guard + def download(self): + python_recipe = Recipe.get_recipe("python3", self.ctx) + if python_recipe.version != self.version: + error( + f"python3 should have same version as hostpython3, {python_recipe.version} != {self.version}" + ) + exit(1) + super().download() + @property def _exe_name(self): ''' diff --git a/pythonforandroid/toolchain.py b/pythonforandroid/toolchain.py index 3987647f9b..63f06802f8 100644 --- a/pythonforandroid/toolchain.py +++ b/pythonforandroid/toolchain.py @@ -188,6 +188,7 @@ class NoAbbrevParser(argparse.ArgumentParser): This subclass alternative is follows the suggestion at https://bugs.python.org/issue14910. """ + def _get_option_tuples(self, option_string): return [] @@ -267,6 +268,37 @@ def __init__(self): '--arch', help='The archs to build for.', action='append', default=[]) + generic_parser.add_argument( + '--extra-index-url', + help=( + 'Extra package indexes to look for prebuilt Android wheels. ' + 'Can be used multiple times.' + ), + action='append', + default=[], + dest="extra_index_urls", + ) + + generic_parser.add_argument( + '--skip-prebuilt', + help='Always build from source; do not use prebuilt wheels.', + action='store_true', + default=False, + dest="skip_prebuilt", + ) + + generic_parser.add_argument( + '--use-prebuilt-version-for', + help=( + 'For these packages, ignore pinned versions and use the latest ' + 'prebuilt version from the extra index if available.' + 'Only applies to packages with a recipe.' + ), + action='append', + default=[], + dest="use_prebuilt_version_for", + ) + # Options for specifying the Distribution generic_parser.add_argument( '--dist-name', '--dist_name', @@ -672,6 +704,10 @@ def add_parser(subparsers, *args, **kwargs): self.ctx.activity_class_name = args.activity_class_name self.ctx.service_class_name = args.service_class_name + self.ctx.extra_index_urls = args.extra_index_urls + self.ctx.skip_prebuilt = args.skip_prebuilt + self.ctx.use_prebuilt_version_for = args.use_prebuilt_version_for + # Each subparser corresponds to a method command = args.subparser_name.replace('-', '_') getattr(self, command)(args) diff --git a/tests/test_build.py b/tests/test_build.py index 92055400e6..e0583e750f 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -5,7 +5,7 @@ import jinja2 from pythonforandroid.build import ( - Context, RECOMMENDED_TARGET_API, run_pymodules_install, process_python_modules + Context, RECOMMENDED_TARGET_API, run_pymodules_install, process_python_modules, is_wheel_compatible ) from pythonforandroid.archs import ArchARMv7_a, ArchAarch_64 @@ -29,20 +29,42 @@ def test_run_pymodules_install_optional_project_dir(self): def test_python_module_parser(self): ctx = mock.Mock(recipe_build_order=[]) ctx.archs = [ArchARMv7_a(ctx), ArchAarch_64(ctx)] + ctx.extra_index_urls = [] + ctx.ndk_api = 24 + arch = ctx.archs[0] + # should not alter original module name (like with adding version number) - assert "kivy_garden.frostedglass" in process_python_modules(ctx, ["kivy_garden.frostedglass"]) + assert "kivy_garden.frostedglass" in process_python_modules(ctx, ["kivy_garden.frostedglass"], arch) # should skip urls and other unsupported format modules = ["https://example.com/some.zip", "git+https://github.com/kivy/python-for-android@develop"] - result = process_python_modules(ctx, modules) + result = process_python_modules(ctx, modules, arch) assert modules == result + def test_is_wheel_compatible(self): + ctx = mock.Mock(recipe_build_order=[]) + ctx.archs = [ArchARMv7_a(ctx), ArchAarch_64(ctx)] + ctx.ndk_api = 24 + arch = ctx.archs[0] + + assert is_wheel_compatible("test-7.1.0-0-cp314-cp314-android_24_aarch64.whl", ctx.archs[1], ctx) + assert is_wheel_compatible("test-7.1.0-0-cp314-cp314-android_24_arm.whl", ctx.archs[0], ctx) + assert is_wheel_compatible("certifi-2026.1.4-py3-none-any.whl", arch, ctx) + + # arches are diff + assert not is_wheel_compatible("test-7.1.0-0-cp314-cp314-android_24_aarch64.whl", ctx.archs[0], ctx) + + # other os + assert not is_wheel_compatible("test-7.1.0-0-cp313-cp313-some_other_os.whl", arch, ctx) + assert not is_wheel_compatible("mmh3-5.2.0-cp314-cp314t-win_amd64.whl", arch, ctx) + def test_strip_if_with_debug_symbols(self): ctx = mock.Mock(recipe_build_order=[]) ctx.python_recipe.major_minor_version_string = "3.6" ctx.get_site_packages_dir.return_value = "test-doesntexist" ctx.build_dir = "nonexistant_directory" - ctx.archs = ["arm64"] + ctx.extra_index_urls = [] + ctx.archs = [ArchAarch_64(ctx)] modules = ["mymodule"] project_dir = None From 15365614d48280cc6d906e1aec68c59f358e06a9 Mon Sep 17 00:00:00 2001 From: Ansh Dadwal Date: Sat, 14 Feb 2026 21:04:57 +0530 Subject: [PATCH 2/8] fix version string --- pythonforandroid/recipe.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 1285ca12d3..b981a2132a 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -1249,8 +1249,10 @@ class PyProjectRecipe(PythonRecipe): def get_pip_name(self): name_str = self.name - if self.name not in self.ctx.use_prebuilt_version_for: - name_str += f"=={self.version}" + if self.name not in self.ctx.use_prebuilt_version_for and self.version is not None: + # Like: v2.3.0 -> 2.3.0 + cleaned_version = self.version.replace("v", "") + name_str += f"=={cleaned_version}" return name_str def get_pip_install_args(self, arch): From 60e7a5aaab770685e40f77d9e1574d3987e1a2c5 Mon Sep 17 00:00:00 2001 From: Ansh Dadwal Date: Sat, 14 Feb 2026 21:20:14 +0530 Subject: [PATCH 3/8] fix pip args --- pythonforandroid/recipe.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index b981a2132a..212631631c 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -1260,10 +1260,12 @@ def get_pip_install_args(self, arch): opts = [ "install", self.get_pip_name(), + "--ignore-installed", "--disable-pip-version-check", "--python-version", python_recipe.version, "--only-binary=:all:", + "--no-deps", ] # add platform tags tags = self.get_wheel_platform_tag(arch.arch) From 9239e37716d72e38fadfa6b9d4f8d1955b8463cf Mon Sep 17 00:00:00 2001 From: Ansh Dadwal Date: Sat, 14 Feb 2026 21:29:44 +0530 Subject: [PATCH 4/8] add save_wheel_dir --- pythonforandroid/build.py | 2 ++ pythonforandroid/recipe.py | 6 ++---- pythonforandroid/toolchain.py | 8 ++++++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/pythonforandroid/build.py b/pythonforandroid/build.py index 1c2505c504..fa5fe3e601 100644 --- a/pythonforandroid/build.py +++ b/pythonforandroid/build.py @@ -107,6 +107,8 @@ class Context: use_prebuilt_version_for = [] + save_wheel_dir = '' + @property def packages_path(self): '''Where packages are downloaded before being unpacked''' diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 212631631c..1e958fc4a0 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -1360,10 +1360,8 @@ def install_wheel(self, arch, built_wheels): ) selected_wheel = join(built_wheel_dir, wheel_tag) - _dev_wheel_dir = environ.get("P4A_WHEEL_DIR", False) - if _dev_wheel_dir: - ensure_dir(_dev_wheel_dir) - shprint(sh.cp, selected_wheel, _dev_wheel_dir) + if exists(self.ctx.save_wheel_dir): + shprint(sh.cp, selected_wheel, self.ctx.save_wheel_dir) info(f"Installing built wheel: {wheel_tag}") destination = self.ctx.get_python_install_dir(arch.arch) diff --git a/pythonforandroid/toolchain.py b/pythonforandroid/toolchain.py index 63f06802f8..75c95aa40c 100644 --- a/pythonforandroid/toolchain.py +++ b/pythonforandroid/toolchain.py @@ -299,6 +299,13 @@ def __init__(self): dest="use_prebuilt_version_for", ) + generic_parser.add_argument( + '--save-wheel-dir', + dest='save_wheel_dir', + default='', + help='Directory to store wheels built by PyProjectRecipe.', + ) + # Options for specifying the Distribution generic_parser.add_argument( '--dist-name', '--dist_name', @@ -707,6 +714,7 @@ def add_parser(subparsers, *args, **kwargs): self.ctx.extra_index_urls = args.extra_index_urls self.ctx.skip_prebuilt = args.skip_prebuilt self.ctx.use_prebuilt_version_for = args.use_prebuilt_version_for + self.ctx.save_wheel_dir = args.save_wheel_dir # Each subparser corresponds to a method command = args.subparser_name.replace('-', '_') From 4ecc0add7613fd57bbc78a6f6ac9cb24686cf9af Mon Sep 17 00:00:00 2001 From: Ansh Dadwal Date: Sun, 15 Feb 2026 10:20:36 +0530 Subject: [PATCH 5/8] improve warning --- pythonforandroid/recipe.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 1e958fc4a0..aae14fa693 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -1281,12 +1281,11 @@ def get_pip_install_args(self, arch): def lookup_prebuilt(self, arch): pip_options = self.get_pip_install_args(arch) # do not install - pip_options.extend(["--dry-run"]) + pip_options.extend(["--dry-run", "-q"]) pip_env = self.get_hostrecipe_env() try: shprint(self._host_recipe.pip, *pip_options, _env=pip_env) - except Exception as e: - warning(f"Lookup fail result: {e}") + except Exception: return False return True From 8c9c350087f7c910e8a323486c2236dbf25a5c83 Mon Sep 17 00:00:00 2001 From: Ansh Dadwal Date: Sat, 21 Mar 2026 18:43:06 +0530 Subject: [PATCH 6/8] use url directly --- pythonforandroid/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/build.py b/pythonforandroid/build.py index fa5fe3e601..7d453ffd8f 100644 --- a/pythonforandroid/build.py +++ b/pythonforandroid/build.py @@ -807,7 +807,7 @@ def process_python_modules(ctx, modules, arch): ) if pure_python: - processed_modules.append(f"{mname}=={mver}") + processed_modules.append(module["download_info"]["url"]) info(" ") if any_not_pure_python: From 3f7887737c0b542db9bc0bac8bc634f36543d744 Mon Sep 17 00:00:00 2001 From: Ansh Dadwal Date: Sat, 21 Mar 2026 18:49:11 +0530 Subject: [PATCH 7/8] fix rare bug --- pythonforandroid/build.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pythonforandroid/build.py b/pythonforandroid/build.py index 7d453ffd8f..7ed2d86a1d 100644 --- a/pythonforandroid/build.py +++ b/pythonforandroid/build.py @@ -511,6 +511,10 @@ def build_recipes(build_order, python_modules, ctx, project_dir, recipe.prepare_build_dir(arch.arch) info_main('# Prebuilding recipes') + # ensure we have `ctx.python_recipe` and `ctx.hostpython` + Recipe.get_recipe("python3", ctx).prebuild_arch(arch) + ctx.hostpython = Recipe.get_recipe("hostpython3", ctx).python_exe + # 2) prebuild packages for recipe in recipes: info_main('Prebuilding {} for {}'.format(recipe.name, arch.arch)) From 6813d66a876ce02d1388a492b1e0f242826981ba Mon Sep 17 00:00:00 2001 From: Ansh Dadwal Date: Sat, 21 Mar 2026 22:11:15 +0530 Subject: [PATCH 8/8] optimize copy of bootstrap --- pythonforandroid/bootstrap.py | 37 ++++++++++++++++++- .../bootstraps/_sdl_common/__init__.py | 2 + pythonforandroid/recipes/kivy/__init__.py | 2 +- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/pythonforandroid/bootstrap.py b/pythonforandroid/bootstrap.py index 10830bf26c..a2d354c2d4 100755 --- a/pythonforandroid/bootstrap.py +++ b/pythonforandroid/bootstrap.py @@ -7,6 +7,7 @@ import sh import shlex import shutil +from pathlib import Path from pythonforandroid.logger import (shprint, info, info_main, logger, debug) from pythonforandroid.util import ( @@ -40,6 +41,36 @@ def copy_files(src_root, dest_root, override=True, symlink=False): os.makedirs(dest_file) +def copytree_filtered(src, dst, skip_dirs=None): + """ + Copy directory tree while skipping explicitly specified directories in src. + """ + + info(f"Copying {src} to {dst} with skip dirs: {skip_dirs}") + + src = Path(src) + dst = Path(dst) + skip_dirs = set(Path(p) for p in (skip_dirs or [])) + + def should_skip(rel_path): + # match exact directory path only + return any(rel_path == skip or skip in rel_path.parents for skip in skip_dirs) + + for item in src.rglob("*"): + rel = item.relative_to(src) + + if should_skip(rel): + continue + + target = dst / rel + + if item.is_dir(): + target.mkdir(parents=True, exist_ok=True) + else: + target.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(item, target) + + default_recipe_priorities = [ "webview", "sdl2", "sdl3", "service_only" # last is highest ] @@ -91,6 +122,10 @@ class Bootstrap: from Bootstrap.get_bootstrap_from_recipes. ''' + # Directories to exclude during copy (relative to src root). + # Used to reduce final build size and exclude unnecessary sources for final build. + skip_dirs = [] + # Other things a Bootstrap might need to track (maybe separately): # ndk_main.c # whitelist.txt @@ -211,7 +246,7 @@ def assemble_distribution(self): info_main(f'# Creating Android project ({self.name})') rmdir(self.dist_dir) - shprint(sh.cp, '-r', self.build_dir, self.dist_dir) + copytree_filtered(self.build_dir, self.dist_dir, skip_dirs=self.skip_dirs) with current_directory(self.dist_dir): with open('local.properties', 'w') as fileh: diff --git a/pythonforandroid/bootstraps/_sdl_common/__init__.py b/pythonforandroid/bootstraps/_sdl_common/__init__.py index 3908909e2d..39589a8dd4 100644 --- a/pythonforandroid/bootstraps/_sdl_common/__init__.py +++ b/pythonforandroid/bootstraps/_sdl_common/__init__.py @@ -9,6 +9,8 @@ class SDLGradleBootstrap(Bootstrap): recipe_depends = [] + skip_dirs = ["jni"] + def _assemble_distribution_for_arch(self, arch): """SDL bootstrap skips distribute_aars() - handled differently.""" self.distribute_libs(arch, [self.ctx.get_libs_dir(arch.arch)]) diff --git a/pythonforandroid/recipes/kivy/__init__.py b/pythonforandroid/recipes/kivy/__init__.py index 5f59457a7d..2446c2101b 100644 --- a/pythonforandroid/recipes/kivy/__init__.py +++ b/pythonforandroid/recipes/kivy/__init__.py @@ -25,7 +25,7 @@ def is_kivy_affected_by_deadlock_issue(recipe=None, arch=None): def is_kivy_less_than_3(recipe=None, arch=None): return packaging.version.parse( str(get_kivy_version(recipe, arch)) - ) < packaging.version.Version("3.0.0") + ) < packaging.version.Version("3.0.0.dev0") class KivyRecipe(PyProjectRecipe):