diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index c4131c53e6..68edad2c3e 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -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 @@ -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", @@ -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( @@ -1401,10 +1400,16 @@ 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" ) @@ -1412,6 +1417,7 @@ def check_host_deps(self): def build_arch(self, arch): self.check_host_deps() + self.ensure_rust_toolchain(arch) super().build_arch(arch) diff --git a/pythonforandroid/recipes/libpthread/__init__.py b/pythonforandroid/recipes/libpthread/__init__.py new file mode 100644 index 0000000000..10feca475a --- /dev/null +++ b/pythonforandroid/recipes/libpthread/__init__.py @@ -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() diff --git a/pythonforandroid/recipes/polars/__init__.py b/pythonforandroid/recipes/polars/__init__.py new file mode 100644 index 0000000000..fd0c89b0e8 --- /dev/null +++ b/pythonforandroid/recipes/polars/__init__.py @@ -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() diff --git a/pythonforandroid/recipes/pyreqwest_impersonate/__init__.py b/pythonforandroid/recipes/pyreqwest_impersonate/__init__.py new file mode 100644 index 0000000000..7e8d5db9ae --- /dev/null +++ b/pythonforandroid/recipes/pyreqwest_impersonate/__init__.py @@ -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() diff --git a/pythonforandroid/recipes/uvloop/__init__.py b/pythonforandroid/recipes/uvloop/__init__.py new file mode 100644 index 0000000000..fcb2204422 --- /dev/null +++ b/pythonforandroid/recipes/uvloop/__init__.py @@ -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()