Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 19 additions & 5 deletions pythonforandroid/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -1563,6 +1563,10 @@ def write_build_options(self, arch):
return option_data

def ensure_args(self, *args):
# Guard against mutating the inherited class-variable list, which would
# bleed meson cross-file args into unrelated PyProjectRecipe subclasses.
if "extra_build_args" not in self.__dict__:
self.extra_build_args = list(self.extra_build_args)
for arg in args:
if arg not in self.extra_build_args:
self.extra_build_args.append(arg)
Expand Down Expand Up @@ -1619,11 +1623,8 @@ def get_recipe_env(self, arch, **kwargs):

# Set rust build target
build_target = self.RUST_ARCH_CODES[arch.arch]
cargo_linker_name = "CARGO_TARGET_{}_LINKER".format(
build_target.upper().replace("-", "_")
)
env["CARGO_BUILD_TARGET"] = build_target
env[cargo_linker_name] = join(
build_target_underscore = build_target.upper().replace("-", "_")
ndk_clang = join(
self.ctx.ndk.llvm_prebuilt_dir,
"bin",
"{}{}-clang".format(
Expand All @@ -1634,6 +1635,19 @@ def get_recipe_env(self, arch, **kwargs):
self.ctx.ndk_api,
),
)
env["CARGO_BUILD_TARGET"] = build_target
env["CARGO_TARGET_{}_LINKER".format(build_target_underscore)] = ndk_clang
# Crates using the `cc` crate in their build.rs (e.g. cryptography-cffi
# to compile the cffi-generated _openssl.c) need a per-target CC too:
# without it, `cc` falls back to the generic CC env var and guesses
# its own --target= from the Rust triple, which for some targets
# (e.g. armv7-linux-androideabi) produces an invalid triple like
# "armv7-none-linux-android" (no API level, wrong vendor). That
# causes the wrong <limits.h> to be picked up, breaking CPython's
# LONG_BIT sanity check in pyport.h. Giving `cc` the NDK's
# per-target clang directly avoids that guesswork.
env["CC_{}".format(build_target.replace("-", "_"))] = ndk_clang
env["AR_{}".format(build_target.replace("-", "_"))] = self.ctx.ndk.llvm_ar
realpython_dir = self.ctx.python_recipe.get_build_dir(arch.arch)

env["RUSTFLAGS"] = "-Clink-args=-L{} -L{}".format(
Expand Down
16 changes: 16 additions & 0 deletions pythonforandroid/recipes/MarkupSafe/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from pythonforandroid.recipe import CompiledComponentsPythonRecipe


class MarkupSafeRecipe(CompiledComponentsPythonRecipe):
version = "2.1.5"
url = "https://pypi.python.org/packages/source/M/MarkupSafe/MarkupSafe-{version}.tar.gz"
depends = [
"setuptools",
"cython",
"pyparsing",
]
call_hostpython_via_targetpython = False
install_in_hostpython = True


recipe = MarkupSafeRecipe()
12 changes: 12 additions & 0 deletions pythonforandroid/recipes/bitstring/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from pythonforandroid.recipe import PythonRecipe


class BitstringRecipe(PythonRecipe):
name = "bitstring"
version = "3.1.9"
url = "https://pypi.python.org/packages/source/b/bitstring/bitstring-{version}.tar.gz"
depends = ["setuptools"]
call_hostpython_via_targetpython = False


recipe = BitstringRecipe()
50 changes: 50 additions & 0 deletions pythonforandroid/recipes/blis/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from pythonforandroid.archs import Archx86_64
from pythonforandroid.recipe import CompiledComponentsPythonRecipe


class BlisRecipe(CompiledComponentsPythonRecipe):
version = "1.2.1"
url = "https://github.com/explosion/cython-blis/archive/refs/tags/release-v{version}.tar.gz"
depends = [
"setuptools",
"cython",
]
hostpython_prerequisites = [ # build only
"numpy",
]
call_hostpython_via_targetpython = False
install_in_hostpython = True

def get_recipe_env(self, arch=None, with_flags_in_cc=False):
env = super().get_recipe_env(arch, with_flags_in_cc)

# see https://github.com/flame/blis/blob/master/config_registry
# if isinstance(arch, ArchAarch_64):
# env["BLIS_ARCH"] = "arm64"
if isinstance(arch, Archx86_64):
env["BLIS_ARCH"] = "x86_64"
else:
env["BLIS_ARCH"] = "generic"

if "CC" in env:
cli = env["CC"].split()[0]
ccache_bin = cli if "ccache" in cli else ""

env["BLIS_COMPILER"] = " ".join(
[
ccache_bin,
arch.get_clang_exe(with_target=True),
]
)

return env

def get_hostrecipe_env(self, arch):
env = super().get_hostrecipe_env(arch)
# see https://github.com/explosion/cython-blis/blob/bdb10be0698002e1d93f79cd91fc1a38f0da3fb3/setup.py#L290
env["BLIS_COMPILER"] = "/usr/bin/ccache gcc"

return env


recipe = BlisRecipe()
20 changes: 19 additions & 1 deletion pythonforandroid/recipes/cryptography/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ class CryptographyRecipe(RustCompiledComponentsRecipe):
version = '46.0.3'
url = 'https://github.com/pyca/cryptography/archive/refs/tags/{version}.tar.gz'
depends = ['openssl', 'cffi']
hostpython_prerequisites = ['maturin', 'cffi']
patches = ['android-cross-include-dir.patch']

def get_recipe_env(self, arch, **kwargs):
env = super().get_recipe_env(arch, **kwargs)
Expand All @@ -17,7 +19,23 @@ def get_recipe_env(self, arch, **kwargs):
openssl_libs = "{}_OPENSSL_LIB_DIR".format(build_target)
env[openssl_include] = join(openssl_build_dir, 'include')
env[openssl_libs] = join(openssl_build_dir)
env["ANDROID_API_LEVEL"] = str(self.ctx.ndk_api)
# cryptography-cffi's build.rs (see android-cross-include-dir.patch)
# asks the *host* python for its own include_dirs via setuptools,
# which is wrong when cross-compiling: it returns hostpython3's
# native Python.h instead of the Android target's, breaking
# CPython's LONG_BIT sanity check in pyport.h.
env["P4A_PYTHON_INCLUDE_DIR"] = self.ctx.python_recipe.include_root(arch.arch)
# Android's bionic linker only resolves symbols from explicitly listed
# NEEDED libraries. abi3 extensions don't link against libpython, so
# _Py_NoneStruct (and other Python symbols) can't be found at dlopen
# time. Explicitly link against libpython so Android resolves them.
python_ver = self.python_major_minor_version
env["RUSTFLAGS"] = (
env.get("RUSTFLAGS", "")
+ " -Clink-args=-lpython{}".format(python_ver)
)
return env


recipe = CryptographyRecipe()
recipe = CryptographyRecipe()
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
--- a/src/rust/cryptography-cffi/build.rs 2026-09-28 02:01:44.655202871 +0200
+++ b/src/rust/cryptography-cffi/build.rs 2026-09-28 02:01:44.658202904 +0200
@@ -47,16 +47,25 @@
)
.unwrap();
println!("cargo:rustc-cfg=python_implementation=\"{python_impl}\"");
- let python_includes = run_python_script(
- &python,
- "import os; \
- import setuptools.dist; \
- import setuptools.command.build_ext; \
- b = setuptools.command.build_ext.build_ext(setuptools.dist.Distribution()); \
- b.finalize_options(); \
- print(os.pathsep.join(b.include_dirs), end='')",
- )
- .unwrap();
+ // p4a: when cross-compiling for Android, `python` here is the host
+ // (native, non-Android) Python used to run build tooling -- asking it
+ // for its own include_dirs via setuptools returns the *host's*
+ // Python.h, not the Android target's, which fails CPython's LONG_BIT
+ // sanity check when compiled for a different word size/ABI. Let the
+ // p4a recipe override this explicitly with the target's include dir.
+ let python_includes = match env::var("P4A_PYTHON_INCLUDE_DIR") {
+ Ok(dir) => dir,
+ Err(_) => run_python_script(
+ &python,
+ "import os; \
+ import setuptools.dist; \
+ import setuptools.command.build_ext; \
+ b = setuptools.command.build_ext.build_ext(setuptools.dist.Distribution()); \
+ b.finalize_options(); \
+ print(os.pathsep.join(b.include_dirs), end='')",
+ )
+ .unwrap(),
+ };
let openssl_c = Path::new(&out_dir).join("_openssl.c");

let mut build = cc::Build::new();
16 changes: 16 additions & 0 deletions pythonforandroid/recipes/cymem/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from pythonforandroid.recipe import CompiledComponentsPythonRecipe


class CymemRecipe(CompiledComponentsPythonRecipe):
version = "2.0.11"
url = "https://pypi.python.org/packages/source/c/cymem/cymem-{version}.tar.gz"
site_packages_name = "cymem"
depends = [
"setuptools",
"cython",
]
call_hostpython_via_targetpython = False
install_in_hostpython = True


recipe = CymemRecipe()
15 changes: 15 additions & 0 deletions pythonforandroid/recipes/freetype-py/fall-back-to-distutils.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
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.")
28 changes: 28 additions & 0 deletions pythonforandroid/recipes/jsonschema/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import os
import sys

import sh

from pythonforandroid.logger import shprint
from pythonforandroid.recipe import PythonRecipe


class JsonschemaRecipe(PythonRecipe):
name = "jsonschema"
version = "4.17.3"
url = None
depends = []

def build_arch(self, arch):
env = {k: v for k, v in os.environ.items()
if k not in ("PYTHONHOME", "PYTHONPATH", "PYTHONNOUSERSITE")}
shprint(
sh.Command(sys.executable), "-m", "pip", "install",
f"{self.name}=={self.version}",
"--target", self.ctx.get_python_install_dir(arch.arch),
"--only-binary", ":all:",
_env=env,
)


recipe = JsonschemaRecipe()
21 changes: 21 additions & 0 deletions pythonforandroid/recipes/kivymd/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from pythonforandroid.recipe import PythonRecipe


class kivyMDRecipe(PythonRecipe):
version = "365aa9b96eee63e0e29c04de297dd222f478fce5"
url = "https://github.com/kivymd/KivyMD/archive/{version}.zip"

depends = [
"kivy",
"materialyoucolor",
"exceptiongroup",
"asyncgui",
"asynckivy",
]
site_packages_name = "kivyMD"
patches = ["kv.patch"]
call_hostpython_via_targetpython = False
install_in_hostpython = True


recipe = kivyMDRecipe()
8 changes: 8 additions & 0 deletions pythonforandroid/recipes/kivymd/kv.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--- a/setup.py
+++ b/setup.py
@@ -80,4 +80,4 @@
try:
- out_files.append(filepath.split(f"kivymd{os.sep}")[1])
+ out_files.append(filepath.split(f"kivymd{os.sep}")[-1])
except IndexError:
out_files.append(filepath)
2 changes: 1 addition & 1 deletion pythonforandroid/recipes/liblzma/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
class LibLzmaRecipe(Recipe):

version = '5.2.4'
url = 'https://tukaani.org/xz/xz-{version}.tar.gz'
url = 'https://github.com/tukaani-project/xz/archive/refs/tags/v{version}.tar.gz'
built_libraries = {'liblzma.so': 'p4a_install/lib'}

def build_arch(self, arch: Arch) -> None:
Expand Down
53 changes: 53 additions & 0 deletions pythonforandroid/recipes/libonnxruntime/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from multiprocessing import cpu_count
from os.path import join

import sh

from pythonforandroid.util import current_directory, ensure_dir
from pythonforandroid.logger import shprint
from pythonforandroid.recipe import Recipe


class LibOnnxRuntimeRecipe(Recipe):
name = "libonnxruntime"
version = '1.24.4'
url = 'https://github.com/microsoft/onnxruntime/archive/refs/tags/v{version}.tar.gz'
built_libraries = {'libonnxruntime.so': 'build'}
patches = ['patches/mlasi_bfloat.patch', 'patches/cse_arm32_alignment.patch']

def build_arch(self, arch):
source_dir = self.get_build_dir(arch.arch)
build_dir = join(source_dir, 'build')
cmake_dir = join(source_dir, 'cmake')
toolchain_file = join(
self.ctx.ndk_dir, 'build', 'cmake', 'android.toolchain.cmake',
)

ensure_dir(build_dir)
with current_directory(build_dir):
env = self.get_recipe_env(arch)
shprint(sh.cmake, cmake_dir,
f'-DANDROID_ABI={arch.arch}',
f'-DANDROID_NATIVE_API_LEVEL={self.ctx.ndk_api}',
f'-DCMAKE_TOOLCHAIN_FILE={toolchain_file}',
'-DCMAKE_BUILD_TYPE=Release',
'-Donnxruntime_BUILD_SHARED_LIB=ON',
'-Donnxruntime_ENABLE_PYTHON=OFF',
'-Donnxruntime_BUILD_UNIT_TESTS=OFF',
'-Donnxruntime_USE_NNAPI_BUILTIN=ON',
_env=env)
shprint(sh.make, f'-j{cpu_count()}', _env=env)

def get_include_dir(self, arch):
"""Headers dir containing onnxruntime_c_api.h for sherpa-onnx."""
return join(
self.get_build_dir(arch.arch),
'include', 'onnxruntime', 'core', 'session',
)

def get_lib_dir(self, arch):
"""Dir containing libonnxruntime.so."""
return join(self.get_build_dir(arch.arch), 'build')


recipe = LibOnnxRuntimeRecipe()
Loading