Skip to content

Commit

Permalink
recipes: add a few recipes
Browse files Browse the repository at this point in the history
  • Loading branch information
T-Dynamos committed May 15, 2024
1 parent 87a32be commit 7d88f4c
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 5 deletions.
16 changes: 11 additions & 5 deletions pythonforandroid/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -1355,6 +1355,8 @@ class RustCompiledComponentsRecipe(PyProjectRecipe):
"x86_64": "x86_64-linux-android",
"x86": "i686-linux-android",
}
# Rust toolchain to be used for building
toolchain = "stable"

call_hostpython_via_targetpython = False

Expand All @@ -1367,6 +1369,7 @@ def get_recipe_env(self, arch, **kwargs):
build_target.upper().replace("-", "_")
)
env["CARGO_BUILD_TARGET"] = build_target
env["TARGET"] = build_target
env[cargo_linker_name] = join(
self.ctx.ndk.llvm_prebuilt_dir,
"bin",
Expand All @@ -1388,10 +1391,6 @@ def get_recipe_env(self, arch, **kwargs):
realpython_dir, "android-build", "build",
"lib.linux-*-{}/".format(self.python_major_minor_version),
))[0])

info_main("Ensuring rust build toolchain")
shprint(sh.rustup, "target", "add", build_target)

# Add host python to PATH
env["PATH"] = ("{hostpython_dir}:{old_path}").format(
hostpython_dir=Recipe.get_recipe(
Expand All @@ -1401,17 +1400,24 @@ def get_recipe_env(self, arch, **kwargs):
)
return env

def ensure_rust_toolchain(self, arch):
info_main("Ensuring rust build toolchain : {}".format(self.toolchain))
shprint(sh.rustup, "toolchain", "install", self.toolchain)
shprint(sh.rustup, "target", "add", "--toolchain", self.toolchain, self.RUST_ARCH_CODES[arch.arch])
shprint(sh.rustup, "default", self.toolchain)

def check_host_deps(self):
if not hasattr(sh, "rustup"):
error(
"`rustup` was not found on host system."
"\n`rustup` was not found on host system."
"Please install it using :"
"\n`curl https://sh.rustup.rs -sSf | sh`\n"
)
exit(1)

def build_arch(self, arch):
self.check_host_deps()
self.ensure_rust_toolchain(arch)
super().build_arch(arch)


Expand Down
51 changes: 51 additions & 0 deletions pythonforandroid/recipes/libpthread/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from os import makedirs, remove
from os.path import exists, join
import sh

from pythonforandroid.recipe import Recipe
from pythonforandroid.logger import shprint


class LibPthread(Recipe):
'''
This is a dumb recipe. We may need this because some recipes inserted some
flags `-lpthread` without our control, case of:
- :class:`~pythonforandroid.recipes.uvloop.UvloopRecipe`
.. note:: the libpthread doesn't exist in android but it is integrated into
libc, so we create a symbolic link which we will remove when our build
finishes'''

def build_arch(self, arch):
libc_path = join(arch.ndk_lib_dir_versioned, 'libc')
# Create a temporary folder to add to link path with a fake libpthread.so:
fake_libpthread_temp_folder = join(
self.get_build_dir(arch.arch),
"p4a-libpthread-recipe-tempdir"
)
if not exists(fake_libpthread_temp_folder):
makedirs(fake_libpthread_temp_folder)

# Set symlinks, and make sure to update them on every build run:
if exists(join(fake_libpthread_temp_folder, "libpthread.so")):
remove(join(fake_libpthread_temp_folder, "libpthread.so"))
shprint(sh.ln, '-sf',
libc_path + '.so',
join(fake_libpthread_temp_folder, "libpthread.so"),
)
if exists(join(fake_libpthread_temp_folder, "libpthread.a")):
remove(join(fake_libpthread_temp_folder, "libpthread.a"))
shprint(sh.ln, '-sf',
libc_path + '.a',
join(fake_libpthread_temp_folder, "libpthread.a"),
)

# Add folder as -L link option for all recipes if not done yet:
if fake_libpthread_temp_folder not in arch.extra_global_link_paths:
arch.extra_global_link_paths.append(
fake_libpthread_temp_folder
)


recipe = LibPthread()
65 changes: 65 additions & 0 deletions pythonforandroid/recipes/polars/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import time
from os.path import join
from glob import glob
from pythonforandroid.logger import warning
from pythonforandroid.recipe import RustCompiledComponentsRecipe

WARNING_MSG = """
This build requires at least 6GB of free RAM. If you cannot arrange free RAM, please consider adding some swap memory.
For Linux:
1. Open a terminal and execute the following commands to create an 8GB swap file:
sudo fallocate -l 8G /swapfile.swap
sudo chmod 700 /swapfile.swap
sudo mkswap /swapfile.swap
sudo swapon /swapfile.swap
2. To make the swap memory permanent, add it to your system's configuration by executing:
sudo sh -c 'echo "/swapfile.swap swap swap defaults 0 0" >> /etc/fstab'
Learn more about swap: https://en.wikipedia.org/wiki/Memory_paging
"""


class PolarsRecipe(RustCompiledComponentsRecipe):
version = "0.20.25"
url = "https://github.com/pola-rs/polars/releases/download/py-{version}/polars-{version}.tar.gz"
toolchain = "nightly-2024-04-15" # from rust-toolchain.toml
need_stl_shared = True

def get_recipe_env(self, arch, **kwargs):
env = super().get_recipe_env(arch, **kwargs)
# Required for libz-ng-sys
env["CMAKE_TOOLCHAIN_FILE"] = join(
self.ctx.ndk_dir, "build", "cmake", "android.toolchain.cmake")

# Enable SIMD instructions
simd_include = glob(join(
self.ctx.ndk.llvm_prebuilt_dir,
"lib64",
"clang",
"*",
"include"
))[0]
env["CFLAGS"] += " -DARM_SIMD -D_MSC_VER -I{}".format(simd_include)

# Required for libgit2-sys
env["CFLAGS"] += " -I{}".format(self.ctx.ndk.sysroot_include_dir)
return env

def build_arch(self, arch):
warning(WARNING_MSG)
time.sleep(5) # let user read the warning

# Polars doesn't officially support 32-bit Python.
# See https://github.com/pola-rs/polars/issues/10460
if arch.arch in ["x86", "armeabi-v7a"]:
warning("Polars does not support architecture: {}".format(arch.arch))
return
else:
super().build_arch(arch)


recipe = PolarsRecipe()
33 changes: 33 additions & 0 deletions pythonforandroid/recipes/pyreqwest_impersonate/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from pythonforandroid.logger import info
from pythonforandroid.recipe import RustCompiledComponentsRecipe


class Pyreqwest_impersonateRecipe(RustCompiledComponentsRecipe):
version = "v0.4.5"
url = "https://github.com/deedy5/pyreqwest_impersonate/archive/refs/tags/{version}.tar.gz"

def get_recipe_env_post(self, arch, **kwargs):
env = super().get_recipe_env(arch, **kwargs)
env["ANDROID_NDK_HOME"] = self.ctx.ndk.llvm_prebuilt_dir
return env

def get_recipe_env_pre(self, arch, **kwargs):
env = super().get_recipe_env(arch, **kwargs)
env["ANDROID_NDK_HOME"] = self.ctx.ndk_dir
return env

def build_arch(self, arch):
# Why need of two env?
# Because there are two dependencies which accepts
# different ANDROID_NDK_HOME
self.get_recipe_env = self.get_recipe_env_pre
prebuild_ = super().build_arch
try:
prebuild_(arch)
except Exception:
info("pyreqwest_impersonate first build failed, as expected")
self.get_recipe_env = self.get_recipe_env_post
prebuild_(arch)


recipe = Pyreqwest_impersonateRecipe()
17 changes: 17 additions & 0 deletions pythonforandroid/recipes/uvloop/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from pythonforandroid.recipe import PyProjectRecipe


class UvloopRecipe(PyProjectRecipe):
# 0.19.0
version = '6c770dc3fbdd281d15c2ad46588c139696f9269c'
url = 'git+https://github.com/MagicStack/uvloop'
depends = ['librt', 'libpthread']

def get_recipe_env(self, arch, **kwargs):
env = super().get_recipe_env(arch, **kwargs)
env["LIBUV_CONFIGURE_HOST"] = arch.command_prefix
env["PLATFORM"] = "android"
return env


recipe = UvloopRecipe()

0 comments on commit 7d88f4c

Please sign in to comment.