From 24494f46901f7adcd01e724db3a741832a3c7459 Mon Sep 17 00:00:00 2001 From: Ansh Dadwal Date: Thu, 27 Mar 2025 02:58:42 +0530 Subject: [PATCH 1/3] `scipy`: update to `v1.15.2` --- Makefile | 13 - ci/constants.py | 2 - pythonforandroid/recipe.py | 75 +- pythonforandroid/recipes/fortran/__init__.py | 188 +++ pythonforandroid/recipes/lapack/__init__.py | 80 -- .../recipes/libopenblas/__init__.py | 50 + pythonforandroid/recipes/numpy/__init__.py | 13 +- pythonforandroid/recipes/pandas/__init__.py | 6 +- pythonforandroid/recipes/python3/__init__.py | 2 +- pythonforandroid/recipes/scipy/__init__.py | 127 +- pythonforandroid/recipes/scipy/meson.patch | 44 + pythonforandroid/recipes/scipy/setup.py.patch | 1098 ----------------- pythonforandroid/recipes/scipy/wrapper.py | 62 + 13 files changed, 453 insertions(+), 1307 deletions(-) create mode 100644 pythonforandroid/recipes/fortran/__init__.py delete mode 100644 pythonforandroid/recipes/lapack/__init__.py create mode 100644 pythonforandroid/recipes/libopenblas/__init__.py create mode 100644 pythonforandroid/recipes/scipy/meson.patch delete mode 100644 pythonforandroid/recipes/scipy/setup.py.patch create mode 100644 pythonforandroid/recipes/scipy/wrapper.py diff --git a/Makefile b/Makefile index 03c4ff3e52..1b31d26bfb 100644 --- a/Makefile +++ b/Makefile @@ -60,19 +60,6 @@ testapps-with-numpy/%: virtualenv --arch=armeabi-v7a --arch=arm64-v8a --arch=x86_64 --arch=x86 \ --permission "(name=android.permission.WRITE_EXTERNAL_STORAGE;maxSdkVersion=18)" --permission "(name=android.permission.INTERNET)" -testapps-with-scipy: testapps-with-scipy/debug/apk testapps-with-scipy/release/aab - -# testapps-with-scipy/MODE/ARTIFACT -testapps-with-scipy/%: virtualenv - $(eval MODE := $(word 2, $(subst /, ,$@))) - $(eval ARTIFACT := $(word 3, $(subst /, ,$@))) - @echo Building testapps-with-scipy for $(MODE) mode and $(ARTIFACT) artifact - . $(ACTIVATE) && cd testapps/on_device_unit_tests/ && \ - export LEGACY_NDK=$(ANDROID_NDK_HOME_LEGACY) && \ - python setup.py $(ARTIFACT) --$(MODE) --sdk-dir $(ANDROID_SDK_HOME) --ndk-dir $(ANDROID_NDK_HOME) \ - --requirements python3,scipy,kivy \ - --arch=armeabi-v7a --arch=arm64-v8a - testapps-webview: testapps-webview/debug/apk testapps-webview/release/aab # testapps-webview/MODE/ARTIFACT diff --git a/ci/constants.py b/ci/constants.py index 382a4a0bfe..458d4bd704 100644 --- a/ci/constants.py +++ b/ci/constants.py @@ -25,8 +25,6 @@ class TargetPython(Enum): # mpmath package with a version >= 0.19 required 'sympy', 'vlc', - # need extra gfortran NDK system add-on - 'lapack', 'scipy', # Outdated and there's a chance that is now useless. 'zope_interface', # Requires zope_interface, which is broken. diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 44469aef2c..42e0902229 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -76,7 +76,7 @@ class Recipe(metaclass=RecipeMeta): _version = None '''A string giving the version of the software the recipe describes, - e.g. ``2.0.3`` or ``master``.''' + e.g. ``2.0.3``. In case of git repository version is branch or a tagged release e.g. ``v12.3`` or ``develop``.''' md5sum = None '''The md5sum of the source from the :attr:`url`. Non-essential, but @@ -109,7 +109,7 @@ class Recipe(metaclass=RecipeMeta): recipe if they are built at all, but whose presence is not essential.''' patches = [] - '''A list of patches to apply to the source. Values can be either a string + '''Alist of patches to apply to the source. Values can be either a string referring to the patch file relative to the recipe dir, or a tuple of the string patch file and a callable, which will receive the kwargs `arch` and `recipe`, which should return True if the patch should be applied.''' @@ -155,6 +155,11 @@ class Recipe(metaclass=RecipeMeta): starting from NDK r18 the `gnustl_shared` lib has been deprecated. ''' + min_ndk_api_support = 20 + ''' + Minimum ndk api your recipe will support + ''' + def get_stl_library(self, arch): return join( arch.ndk_lib_dir, @@ -252,23 +257,20 @@ def report_hook(index, blksize, size): if not isdir(target): if url.startswith('git+'): url = url[4:] - # if 'version' is specified, do a shallow clone - if self.version: - ensure_dir(target) - with current_directory(target): - shprint(sh.git, 'init') - shprint(sh.git, 'remote', 'add', 'origin', url) - else: - shprint(sh.git, 'clone', '--recursive', url, target) - with current_directory(target): - if self.version: - shprint(sh.git, 'fetch', '--tags', '--depth', '1') - shprint(sh.git, 'checkout', self.version) - branch = sh.git('branch', '--show-current') - if branch: - shprint(sh.git, 'pull') - shprint(sh.git, 'pull', '--recurse-submodules') - shprint(sh.git, 'submodule', 'update', '--recursive', '--init', '--depth', '1') + + shprint( + sh.git, + 'clone', + '--branch', + self.version, + '--depth', + '1', + '--recurse-submodules', + '--shallow-submodules', + url, + target, + ) + return target def apply_patch(self, filename, arch, build_dir=None): @@ -375,7 +377,12 @@ def get_recipe_dir(self): # Public Recipe API to be subclassed if needed def download_if_necessary(self): + if self.ctx.ndk_api < self.min_ndk_api_support: + error(f"In order to build '{self.name}', you must set minimum ndk api (minapi) to `{self.min_ndk_api_support}`.\n") + exit(1) + info_main('Downloading {}'.format(self.name)) + user_dir = environ.get('P4A_{}_DIR'.format(self.name.lower())) if user_dir is not None: info('P4A_{}_DIR is set, skipping download for {}'.format( @@ -919,6 +926,32 @@ def folder_name(self): name = self.name return name + def patch_shebang(self, _file, original_bin): + _file_des = open(_file, "r") + + try: + data = _file_des.readlines() + except UnicodeDecodeError: + return + + if "#!" in (line := data[0]): + if line.split("#!")[-1].strip() == original_bin: + return + + info(f"Fixing sheband for '{_file}'") + data.pop(0) + data.insert(0, "#!" + original_bin + "\n") + _file_des.close() + _file_des = open(_file, "w") + _file_des.write("".join(data)) + _file_des.close() + + def patch_shebangs(self, path, original_bin): + # set correct shebang + for file in listdir(path): + _file = join(path, file) + self.patch_shebang(_file, original_bin) + def get_recipe_env(self, arch=None, with_flags_in_cc=True): env = super().get_recipe_env(arch, with_flags_in_cc) env['PYTHONNOUSERSITE'] = '1' @@ -1260,6 +1293,9 @@ def build_arch(self, arch): self.install_hostpython_prerequisites( packages=["build[virtualenv]", "pip"] + self.hostpython_prerequisites ) + python_bin_dir = join(self.hostpython_site_dir, "bin") + self.patch_shebangs(python_bin_dir, self.real_hostpython_location) + build_dir = self.get_build_dir(arch.arch) env = self.get_recipe_env(arch, with_flags_in_cc=True) # make build dir separately @@ -1308,6 +1344,7 @@ def get_recipe_meson_options(self, arch): "cpp_args": self.sanitize_flags(env["CXXFLAGS"], env["CPPFLAGS"]), "c_link_args": self.sanitize_flags(env["LDFLAGS"]), "cpp_link_args": self.sanitize_flags(env["LDFLAGS"]), + "fortran_link_args": self.sanitize_flags(env["LDFLAGS"]), }, "properties": { "needs_exe_wrapper": True, diff --git a/pythonforandroid/recipes/fortran/__init__.py b/pythonforandroid/recipes/fortran/__init__.py new file mode 100644 index 0000000000..78d97cbc64 --- /dev/null +++ b/pythonforandroid/recipes/fortran/__init__.py @@ -0,0 +1,188 @@ +import os +import subprocess +import shutil +import sh +from pathlib import Path +from os.path import join +from pythonforandroid.recipe import Recipe +from pythonforandroid.recommendations import read_ndk_version +from pythonforandroid.logger import info, shprint, info_main +from pythonforandroid.util import ensure_dir +import hashlib + +FLANG_FILES = { + "package-flang-aarch64.tar.bz2": "775f362c758abe8d3173edc7be9ced3730ff14c64d44743017c3af7ceb0a6610", + "package-flang-host.tar.bz2": "04fe24d67ee7eb5a4223299c610013585e75c56467e4b185ed929a3d17e3d077", + "package-flang-x86_64.tar.bz2": "2061a0e3179f4afa55516ce3858582d25ea7b108ff762d9fb4ec8a03b49b36d2", + "package-install.tar.bz2": "d37dc6a58b495807f015c7fec08a57ff95d52ad0d0553cbf573b0215d8a1707c", +} + + +class GFortranRecipe(Recipe): + # flang support in NDK by @licy183 (on github) + name = "fortran" + toolchain_ver = 0 + url = "https://github.com/licy183/ndk-toolchain-clang-with-flang/releases/download/" + + def match_sha256(self, file_path, expected_hash): + sha256 = hashlib.sha256() + with open(file_path, "rb") as f: + for chunk in iter(lambda: f.read(8192), b""): + sha256.update(chunk) + file_hash = sha256.hexdigest() + return file_hash == expected_hash + + @property + def ndk_version(self): + ndk_version = read_ndk_version(self.ctx.ndk_dir) + minor_to_letter = {0: ""} + minor_to_letter.update( + {n + 1: chr(i) for n, i in enumerate(range(ord("b"), ord("b") + 25))} + ) + return f"{ndk_version.major}{minor_to_letter[ndk_version.minor]}" + + def get_cache_dir(self): + dir_name = self.get_dir_name() + return join(self.ctx.build_dir, "other_builds", dir_name) + + def get_fortran_dir(self): + toolchain_name = f"android-r{self.ndk_version}-api-{self.ctx.ndk_api}" + return join( + self.get_cache_dir(), f"{toolchain_name}-flang-v{self.toolchain_ver}" + ) + + def get_incomplete_files(self): + incomplete_files = [] + cache_dir = self.get_cache_dir() + for file, sha256sum in FLANG_FILES.items(): + _file = join(cache_dir, file) + if not (os.path.exists(_file) and self.match_sha256(_file, sha256sum)): + incomplete_files.append(file) + return incomplete_files + + def download_if_necessary(self): + assert self.ndk_version == "27c" + if len(self.get_incomplete_files()) == 0: + return + self.download() + + def download(self): + cache_dir = self.get_cache_dir() + ensure_dir(cache_dir) + for file in self.get_incomplete_files(): + _file = join(cache_dir, file) + if os.path.exists(_file): + os.remove(_file) + self.download_file(f"{self.url}r{join(self.ndk_version, file)}", _file) + + def extract_tar(self, file_path: Path, dest: Path, strip=1): + shprint( + sh.tar, + "xf", + str(file_path), + "--strip-components", + str(strip), + "-C", + str(dest) if dest else ".", + ) + + def create_flang_wrapper(self, path: Path, target: str): + script = f"""#!/usr/bin/env bash +if [ "$1" != "-cpp" ] && [ "$1" != "-fc1" ]; then + `dirname $0`/flang-new --target={target}{self.ctx.ndk_api} -D__ANDROID_API__={self.ctx.ndk_api} "$@" +else + `dirname $0`/flang-new "$@" +fi +""" + path.write_text(script) + path.chmod(0o755) + + def unpack(self, arch): + info_main("Unpacking fortran") + + flang_folder = self.get_fortran_dir() + if os.path.exists(flang_folder): + info("{} is already unpacked, skipping".format(self.name)) + return + + toolchain_path = Path( + join(self.ctx.ndk_dir, "toolchains/llvm/prebuilt/linux-x86_64") + ) + cache_dir = Path(os.path.abspath(self.get_cache_dir())) + + # clean tmp folder + tmp_folder = Path(os.path.abspath(f"{flang_folder}-tmp")) + shutil.rmtree(tmp_folder, ignore_errors=True) + tmp_folder.mkdir(parents=True) + os.chdir(tmp_folder) + + self.extract_tar(cache_dir / "package-install.tar.bz2", None, strip=4) + self.extract_tar(cache_dir / "package-flang-host.tar.bz2", None) + + sysroot_path = tmp_folder / "sysroot" + shutil.copytree(toolchain_path / "sysroot", sysroot_path) + + self.extract_tar( + cache_dir / "package-flang-aarch64.tar.bz2", + sysroot_path / "usr/lib/aarch64-linux-android", + ) + self.extract_tar( + cache_dir / "package-flang-x86_64.tar.bz2", + sysroot_path / "usr/lib/x86_64-linux-android", + ) + + # Fix lib/clang paths + version_output = subprocess.check_output( + [str(tmp_folder / "bin/clang"), "--version"], text=True + ) + clang_version = next( + (line for line in version_output.splitlines() if "clang version" in line), + "", + ) + major_ver = clang_version.split("clang version ")[-1].split(".")[0] + + lib_path = tmp_folder / f"lib/clang/{major_ver}/lib" + src_lib_path = toolchain_path / f"lib/clang/{major_ver}/lib" + shutil.rmtree(lib_path, ignore_errors=True) + lib_path.mkdir(parents=True) + + for item in src_lib_path.iterdir(): + shprint(sh.cp, "-r", str(item), str(lib_path)) + + # Create flang wrappers + targets = [ + "aarch64-linux-android", + "armv7a-linux-androideabi", + "i686-linux-android", + "x86_64-linux-android", + ] + + for target in targets: + wrapper_path = tmp_folder / f"bin/{target}-flang" + self.create_flang_wrapper(wrapper_path, target) + shutil.copy( + wrapper_path, tmp_folder / f"bin/{target}{self.ctx.ndk_api}-flang" + ) + + tmp_folder.rename(flang_folder) + + @property + def bin_path(self): + return f"{self.get_fortran_dir()}/bin" + + def get_host_platform(self, arch): + return { + "arm64-v8a": "aarch64-linux-android", + "armeabi-v7a": "armv7a-linux-androideabi", + "x86_64": "x86_64-linux-android", + "x86": "i686-linux-android", + }[arch] + + def get_fortran_bin(self, arch): + return join(self.bin_path, f"{self.get_host_platform(arch)}-flang") + + def get_fortran_flags(self, arch): + return f"--target={self.get_host_platform(arch)}{self.ctx.ndk_api} -D__ANDROID_API__={self.ctx.ndk_api}" + + +recipe = GFortranRecipe() diff --git a/pythonforandroid/recipes/lapack/__init__.py b/pythonforandroid/recipes/lapack/__init__.py deleted file mode 100644 index b6124dc285..0000000000 --- a/pythonforandroid/recipes/lapack/__init__.py +++ /dev/null @@ -1,80 +0,0 @@ -''' -known to build with cmake version 3.23.2 and NDK r21e. -See https://gitlab.kitware.com/cmake/cmake/-/issues/18739 -''' - -from pythonforandroid.recipe import Recipe -from pythonforandroid.logger import shprint -from pythonforandroid.util import current_directory, ensure_dir, BuildInterruptingException -from multiprocessing import cpu_count -from os.path import join -import sh -import shutil -from os import environ -from pythonforandroid.util import build_platform, rmdir - -arch_to_sysroot = {'armeabi': 'arm', 'armeabi-v7a': 'arm', 'arm64-v8a': 'arm64'} - - -def arch_to_toolchain(arch): - if 'arm' in arch.arch: - return arch.command_prefix - return arch.arch - - -class LapackRecipe(Recipe): - - name = 'lapack' - version = 'v3.10.1' - url = 'https://github.com/Reference-LAPACK/lapack/archive/{version}.tar.gz' - libdir = 'build/install/lib' - built_libraries = {'libblas.so': libdir, 'liblapack.so': libdir, 'libcblas.so': libdir} - - def get_recipe_env(self, arch): - env = super().get_recipe_env(arch) - - ndk_dir = environ.get("LEGACY_NDK") - if ndk_dir is None: - raise BuildInterruptingException("Please set the environment variable 'LEGACY_NDK' to point to a NDK location with gcc/gfortran support (supported NDK version: 'r21e')") - - GCC_VER = '4.9' - HOST = build_platform - - sysroot_suffix = arch_to_sysroot.get(arch.arch, arch.arch) - sysroot = f"{ndk_dir}/platforms/{env['NDK_API']}/arch-{sysroot_suffix}" - FC = f"{ndk_dir}/toolchains/{arch_to_toolchain(arch)}-{GCC_VER}/prebuilt/{HOST}/bin/{arch.command_prefix}-gfortran" - env['FC'] = f'{FC} --sysroot={sysroot}' - if shutil.which(FC) is None: - raise BuildInterruptingException(f"{FC} not found. See https://github.com/mzakharo/android-gfortran") - return env - - def build_arch(self, arch): - source_dir = self.get_build_dir(arch.arch) - build_target = join(source_dir, 'build') - install_target = join(build_target, 'install') - - ensure_dir(build_target) - with current_directory(build_target): - env = self.get_recipe_env(arch) - ndk_dir = environ["LEGACY_NDK"] - rmdir('CMakeFiles') - shprint(sh.rm, '-f', 'CMakeCache.txt', _env=env) - opts = [ - '-DCMAKE_SYSTEM_NAME=Android', - '-DCMAKE_POSITION_INDEPENDENT_CODE=1', - '-DCMAKE_ANDROID_ARCH_ABI={arch}'.format(arch=arch.arch), - '-DCMAKE_ANDROID_NDK=' + ndk_dir, - '-DCMAKE_ANDROID_API={api}'.format(api=self.ctx.ndk_api), - '-DCMAKE_BUILD_TYPE=Release', - '-DCMAKE_INSTALL_PREFIX={}'.format(install_target), - '-DCBLAS=ON', - '-DBUILD_SHARED_LIBS=ON', - ] - if arch.arch == 'armeabi-v7a': - opts.append('-DCMAKE_ANDROID_ARM_NEON=ON') - shprint(sh.cmake, source_dir, *opts, _env=env) - shprint(sh.make, '-j' + str(cpu_count()), _env=env) - shprint(sh.make, 'install', _env=env) - - -recipe = LapackRecipe() diff --git a/pythonforandroid/recipes/libopenblas/__init__.py b/pythonforandroid/recipes/libopenblas/__init__.py new file mode 100644 index 0000000000..e88feb6686 --- /dev/null +++ b/pythonforandroid/recipes/libopenblas/__init__.py @@ -0,0 +1,50 @@ +from pythonforandroid.recipe import Recipe +from pythonforandroid.logger import shprint +from pythonforandroid.util import current_directory, ensure_dir +from multiprocessing import cpu_count +from os.path import join +import sh +from pythonforandroid.util import rmdir + + +class LibOpenBlasRecipe(Recipe): + + version = "0.3.29" + url = "https://github.com/OpenMathLib/OpenBLAS/archive/refs/tags/v{version}.tar.gz" + built_libraries = {"libopenblas.so": "build/lib"} + min_ndk_api_support = 24 # complex math functions support + + def build_arch(self, arch): + source_dir = self.get_build_dir(arch.arch) + build_target = join(source_dir, "build") + + ensure_dir(build_target) + with current_directory(build_target): + env = self.get_recipe_env(arch) + rmdir("CMakeFiles") + shprint(sh.rm, "-f", "CMakeCache.txt", _env=env) + + opts = [ + # default cmake options + "-DCMAKE_SYSTEM_NAME=Android", + "-DCMAKE_ANDROID_ARCH_ABI={arch}".format(arch=arch.arch), + "-DCMAKE_ANDROID_NDK=" + self.ctx.ndk_dir, + "-DCMAKE_ANDROID_API={api}".format(api=self.ctx.ndk_api), + "-DCMAKE_BUILD_TYPE=Release", + "-DBUILD_SHARED_LIBS=ON", + "-DC_LAPACK=ON", + "-DTARGET={target}".format( + target={ + "arm64-v8a": "ARMV8", + "armeabi-v7a": "ARMV7", + "x86_64": "CORE2", + "x86": "CORE2", + }[arch.arch] + ), + ] + + shprint(sh.cmake, source_dir, *opts, _env=env) + shprint(sh.make, "-j" + str(cpu_count()), _env=env) + + +recipe = LibOpenBlasRecipe() diff --git a/pythonforandroid/recipes/numpy/__init__.py b/pythonforandroid/recipes/numpy/__init__.py index fb34c0c9f7..da0f76bdef 100644 --- a/pythonforandroid/recipes/numpy/__init__.py +++ b/pythonforandroid/recipes/numpy/__init__.py @@ -1,17 +1,15 @@ from pythonforandroid.recipe import Recipe, MesonRecipe -from pythonforandroid.logger import error from os.path import join import shutil -NUMPY_NDK_MESSAGE = "In order to build numpy, you must set minimum ndk api (minapi) to `24`.\n" - class NumpyRecipe(MesonRecipe): - version = 'v1.26.5' + version = 'v2.2.4' url = 'git+https://github.com/numpy/numpy' hostpython_prerequisites = ["Cython>=3.0.6"] # meson does not detects venv's cython extra_build_args = ['-Csetup-args=-Dblas=none', '-Csetup-args=-Dlapack=none'] need_stl_shared = True + min_ndk_api_support = 24 # NumPy requires complex math functions which were added in api 24 def get_recipe_meson_options(self, arch): options = super().get_recipe_meson_options(arch) @@ -36,13 +34,6 @@ def get_recipe_env(self, arch, **kwargs): "python3", self.ctx).get_build_dir(arch.arch), "android-build", "python") return env - def download_if_necessary(self): - # NumPy requires complex math functions which were added in api 24 - if self.ctx.ndk_api < 24: - error(NUMPY_NDK_MESSAGE) - exit(1) - super().download_if_necessary() - def build_arch(self, arch): super().build_arch(arch) self.restore_hostpython_prerequisites(["cython"]) diff --git a/pythonforandroid/recipes/pandas/__init__.py b/pythonforandroid/recipes/pandas/__init__.py index 3f56adef6c..a656649c68 100644 --- a/pythonforandroid/recipes/pandas/__init__.py +++ b/pythonforandroid/recipes/pandas/__init__.py @@ -3,10 +3,10 @@ class PandasRecipe(MesonRecipe): - version = 'v2.2.1' + version = 'v2.2.3' url = 'git+https://github.com/pandas-dev/pandas' # noqa depends = ['numpy', 'libbz2', 'liblzma'] - hostpython_prerequisites = ["Cython~=3.0.5"] # meson does not detects venv's cython + hostpython_prerequisites = ["Cython==3.0.5"] # meson does not detects venv's cython patches = ['fix_numpy_includes.patch'] python_depends = ['python-dateutil', 'pytz'] need_stl_shared = True @@ -17,7 +17,7 @@ def get_recipe_env(self, arch, **kwargs): # because we need some includes generated at numpy's compile time env['NUMPY_INCLUDES'] = join( - self.ctx.get_python_install_dir(arch.arch), "numpy/core/include", + self.ctx.get_python_install_dir(arch.arch), "numpy/_core/include", ) env["PYTHON_INCLUDE_DIR"] = self.ctx.python_recipe.include_root(arch) diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index 2334db6add..6127762e5a 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -141,7 +141,7 @@ class Python3Recipe(TargetPythonRecipe): site_packages_dir_blacklist = { '__pycache__', - 'tests' + # 'tests' } '''The directories from site packages dir that we don't want to be included in our python bundle.''' diff --git a/pythonforandroid/recipes/scipy/__init__.py b/pythonforandroid/recipes/scipy/__init__.py index 242ca04234..12e807cde8 100644 --- a/pythonforandroid/recipes/scipy/__init__.py +++ b/pythonforandroid/recipes/scipy/__init__.py @@ -1,91 +1,58 @@ -from multiprocessing import cpu_count -from os.path import join -from os import environ -import sh -from pythonforandroid.logger import shprint -from pythonforandroid.recipe import CompiledComponentsPythonRecipe, Recipe -from pythonforandroid.util import build_platform, current_directory +from os.path import join, dirname, basename +from pythonforandroid.recipe import MesonRecipe, Recipe +from pathlib import Path -def arch_to_toolchain(arch): - if 'arm' in arch.arch: - return arch.command_prefix - return arch.arch +class ScipyRecipe(MesonRecipe): - -class ScipyRecipe(CompiledComponentsPythonRecipe): - - version = 'maintenance/1.11.x' - url = 'git+https://github.com/scipy/scipy.git' - git_commit = 'b430bf54b5064465983813e2cfef3fcb86c3df07' # version 1.11.3 - site_packages_name = 'scipy' - hostpython_prerequisites = ['numpy'] - depends = ['setuptools', 'cython', 'numpy', 'lapack', 'pybind11'] - call_hostpython_via_targetpython = False + version = "v1.15.2" + url = "git+https://github.com/scipy/scipy.git" + hostpython_prerequisites = ["Cython>=3.0.6"] + depends = ["numpy", "libopenblas", "fortran"] need_stl_shared = True - patches = ["setup.py.patch"] - - def build_compiled_components(self, arch): - self.setup_extra_args = ['-j', str(cpu_count())] - super().build_compiled_components(arch) - self.setup_extra_args = [] - - def rebuild_compiled_components(self, arch, env): - self.setup_extra_args = ['-j', str(cpu_count())] - super().rebuild_compiled_components(arch, env) - self.setup_extra_args = [] - - def download_file(self, url, target, cwd=None): - super().download_file(url, target, cwd=cwd) - with current_directory(target): - shprint(sh.git, 'fetch', '--unshallow') - shprint(sh.git, 'checkout', self.git_commit) - - def get_recipe_env(self, arch): - env = super().get_recipe_env(arch) + meson_version = "1.5.0" + hostpython_prerequisites = ["numpy"] + patches = ["meson.patch"] + + def get_recipe_meson_options(self, arch): + options = super().get_recipe_meson_options(arch) + options["binaries"]["python"] = self.ctx.python_recipe.python_exe + options["binaries"]["fortran"] = self.place_wrapper(arch) + options["properties"]["numpy-include-dir"] = join( + self.ctx.get_python_install_dir(arch.arch), "numpy/_core/include" + ) + self.ensure_args( + "-Csetup-args=-Dblas=openblas", + "-Csetup-args=-Dlapack=openblas", + f"-Csetup-args=-Dopenblas_libdir={self.ctx.get_libs_dir(arch.arch)}", + f'-Csetup-args=-Dopenblas_incldir={join(Recipe.get_recipe("libopenblas", self.ctx).get_build_dir(arch.arch), "build")}', + "-Csetup-args=-Duse-pythran=false", + ) + return options + + def place_wrapper(self, arch): + compiler = Recipe.get_recipe("fortran", self.ctx).get_fortran_bin(arch.arch) + file = join(self.get_recipe_dir(), "wrapper.py") + with open(file, "r") as _file: + data = _file.read() + _file.close() + data = data.replace("@COMPILER@", compiler) + # custom compiler + # taken from: https://github.com/termux/termux-packages/blob/master/packages/python-scipy/ + m_compiler = Path(join(dirname(compiler), basename(compiler) + "-scipy")) + m_compiler.write_text(data) + m_compiler.chmod(0o755) + self.patch_shebang(str(m_compiler), self.real_hostpython_location) + return str(m_compiler) + + def get_recipe_env(self, arch, **kwargs): + env = super().get_recipe_env(arch, **kwargs) arch_env = arch.get_env() - - env['LDFLAGS'] = arch_env['LDFLAGS'] - env['LDFLAGS'] += ' -L{} -lpython{}'.format( + env["LDFLAGS"] = arch_env["LDFLAGS"] + env["LDFLAGS"] += " -L{} -lpython{}".format( self.ctx.python_recipe.link_root(arch.arch), self.ctx.python_recipe.link_version, ) - - ndk_dir = environ["LEGACY_NDK"] - GCC_VER = '4.9' - HOST = build_platform - suffix = '64' if '64' in arch.arch else '' - - prefix = arch.command_prefix - CLANG_BIN = f'{ndk_dir}/toolchains/llvm/prebuilt/{HOST}/bin/' - GCC = f'{ndk_dir}/toolchains/{arch_to_toolchain(arch)}-{GCC_VER}/prebuilt/{HOST}' - libgfortran = f'{GCC}/{prefix}/lib{suffix}' - numpylib = self.ctx.get_python_install_dir(arch.arch) + '/numpy' - arch_cflags = ' '.join(arch.arch_cflags) - LDSHARED_opts = f'-target {arch.target} {arch_cflags} ' + ' '.join(arch.common_ldshared) - - # TODO: add pythran support - env['SCIPY_USE_PYTHRAN'] = '0' - - lapack_dir = join(Recipe.get_recipe('lapack', self.ctx).get_build_dir(arch.arch), 'build', 'install') - env['LAPACK'] = f'{lapack_dir}/lib' - env['BLAS'] = env['LAPACK'] - - # compilers - env['F77'] = f'{GCC}/bin/{prefix}-gfortran' - env['F90'] = f'{GCC}/bin/{prefix}-gfortran' - env['CC'] = f'{CLANG_BIN}clang -target {arch.target} {arch_cflags}' - env['CXX'] = f'{CLANG_BIN}clang++ -target {arch.target} {arch_cflags}' - - # scipy expects ldshared to be a single executable without options - env['LDSHARED'] = f'{CLANG_BIN}/clang' - - # erase the default NDK C++ include options - env['CPPFLAGS'] = '-DANDROID' - - # configure linker - env['LDFLAGS'] += f' {LDSHARED_opts} -L{libgfortran} -L{numpylib}/core/lib -L{numpylib}/random/lib' - env['LDFLAGS'] += f' -l{self.stl_lib_name}' return env diff --git a/pythonforandroid/recipes/scipy/meson.patch b/pythonforandroid/recipes/scipy/meson.patch new file mode 100644 index 0000000000..1fa91e9276 --- /dev/null +++ b/pythonforandroid/recipes/scipy/meson.patch @@ -0,0 +1,44 @@ +diff '--color=auto' -uNr scipy.git/meson.options scipy.git.patch/meson.options +--- scipy.git/meson.options 2025-03-27 02:55:14.586853766 +0530 ++++ scipy.git.patch/meson.options 2025-03-27 02:07:29.736674085 +0530 +@@ -2,6 +2,8 @@ + description: 'option for BLAS library switching') + option('lapack', type: 'string', value: 'openblas', + description: 'option for LAPACK library switching') ++option('openblas_incldir', type: 'string', value: '', description: 'OpenBLAS include directory') ++option('openblas_libdir', type: 'string', value: '', description: 'OpenBLAS library directory') + option('use-g77-abi', type: 'boolean', value: false, + description: 'If set to true, forces using g77 compatibility wrappers ' + + 'for LAPACK functions. The default is to use gfortran ' + +diff '--color=auto' -uNr scipy.git/scipy/meson.build scipy.git.patch/scipy/meson.build +--- scipy.git/scipy/meson.build 2025-03-27 02:55:14.632428649 +0530 ++++ scipy.git.patch/scipy/meson.build 2025-03-27 11:25:33.756445056 +0530 +@@ -268,10 +268,18 @@ + endif + endif + ++openblas_inc = get_option('openblas_incldir') ++openblas_lib = get_option('openblas_libdir') ++ ++openblas_dep = declare_dependency( ++ include_directories: include_directories(openblas_inc), ++ link_args: ['-L' + openblas_lib, '-lopenblas'] ++) ++ + # pkg-config uses a lower-case name while CMake uses a capitalized name, so try + # that too to make the fallback detection with CMake work + if blas_name == 'openblas' +- blas = dependency(['openblas', 'OpenBLAS']) ++ blas = openblas_dep + elif blas_name != 'scipy-openblas' # if so, we found it already + blas = dependency(blas_name) + endif +@@ -295,7 +303,7 @@ + # use that - no need to run the full detection twice. + lapack = blas + elif lapack_name == 'openblas' +- lapack = dependency(['openblas', 'OpenBLAS']) ++ lapack = openblas_dep + else + lapack = dependency(lapack_name) + endif diff --git a/pythonforandroid/recipes/scipy/setup.py.patch b/pythonforandroid/recipes/scipy/setup.py.patch deleted file mode 100644 index 9fbc0ab5fb..0000000000 --- a/pythonforandroid/recipes/scipy/setup.py.patch +++ /dev/null @@ -1,1098 +0,0 @@ -diff '--color=auto' -uNr scipy/_setup.py scipy.mod/_setup.py ---- scipy/_setup.py 2023-10-30 19:20:36.545524745 +0530 -+++ scipy.mod/_setup.py 1970-01-01 05:30:00.000000000 +0530 -@@ -1,545 +0,0 @@ --#!/usr/bin/env python --"""SciPy: Scientific Library for Python -- --SciPy (pronounced "Sigh Pie") is open-source software for mathematics, --science, and engineering. The SciPy library --depends on NumPy, which provides convenient and fast N-dimensional --array manipulation. The SciPy library is built to work with NumPy --arrays, and provides many user-friendly and efficient numerical --routines such as routines for numerical integration and optimization. --Together, they run on all popular operating systems, are quick to --install, and are free of charge. NumPy and SciPy are easy to use, --but powerful enough to be depended upon by some of the world's --leading scientists and engineers. If you need to manipulate --numbers on a computer and display or publish the results, --give SciPy a try! -- --""" -- -- --# IMPORTANT: --# --# THIS FILE IS INTENTIONALLY RENAMED FROM setup.py TO _setup.py --# IT IS ONLY KEPT IN THE REPO BECAUSE conda-forge STILL NEEDS IT --# FOR BUILDING SCIPY ON WINDOWS. IT SHOULD NOT BE USED BY ANYONE --# ELSE. USE `pip install .` OR ANOTHER INSTALL COMMAND USING A --# BUILD FRONTEND LIKE pip OR pypa/build TO INSTALL SCIPY FROM SOURCE. --# --# SEE http://scipy.github.io/devdocs/building/index.html FOR BUILD --# INSTRUCTIONS. -- -- --DOCLINES = (__doc__ or '').split("\n") -- --import os --import sys --import subprocess --import textwrap --import warnings --import sysconfig --from tools.version_utils import write_version_py, get_version_info --from tools.version_utils import IS_RELEASE_BRANCH --import importlib -- -- --if sys.version_info[:2] < (3, 9): -- raise RuntimeError("Python version >= 3.9 required.") -- --import builtins -- -- --CLASSIFIERS = """\ --Development Status :: 5 - Production/Stable --Intended Audience :: Science/Research --Intended Audience :: Developers --License :: OSI Approved :: BSD License --Programming Language :: C --Programming Language :: Python --Programming Language :: Python :: 3 --Programming Language :: Python :: 3.9 --Programming Language :: Python :: 3.10 --Programming Language :: Python :: 3.11 --Topic :: Software Development :: Libraries --Topic :: Scientific/Engineering --Operating System :: Microsoft :: Windows --Operating System :: POSIX :: Linux --Operating System :: POSIX --Operating System :: Unix --Operating System :: MacOS -- --""" -- -- --# BEFORE importing setuptools, remove MANIFEST. Otherwise it may not be --# properly updated when the contents of directories change (true for distutils, --# not sure about setuptools). --if os.path.exists('MANIFEST'): -- os.remove('MANIFEST') -- --# This is a bit hackish: we are setting a global variable so that the main --# scipy __init__ can detect if it is being loaded by the setup routine, to --# avoid attempting to load components that aren't built yet. While ugly, it's --# a lot more robust than what was previously being used. --builtins.__SCIPY_SETUP__ = True -- -- --def check_submodules(): -- """ verify that the submodules are checked out and clean -- use `git submodule update --init`; on failure -- """ -- if not os.path.exists('.git'): -- return -- with open('.gitmodules') as f: -- for l in f: -- if 'path' in l: -- p = l.split('=')[-1].strip() -- if not os.path.exists(p): -- raise ValueError('Submodule %s missing' % p) -- -- -- proc = subprocess.Popen(['git', 'submodule', 'status'], -- stdout=subprocess.PIPE) -- status, _ = proc.communicate() -- status = status.decode("ascii", "replace") -- for line in status.splitlines(): -- if line.startswith('-') or line.startswith('+'): -- raise ValueError('Submodule not clean: %s' % line) -- -- --class concat_license_files(): -- """Merge LICENSE.txt and LICENSES_bundled.txt for sdist creation -- -- Done this way to keep LICENSE.txt in repo as exact BSD 3-clause (see -- NumPy gh-13447). This makes GitHub state correctly how SciPy is licensed. -- """ -- def __init__(self): -- self.f1 = 'LICENSE.txt' -- self.f2 = 'LICENSES_bundled.txt' -- -- def __enter__(self): -- """Concatenate files and remove LICENSES_bundled.txt""" -- with open(self.f1, 'r') as f1: -- self.bsd_text = f1.read() -- -- with open(self.f1, 'a') as f1: -- with open(self.f2, 'r') as f2: -- self.bundled_text = f2.read() -- f1.write('\n\n') -- f1.write(self.bundled_text) -- -- def __exit__(self, exception_type, exception_value, traceback): -- """Restore content of both files""" -- with open(self.f1, 'w') as f: -- f.write(self.bsd_text) -- -- --from distutils.command.sdist import sdist --class sdist_checked(sdist): -- """ check submodules on sdist to prevent incomplete tarballs """ -- def run(self): -- check_submodules() -- with concat_license_files(): -- sdist.run(self) -- -- --def get_build_ext_override(): -- """ -- Custom build_ext command to tweak extension building. -- """ -- from numpy.distutils.command.build_ext import build_ext as npy_build_ext -- if int(os.environ.get('SCIPY_USE_PYTHRAN', 1)): -- try: -- import pythran -- from pythran.dist import PythranBuildExt -- except ImportError: -- BaseBuildExt = npy_build_ext -- else: -- BaseBuildExt = PythranBuildExt[npy_build_ext] -- _pep440 = importlib.import_module('scipy._lib._pep440') -- if _pep440.parse(pythran.__version__) < _pep440.Version('0.11.0'): -- raise RuntimeError("The installed `pythran` is too old, >= " -- "0.11.0 is needed, {} detected. Please " -- "upgrade Pythran, or use `export " -- "SCIPY_USE_PYTHRAN=0`.".format( -- pythran.__version__)) -- else: -- BaseBuildExt = npy_build_ext -- -- class build_ext(BaseBuildExt): -- def finalize_options(self): -- super().finalize_options() -- -- # Disable distutils parallel build, due to race conditions -- # in numpy.distutils (Numpy issue gh-15957) -- if self.parallel: -- print("NOTE: -j build option not supported. Set NPY_NUM_BUILD_JOBS=4 " -- "for parallel build.") -- self.parallel = None -- -- def build_extension(self, ext): -- # When compiling with GNU compilers, use a version script to -- # hide symbols during linking. -- if self.__is_using_gnu_linker(ext): -- export_symbols = self.get_export_symbols(ext) -- text = '{global: %s; local: *; };' % (';'.join(export_symbols),) -- -- script_fn = os.path.join(self.build_temp, 'link-version-{}.map'.format(ext.name)) -- with open(script_fn, 'w') as f: -- f.write(text) -- # line below fixes gh-8680 -- ext.extra_link_args = [arg for arg in ext.extra_link_args if not "version-script" in arg] -- ext.extra_link_args.append('-Wl,--version-script=' + script_fn) -- -- # Allow late configuration -- hooks = getattr(ext, '_pre_build_hook', ()) -- _run_pre_build_hooks(hooks, (self, ext)) -- -- super().build_extension(ext) -- -- def __is_using_gnu_linker(self, ext): -- if not sys.platform.startswith('linux'): -- return False -- -- # Fortran compilation with gfortran uses it also for -- # linking. For the C compiler, we detect gcc in a similar -- # way as distutils does it in -- # UnixCCompiler.runtime_library_dir_option -- if ext.language == 'f90': -- is_gcc = (self._f90_compiler.compiler_type in ('gnu', 'gnu95')) -- elif ext.language == 'f77': -- is_gcc = (self._f77_compiler.compiler_type in ('gnu', 'gnu95')) -- else: -- is_gcc = False -- if self.compiler.compiler_type == 'unix': -- cc = sysconfig.get_config_var("CC") -- if not cc: -- cc = "" -- compiler_name = os.path.basename(cc.split(" ")[0]) -- is_gcc = "gcc" in compiler_name or "g++" in compiler_name -- return is_gcc and sysconfig.get_config_var('GNULD') == 'yes' -- -- return build_ext -- -- --def get_build_clib_override(): -- """ -- Custom build_clib command to tweak library building. -- """ -- from numpy.distutils.command.build_clib import build_clib as old_build_clib -- -- class build_clib(old_build_clib): -- def finalize_options(self): -- super().finalize_options() -- -- # Disable parallelization (see build_ext above) -- self.parallel = None -- -- def build_a_library(self, build_info, lib_name, libraries): -- # Allow late configuration -- hooks = build_info.get('_pre_build_hook', ()) -- _run_pre_build_hooks(hooks, (self, build_info)) -- old_build_clib.build_a_library(self, build_info, lib_name, libraries) -- -- return build_clib -- -- --def _run_pre_build_hooks(hooks, args): -- """Call a sequence of pre-build hooks, if any""" -- if hooks is None: -- hooks = () -- elif not hasattr(hooks, '__iter__'): -- hooks = (hooks,) -- for hook in hooks: -- hook(*args) -- -- --def generate_cython(): -- cwd = os.path.abspath(os.path.dirname(__file__)) -- print("Cythonizing sources") -- p = subprocess.call([sys.executable, -- os.path.join(cwd, 'tools', 'cythonize.py'), -- 'scipy'], -- cwd=cwd) -- if p != 0: -- # Could be due to a too old pip version and build isolation, check that -- try: -- # Note, pip may not be installed or not have been used -- import pip -- except (ImportError, ModuleNotFoundError): -- raise RuntimeError("Running cythonize failed!") -- else: -- _pep440 = importlib.import_module('scipy._lib._pep440') -- if _pep440.parse(pip.__version__) < _pep440.Version('18.0.0'): -- raise RuntimeError("Cython not found or too old. Possibly due " -- "to `pip` being too old, found version {}, " -- "needed is >= 18.0.0.".format( -- pip.__version__)) -- else: -- raise RuntimeError("Running cythonize failed!") -- -- --def parse_setuppy_commands(): -- """Check the commands and respond appropriately. Disable broken commands. -- -- Return a boolean value for whether or not to run the build or not (avoid -- parsing Cython and template files if False). -- """ -- args = sys.argv[1:] -- -- if not args: -- # User forgot to give an argument probably, let setuptools handle that. -- return True -- -- info_commands = ['--help-commands', '--name', '--version', '-V', -- '--fullname', '--author', '--author-email', -- '--maintainer', '--maintainer-email', '--contact', -- '--contact-email', '--url', '--license', '--description', -- '--long-description', '--platforms', '--classifiers', -- '--keywords', '--provides', '--requires', '--obsoletes'] -- -- for command in info_commands: -- if command in args: -- return False -- -- # Note that 'alias', 'saveopts' and 'setopt' commands also seem to work -- # fine as they are, but are usually used together with one of the commands -- # below and not standalone. Hence they're not added to good_commands. -- good_commands = ('develop', 'sdist', 'build', 'build_ext', 'build_py', -- 'build_clib', 'build_scripts', 'bdist_wheel', 'bdist_rpm', -- 'bdist_wininst', 'bdist_msi', 'bdist_mpkg') -- -- for command in good_commands: -- if command in args: -- return True -- -- # The following commands are supported, but we need to show more -- # useful messages to the user -- if 'install' in args: -- print(textwrap.dedent(""" -- Note: for reliable uninstall behaviour and dependency installation -- and uninstallation, please use pip instead of using -- `setup.py install`: -- -- - `pip install .` (from a git repo or downloaded source -- release) -- - `pip install scipy` (last SciPy release on PyPI) -- -- """)) -- return True -- -- if '--help' in args or '-h' in sys.argv[1]: -- print(textwrap.dedent(""" -- SciPy-specific help -- ------------------- -- -- To install SciPy from here with reliable uninstall, we recommend -- that you use `pip install .`. To install the latest SciPy release -- from PyPI, use `pip install scipy`. -- -- For help with build/installation issues, please ask on the -- scipy-user mailing list. If you are sure that you have run -- into a bug, please report it at https://github.com/scipy/scipy/issues. -- -- Setuptools commands help -- ------------------------ -- """)) -- return False -- -- -- # The following commands aren't supported. They can only be executed when -- # the user explicitly adds a --force command-line argument. -- bad_commands = dict( -- test=""" -- `setup.py test` is not supported. Use one of the following -- instead: -- -- - `python runtests.py` (to build and test) -- - `python runtests.py --no-build` (to test installed scipy) -- - `>>> scipy.test()` (run tests for installed scipy -- from within an interpreter) -- """, -- upload=""" -- `setup.py upload` is not supported, because it's insecure. -- Instead, build what you want to upload and upload those files -- with `twine upload -s ` instead. -- """, -- upload_docs="`setup.py upload_docs` is not supported", -- easy_install="`setup.py easy_install` is not supported", -- clean=""" -- `setup.py clean` is not supported, use one of the following instead: -- -- - `git clean -xdf` (cleans all files) -- - `git clean -Xdf` (cleans all versioned files, doesn't touch -- files that aren't checked into the git repo) -- """, -- check="`setup.py check` is not supported", -- register="`setup.py register` is not supported", -- bdist_dumb="`setup.py bdist_dumb` is not supported", -- bdist="`setup.py bdist` is not supported", -- flake8="`setup.py flake8` is not supported, use flake8 standalone", -- build_sphinx="`setup.py build_sphinx` is not supported, see doc/README.md", -- ) -- bad_commands['nosetests'] = bad_commands['test'] -- for command in ('upload_docs', 'easy_install', 'bdist', 'bdist_dumb', -- 'register', 'check', 'install_data', 'install_headers', -- 'install_lib', 'install_scripts', ): -- bad_commands[command] = "`setup.py %s` is not supported" % command -- -- for command in bad_commands.keys(): -- if command in args: -- print(textwrap.dedent(bad_commands[command]) + -- "\nAdd `--force` to your command to use it anyway if you " -- "must (unsupported).\n") -- sys.exit(1) -- -- # Commands that do more than print info, but also don't need Cython and -- # template parsing. -- other_commands = ['egg_info', 'install_egg_info', 'rotate'] -- for command in other_commands: -- if command in args: -- return False -- -- # If we got here, we didn't detect what setup.py command was given -- warnings.warn("Unrecognized setuptools command ('{}'), proceeding with " -- "generating Cython sources and expanding templates".format( -- ' '.join(sys.argv[1:]))) -- return True -- --def check_setuppy_command(): -- run_build = parse_setuppy_commands() -- if run_build: -- try: -- pkgname = 'numpy' -- import numpy -- pkgname = 'pybind11' -- import pybind11 -- except ImportError as exc: # We do not have our build deps installed -- print(textwrap.dedent( -- """Error: '%s' must be installed before running the build. -- """ -- % (pkgname,))) -- sys.exit(1) -- -- return run_build -- --def configuration(parent_package='', top_path=None): -- from numpy.distutils.system_info import get_info, NotFoundError -- from numpy.distutils.misc_util import Configuration -- -- lapack_opt = get_info('lapack_opt') -- -- if not lapack_opt: -- if sys.platform == "darwin": -- msg = ('No BLAS/LAPACK libraries found. ' -- 'Note: Accelerate is no longer supported.') -- else: -- msg = 'No BLAS/LAPACK libraries found.' -- msg += ("\n" -- "To build Scipy from sources, BLAS & LAPACK libraries " -- "need to be installed.\n" -- "See site.cfg.example in the Scipy source directory and\n" -- "https://docs.scipy.org/doc/scipy/dev/contributor/building.html " -- "for details.") -- raise NotFoundError(msg) -- -- config = Configuration(None, parent_package, top_path) -- config.set_options(ignore_setup_xxx_py=True, -- assume_default_configuration=True, -- delegate_options_to_subpackages=True, -- quiet=True) -- -- config.add_subpackage('scipy') -- config.add_data_files(('scipy', '*.txt')) -- -- config.get_version('scipy/version.py') -- -- return config -- -- --def setup_package(): -- # In maintenance branch, change np_maxversion to N+3 if numpy is at N -- # Update here, in pyproject.toml, and in scipy/__init__.py -- # Rationale: SciPy builds without deprecation warnings with N; deprecations -- # in N+1 will turn into errors in N+3 -- # For Python versions, if releases is (e.g.) <=3.9.x, set bound to 3.10 -- np_minversion = '1.21.6' -- np_maxversion = '1.28.0' -- python_minversion = '3.9' -- python_maxversion = '3.13' -- if IS_RELEASE_BRANCH: -- req_np = 'numpy>={},<{}'.format(np_minversion, np_maxversion) -- req_py = '>={},<{}'.format(python_minversion, python_maxversion) -- else: -- req_np = 'numpy>={}'.format(np_minversion) -- req_py = '>={}'.format(python_minversion) -- -- # Rewrite the version file every time -- write_version_py('.') -- -- cmdclass = {'sdist': sdist_checked} -- -- metadata = dict( -- name='scipy', -- maintainer="SciPy Developers", -- maintainer_email="scipy-dev@python.org", -- description=DOCLINES[0], -- long_description="\n".join(DOCLINES[2:]), -- url="https://www.scipy.org", -- download_url="https://github.com/scipy/scipy/releases", -- project_urls={ -- "Bug Tracker": "https://github.com/scipy/scipy/issues", -- "Documentation": "https://docs.scipy.org/doc/scipy/reference/", -- "Source Code": "https://github.com/scipy/scipy", -- }, -- license='BSD', -- cmdclass=cmdclass, -- classifiers=[_f for _f in CLASSIFIERS.split('\n') if _f], -- platforms=["Windows", "Linux", "Solaris", "Mac OS-X", "Unix"], -- install_requires=[req_np], -- python_requires=req_py, -- zip_safe=False, -- ) -- -- if "--force" in sys.argv: -- run_build = True -- sys.argv.remove('--force') -- else: -- # Raise errors for unsupported commands, improve help output, etc. -- run_build = check_setuppy_command() -- -- # Disable OSX Accelerate, it has too old LAPACK -- os.environ['ACCELERATE'] = 'None' -- -- # This import is here because it needs to be done before importing setup() -- # from numpy.distutils, but after the MANIFEST removing and sdist import -- # higher up in this file. -- from setuptools import setup -- -- if run_build: -- from numpy.distutils.core import setup -- -- # Customize extension building -- cmdclass['build_ext'] = get_build_ext_override() -- cmdclass['build_clib'] = get_build_clib_override() -- -- if not 'sdist' in sys.argv: -- # Generate Cython sources, unless we're creating an sdist -- # Cython is a build dependency, and shipping generated .c files -- # can cause problems (see gh-14199) -- generate_cython() -- -- metadata['configuration'] = configuration -- else: -- # Don't import numpy here - non-build actions are required to succeed -- # without NumPy for example when pip is used to install Scipy when -- # NumPy is not yet present in the system. -- -- # Version number is added to metadata inside configuration() if build -- # is run. -- metadata['version'] = get_version_info('.')[0] -- -- setup(**metadata) -- -- --if __name__ == '__main__': -- setup_package() -diff '--color=auto' -uNr scipy/setup.py scipy.mod/setup.py ---- scipy/setup.py 1970-01-01 05:30:00.000000000 +0530 -+++ scipy.mod/setup.py 2023-10-30 19:22:02.921729484 +0530 -@@ -0,0 +1,545 @@ -+#!/usr/bin/env python -+"""SciPy: Scientific Library for Python -+ -+SciPy (pronounced "Sigh Pie") is open-source software for mathematics, -+science, and engineering. The SciPy library -+depends on NumPy, which provides convenient and fast N-dimensional -+array manipulation. The SciPy library is built to work with NumPy -+arrays, and provides many user-friendly and efficient numerical -+routines such as routines for numerical integration and optimization. -+Together, they run on all popular operating systems, are quick to -+install, and are free of charge. NumPy and SciPy are easy to use, -+but powerful enough to be depended upon by some of the world's -+leading scientists and engineers. If you need to manipulate -+numbers on a computer and display or publish the results, -+give SciPy a try! -+ -+""" -+ -+ -+# IMPORTANT: -+# -+# THIS FILE IS INTENTIONALLY RENAMED FROM setup.py TO _setup.py -+# IT IS ONLY KEPT IN THE REPO BECAUSE conda-forge STILL NEEDS IT -+# FOR BUILDING SCIPY ON WINDOWS. IT SHOULD NOT BE USED BY ANYONE -+# ELSE. USE `pip install .` OR ANOTHER INSTALL COMMAND USING A -+# BUILD FRONTEND LIKE pip OR pypa/build TO INSTALL SCIPY FROM SOURCE. -+# -+# SEE http://scipy.github.io/devdocs/building/index.html FOR BUILD -+# INSTRUCTIONS. -+ -+ -+DOCLINES = (__doc__ or '').split("\n") -+ -+import os -+import sys -+import subprocess -+import textwrap -+import warnings -+import sysconfig -+from tools.version_utils import write_version_py, get_version_info -+from tools.version_utils import IS_RELEASE_BRANCH -+import importlib -+ -+ -+if sys.version_info[:2] < (3, 9): -+ raise RuntimeError("Python version >= 3.9 required.") -+ -+import builtins -+ -+ -+CLASSIFIERS = """\ -+Development Status :: 5 - Production/Stable -+Intended Audience :: Science/Research -+Intended Audience :: Developers -+License :: OSI Approved :: BSD License -+Programming Language :: C -+Programming Language :: Python -+Programming Language :: Python :: 3 -+Programming Language :: Python :: 3.9 -+Programming Language :: Python :: 3.10 -+Programming Language :: Python :: 3.11 -+Topic :: Software Development :: Libraries -+Topic :: Scientific/Engineering -+Operating System :: Microsoft :: Windows -+Operating System :: POSIX :: Linux -+Operating System :: POSIX -+Operating System :: Unix -+Operating System :: MacOS -+ -+""" -+ -+ -+# BEFORE importing setuptools, remove MANIFEST. Otherwise it may not be -+# properly updated when the contents of directories change (true for distutils, -+# not sure about setuptools). -+if os.path.exists('MANIFEST'): -+ os.remove('MANIFEST') -+ -+# This is a bit hackish: we are setting a global variable so that the main -+# scipy __init__ can detect if it is being loaded by the setup routine, to -+# avoid attempting to load components that aren't built yet. While ugly, it's -+# a lot more robust than what was previously being used. -+builtins.__SCIPY_SETUP__ = True -+ -+ -+def check_submodules(): -+ """ verify that the submodules are checked out and clean -+ use `git submodule update --init`; on failure -+ """ -+ if not os.path.exists('.git'): -+ return -+ with open('.gitmodules') as f: -+ for l in f: -+ if 'path' in l: -+ p = l.split('=')[-1].strip() -+ if not os.path.exists(p): -+ raise ValueError('Submodule %s missing' % p) -+ -+ -+ proc = subprocess.Popen(['git', 'submodule', 'status'], -+ stdout=subprocess.PIPE) -+ status, _ = proc.communicate() -+ status = status.decode("ascii", "replace") -+ for line in status.splitlines(): -+ if line.startswith('-') or line.startswith('+'): -+ raise ValueError('Submodule not clean: %s' % line) -+ -+ -+class concat_license_files(): -+ """Merge LICENSE.txt and LICENSES_bundled.txt for sdist creation -+ -+ Done this way to keep LICENSE.txt in repo as exact BSD 3-clause (see -+ NumPy gh-13447). This makes GitHub state correctly how SciPy is licensed. -+ """ -+ def __init__(self): -+ self.f1 = 'LICENSE.txt' -+ self.f2 = 'LICENSES_bundled.txt' -+ -+ def __enter__(self): -+ """Concatenate files and remove LICENSES_bundled.txt""" -+ with open(self.f1, 'r') as f1: -+ self.bsd_text = f1.read() -+ -+ with open(self.f1, 'a') as f1: -+ with open(self.f2, 'r') as f2: -+ self.bundled_text = f2.read() -+ f1.write('\n\n') -+ f1.write(self.bundled_text) -+ -+ def __exit__(self, exception_type, exception_value, traceback): -+ """Restore content of both files""" -+ with open(self.f1, 'w') as f: -+ f.write(self.bsd_text) -+ -+ -+from distutils.command.sdist import sdist -+class sdist_checked(sdist): -+ """ check submodules on sdist to prevent incomplete tarballs """ -+ def run(self): -+ check_submodules() -+ with concat_license_files(): -+ sdist.run(self) -+ -+ -+def get_build_ext_override(): -+ """ -+ Custom build_ext command to tweak extension building. -+ """ -+ from numpy.distutils.command.build_ext import build_ext as npy_build_ext -+ if int(os.environ.get('SCIPY_USE_PYTHRAN', 1)): -+ try: -+ import pythran -+ from pythran.dist import PythranBuildExt -+ except ImportError: -+ BaseBuildExt = npy_build_ext -+ else: -+ BaseBuildExt = PythranBuildExt[npy_build_ext] -+ _pep440 = importlib.import_module('scipy._lib._pep440') -+ if _pep440.parse(pythran.__version__) < _pep440.Version('0.11.0'): -+ raise RuntimeError("The installed `pythran` is too old, >= " -+ "0.11.0 is needed, {} detected. Please " -+ "upgrade Pythran, or use `export " -+ "SCIPY_USE_PYTHRAN=0`.".format( -+ pythran.__version__)) -+ else: -+ BaseBuildExt = npy_build_ext -+ -+ class build_ext(BaseBuildExt): -+ def finalize_options(self): -+ super().finalize_options() -+ -+ # Disable distutils parallel build, due to race conditions -+ # in numpy.distutils (Numpy issue gh-15957) -+ if self.parallel: -+ print("NOTE: -j build option not supported. Set NPY_NUM_BUILD_JOBS=4 " -+ "for parallel build.") -+ self.parallel = None -+ -+ def build_extension(self, ext): -+ # When compiling with GNU compilers, use a version script to -+ # hide symbols during linking. -+ if self.__is_using_gnu_linker(ext): -+ export_symbols = self.get_export_symbols(ext) -+ text = '{global: %s; local: *; };' % (';'.join(export_symbols),) -+ -+ script_fn = os.path.join(self.build_temp, 'link-version-{}.map'.format(ext.name)) -+ with open(script_fn, 'w') as f: -+ f.write(text) -+ # line below fixes gh-8680 -+ ext.extra_link_args = [arg for arg in ext.extra_link_args if not "version-script" in arg] -+ ext.extra_link_args.append('-Wl,--version-script=' + script_fn) -+ -+ # Allow late configuration -+ hooks = getattr(ext, '_pre_build_hook', ()) -+ _run_pre_build_hooks(hooks, (self, ext)) -+ -+ super().build_extension(ext) -+ -+ def __is_using_gnu_linker(self, ext): -+ if not sys.platform.startswith('linux'): -+ return False -+ -+ # Fortran compilation with gfortran uses it also for -+ # linking. For the C compiler, we detect gcc in a similar -+ # way as distutils does it in -+ # UnixCCompiler.runtime_library_dir_option -+ if ext.language == 'f90': -+ is_gcc = (self._f90_compiler.compiler_type in ('gnu', 'gnu95')) -+ elif ext.language == 'f77': -+ is_gcc = (self._f77_compiler.compiler_type in ('gnu', 'gnu95')) -+ else: -+ is_gcc = False -+ if self.compiler.compiler_type == 'unix': -+ cc = sysconfig.get_config_var("CC") -+ if not cc: -+ cc = "" -+ compiler_name = os.path.basename(cc.split(" ")[0]) -+ is_gcc = "gcc" in compiler_name or "g++" in compiler_name -+ return is_gcc and sysconfig.get_config_var('GNULD') == 'yes' -+ -+ return build_ext -+ -+ -+def get_build_clib_override(): -+ """ -+ Custom build_clib command to tweak library building. -+ """ -+ from numpy.distutils.command.build_clib import build_clib as old_build_clib -+ -+ class build_clib(old_build_clib): -+ def finalize_options(self): -+ super().finalize_options() -+ -+ # Disable parallelization (see build_ext above) -+ self.parallel = None -+ -+ def build_a_library(self, build_info, lib_name, libraries): -+ # Allow late configuration -+ hooks = build_info.get('_pre_build_hook', ()) -+ _run_pre_build_hooks(hooks, (self, build_info)) -+ old_build_clib.build_a_library(self, build_info, lib_name, libraries) -+ -+ return build_clib -+ -+ -+def _run_pre_build_hooks(hooks, args): -+ """Call a sequence of pre-build hooks, if any""" -+ if hooks is None: -+ hooks = () -+ elif not hasattr(hooks, '__iter__'): -+ hooks = (hooks,) -+ for hook in hooks: -+ hook(*args) -+ -+ -+def generate_cython(): -+ cwd = os.path.abspath(os.path.dirname(__file__)) -+ print("Cythonizing sources") -+ p = subprocess.call([sys.executable, -+ os.path.join(cwd, 'tools', 'cythonize.py'), -+ 'scipy'], -+ cwd=cwd) -+ if p != 0: -+ # Could be due to a too old pip version and build isolation, check that -+ try: -+ # Note, pip may not be installed or not have been used -+ import pip -+ except (ImportError, ModuleNotFoundError): -+ raise RuntimeError("Running cythonize failed!") -+ else: -+ _pep440 = importlib.import_module('scipy._lib._pep440') -+ if _pep440.parse(pip.__version__) < _pep440.Version('18.0.0'): -+ raise RuntimeError("Cython not found or too old. Possibly due " -+ "to `pip` being too old, found version {}, " -+ "needed is >= 18.0.0.".format( -+ pip.__version__)) -+ else: -+ raise RuntimeError("Running cythonize failed!") -+ -+ -+def parse_setuppy_commands(): -+ """Check the commands and respond appropriately. Disable broken commands. -+ -+ Return a boolean value for whether or not to run the build or not (avoid -+ parsing Cython and template files if False). -+ """ -+ args = sys.argv[1:] -+ -+ if not args: -+ # User forgot to give an argument probably, let setuptools handle that. -+ return True -+ -+ info_commands = ['--help-commands', '--name', '--version', '-V', -+ '--fullname', '--author', '--author-email', -+ '--maintainer', '--maintainer-email', '--contact', -+ '--contact-email', '--url', '--license', '--description', -+ '--long-description', '--platforms', '--classifiers', -+ '--keywords', '--provides', '--requires', '--obsoletes'] -+ -+ for command in info_commands: -+ if command in args: -+ return False -+ -+ # Note that 'alias', 'saveopts' and 'setopt' commands also seem to work -+ # fine as they are, but are usually used together with one of the commands -+ # below and not standalone. Hence they're not added to good_commands. -+ good_commands = ('develop', 'sdist', 'build', 'build_ext', 'build_py', -+ 'build_clib', 'build_scripts', 'bdist_wheel', 'bdist_rpm', -+ 'bdist_wininst', 'bdist_msi', 'bdist_mpkg') -+ -+ for command in good_commands: -+ if command in args: -+ return True -+ -+ # The following commands are supported, but we need to show more -+ # useful messages to the user -+ if 'install' in args: -+ print(textwrap.dedent(""" -+ Note: for reliable uninstall behaviour and dependency installation -+ and uninstallation, please use pip instead of using -+ `setup.py install`: -+ -+ - `pip install .` (from a git repo or downloaded source -+ release) -+ - `pip install scipy` (last SciPy release on PyPI) -+ -+ """)) -+ return True -+ -+ if '--help' in args or '-h' in sys.argv[1]: -+ print(textwrap.dedent(""" -+ SciPy-specific help -+ ------------------- -+ -+ To install SciPy from here with reliable uninstall, we recommend -+ that you use `pip install .`. To install the latest SciPy release -+ from PyPI, use `pip install scipy`. -+ -+ For help with build/installation issues, please ask on the -+ scipy-user mailing list. If you are sure that you have run -+ into a bug, please report it at https://github.com/scipy/scipy/issues. -+ -+ Setuptools commands help -+ ------------------------ -+ """)) -+ return False -+ -+ -+ # The following commands aren't supported. They can only be executed when -+ # the user explicitly adds a --force command-line argument. -+ bad_commands = dict( -+ test=""" -+ `setup.py test` is not supported. Use one of the following -+ instead: -+ -+ - `python runtests.py` (to build and test) -+ - `python runtests.py --no-build` (to test installed scipy) -+ - `>>> scipy.test()` (run tests for installed scipy -+ from within an interpreter) -+ """, -+ upload=""" -+ `setup.py upload` is not supported, because it's insecure. -+ Instead, build what you want to upload and upload those files -+ with `twine upload -s ` instead. -+ """, -+ upload_docs="`setup.py upload_docs` is not supported", -+ easy_install="`setup.py easy_install` is not supported", -+ clean=""" -+ `setup.py clean` is not supported, use one of the following instead: -+ -+ - `git clean -xdf` (cleans all files) -+ - `git clean -Xdf` (cleans all versioned files, doesn't touch -+ files that aren't checked into the git repo) -+ """, -+ check="`setup.py check` is not supported", -+ register="`setup.py register` is not supported", -+ bdist_dumb="`setup.py bdist_dumb` is not supported", -+ bdist="`setup.py bdist` is not supported", -+ flake8="`setup.py flake8` is not supported, use flake8 standalone", -+ build_sphinx="`setup.py build_sphinx` is not supported, see doc/README.md", -+ ) -+ bad_commands['nosetests'] = bad_commands['test'] -+ for command in ('upload_docs', 'easy_install', 'bdist', 'bdist_dumb', -+ 'register', 'check', 'install_data', 'install_headers', -+ 'install_lib', 'install_scripts', ): -+ bad_commands[command] = "`setup.py %s` is not supported" % command -+ -+ for command in bad_commands.keys(): -+ if command in args: -+ print(textwrap.dedent(bad_commands[command]) + -+ "\nAdd `--force` to your command to use it anyway if you " -+ "must (unsupported).\n") -+ sys.exit(1) -+ -+ # Commands that do more than print info, but also don't need Cython and -+ # template parsing. -+ other_commands = ['egg_info', 'install_egg_info', 'rotate'] -+ for command in other_commands: -+ if command in args: -+ return False -+ -+ # If we got here, we didn't detect what setup.py command was given -+ warnings.warn("Unrecognized setuptools command ('{}'), proceeding with " -+ "generating Cython sources and expanding templates".format( -+ ' '.join(sys.argv[1:]))) -+ return True -+ -+def check_setuppy_command(): -+ run_build = parse_setuppy_commands() -+ if run_build: -+ try: -+ pkgname = 'numpy' -+ import numpy -+ pkgname = 'pybind11' -+ import pybind11 -+ except ImportError as exc: # We do not have our build deps installed -+ print(textwrap.dedent( -+ """Error: '%s' must be installed before running the build. -+ """ -+ % (pkgname,))) -+ sys.exit(1) -+ -+ return run_build -+ -+def configuration(parent_package='', top_path=None): -+ from numpy.distutils.system_info import get_info, NotFoundError -+ from numpy.distutils.misc_util import Configuration -+ -+ lapack_opt = get_info('lapack_opt') -+ -+ if not lapack_opt: -+ if sys.platform == "darwin": -+ msg = ('No BLAS/LAPACK libraries found. ' -+ 'Note: Accelerate is no longer supported.') -+ else: -+ msg = 'No BLAS/LAPACK libraries found.' -+ msg += ("\n" -+ "To build Scipy from sources, BLAS & LAPACK libraries " -+ "need to be installed.\n" -+ "See site.cfg.example in the Scipy source directory and\n" -+ "https://docs.scipy.org/doc/scipy/dev/contributor/building.html " -+ "for details.") -+ raise NotFoundError(msg) -+ -+ config = Configuration(None, parent_package, top_path) -+ config.set_options(ignore_setup_xxx_py=True, -+ assume_default_configuration=True, -+ delegate_options_to_subpackages=True, -+ quiet=True) -+ -+ config.add_subpackage('scipy') -+ config.add_data_files(('scipy', '*.txt')) -+ -+ config.get_version('scipy/version.py') -+ -+ return config -+ -+ -+def setup_package(): -+ # In maintenance branch, change np_maxversion to N+3 if numpy is at N -+ # Update here, in pyproject.toml, and in scipy/__init__.py -+ # Rationale: SciPy builds without deprecation warnings with N; deprecations -+ # in N+1 will turn into errors in N+3 -+ # For Python versions, if releases is (e.g.) <=3.9.x, set bound to 3.10 -+ np_minversion = '1.21.6' -+ np_maxversion = '1.28.0' -+ python_minversion = '3.9' -+ python_maxversion = '3.13' -+ if IS_RELEASE_BRANCH: -+ req_np = 'numpy>={},<{}'.format(np_minversion, np_maxversion) -+ req_py = '>={},<{}'.format(python_minversion, python_maxversion) -+ else: -+ req_np = 'numpy>={}'.format(np_minversion) -+ req_py = '>={}'.format(python_minversion) -+ -+ # Rewrite the version file every time -+ write_version_py('.') -+ -+ cmdclass = {'sdist': sdist_checked} -+ -+ metadata = dict( -+ name='scipy', -+ maintainer="SciPy Developers", -+ maintainer_email="scipy-dev@python.org", -+ description=DOCLINES[0], -+ long_description="\n".join(DOCLINES[2:]), -+ url="https://www.scipy.org", -+ download_url="https://github.com/scipy/scipy/releases", -+ project_urls={ -+ "Bug Tracker": "https://github.com/scipy/scipy/issues", -+ "Documentation": "https://docs.scipy.org/doc/scipy/reference/", -+ "Source Code": "https://github.com/scipy/scipy", -+ }, -+ license='BSD', -+ cmdclass=cmdclass, -+ classifiers=[_f for _f in CLASSIFIERS.split('\n') if _f], -+ platforms=["Windows", "Linux", "Solaris", "Mac OS-X", "Unix"], -+ install_requires=[req_np], -+ python_requires=req_py, -+ zip_safe=False, -+ ) -+ -+ if "--force" in sys.argv: -+ run_build = True -+ sys.argv.remove('--force') -+ else: -+ # Raise errors for unsupported commands, improve help output, etc. -+ run_build = check_setuppy_command() -+ -+ # Disable OSX Accelerate, it has too old LAPACK -+ os.environ['ACCELERATE'] = 'None' -+ -+ # This import is here because it needs to be done before importing setup() -+ # from numpy.distutils, but after the MANIFEST removing and sdist import -+ # higher up in this file. -+ from setuptools import setup -+ -+ if run_build: -+ from numpy.distutils.core import setup -+ -+ # Customize extension building -+ cmdclass['build_ext'] = get_build_ext_override() -+ cmdclass['build_clib'] = get_build_clib_override() -+ -+ if not 'sdist' in sys.argv: -+ # Generate Cython sources, unless we're creating an sdist -+ # Cython is a build dependency, and shipping generated .c files -+ # can cause problems (see gh-14199) -+ generate_cython() -+ -+ metadata['configuration'] = configuration -+ else: -+ # Don't import numpy here - non-build actions are required to succeed -+ # without NumPy for example when pip is used to install Scipy when -+ # NumPy is not yet present in the system. -+ -+ # Version number is added to metadata inside configuration() if build -+ # is run. -+ metadata['version'] = get_version_info('.')[0] -+ -+ setup(**metadata) -+ -+ -+if __name__ == '__main__': -+ setup_package() diff --git a/pythonforandroid/recipes/scipy/wrapper.py b/pythonforandroid/recipes/scipy/wrapper.py new file mode 100644 index 0000000000..ca0e60d22e --- /dev/null +++ b/pythonforandroid/recipes/scipy/wrapper.py @@ -0,0 +1,62 @@ +#!/usr/bin/python3 + +# Taken from https://github.com/termux/termux-packages/blob/master/packages/python-scipy/wrapper.py.in + +import os +import subprocess +import sys +import typing + +""" +This wrapper is used to ignore or replace some unsupported flags for flang-new. + +It will operate as follows: + +1. Ignore `-Minform=inform` and `-fdiagnostics-color`. + They are added by meson automatically, but are not supported by flang-new yet. +2. Remove `-lflang` and `-lpgmath`. + It exists in classic-flang but doesn't exist in flang-new. +3. Replace `-Oz` to `-O2`. + `-Oz` is not supported by flang-new. +4. Replace `-module` to `-J`. + See https://github.com/llvm/llvm-project/issues/66969 +5. Ignore `-MD`, `-MQ file` and `-MF file`. + They generates files used by GNU make but we're using ninja. +6. Ignore `-fvisibility=hidden`. + It is not supported by flang-new, and ignoring it will not break the functionality, + as scipy also uses version script for shared libraries. +""" + +COMPLIER_PATH = "@COMPILER@" + + +def main(argv: typing.List[str]): + cwd = os.getcwd() + argv_new = [] + i = 0 + while i < len(argv): + arg = argv[i] + if arg in [ + "-Minform=inform", + "-lflang", + "-lpgmath", + "-MD", + "-fvisibility=hidden", + ] or arg.startswith("-fdiagnostics-color"): + pass + elif arg == "-Oz": + argv_new.append("-O2") + elif arg == "-module": + argv_new.append("-J") + elif arg in ["-MQ", "-MF"]: + i += 1 + else: + argv_new.append(arg) + i += 1 + + args = [COMPLIER_PATH] + argv_new + subprocess.check_call(args, env=os.environ, cwd=cwd, text=True) + + +if __name__ == "__main__": + main(sys.argv[1:]) From 6a7755e08a74ac21df6920be71df7b1092ae2d30 Mon Sep 17 00:00:00 2001 From: Ansh Dadwal Date: Thu, 27 Mar 2025 20:30:14 +0530 Subject: [PATCH 2/3] Bump minimal and recommended Android NDK version to 27c --- ci/makefiles/android.mk | 2 +- doc/source/quickstart.rst | 2 +- pythonforandroid/recipe.py | 2 +- pythonforandroid/recommendations.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ci/makefiles/android.mk b/ci/makefiles/android.mk index 2041a6ce76..b9aecffc14 100644 --- a/ci/makefiles/android.mk +++ b/ci/makefiles/android.mk @@ -1,7 +1,7 @@ # Downloads and installs the Android SDK depending on supplied platform: darwin or linux # Those android NDK/SDK variables can be override when running the file -ANDROID_NDK_VERSION ?= 25b +ANDROID_NDK_VERSION ?= 27c ANDROID_NDK_VERSION_LEGACY ?= 21e ANDROID_SDK_TOOLS_VERSION ?= 6514223 ANDROID_SDK_BUILD_TOOLS_VERSION ?= 29.0.3 diff --git a/doc/source/quickstart.rst b/doc/source/quickstart.rst index 61c33d6f16..449f1314c0 100644 --- a/doc/source/quickstart.rst +++ b/doc/source/quickstart.rst @@ -119,7 +119,7 @@ named ``tools``, and you will need to run extra commands to install the SDK packages needed. For Android NDK, note that modern releases will only work on a 64-bit -operating system. **The minimal, and recommended, NDK version to use is r25b:** +operating system. **The minimal, and recommended, NDK version to use is r27c:** - `Go to ndk downloads page `_ - Windows users should create a virtual machine with an GNU Linux os diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 42e0902229..e277bcb817 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -109,7 +109,7 @@ class Recipe(metaclass=RecipeMeta): recipe if they are built at all, but whose presence is not essential.''' patches = [] - '''Alist of patches to apply to the source. Values can be either a string + '''A list of patches to apply to the source. Values can be either a string referring to the patch file relative to the recipe dir, or a tuple of the string patch file and a callable, which will receive the kwargs `arch` and `recipe`, which should return True if the patch should be applied.''' diff --git a/pythonforandroid/recommendations.py b/pythonforandroid/recommendations.py index 269a57fcf8..5584815d57 100644 --- a/pythonforandroid/recommendations.py +++ b/pythonforandroid/recommendations.py @@ -13,7 +13,7 @@ MAX_NDK_VERSION = 25 # DO NOT CHANGE LINE FORMAT: buildozer parses the existence of a RECOMMENDED_NDK_VERSION -RECOMMENDED_NDK_VERSION = "25b" +RECOMMENDED_NDK_VERSION = "27c" NDK_DOWNLOAD_URL = "https://developer.android.com/ndk/downloads/" From b972e5afc548cc47e79a405ea57911f70733148f Mon Sep 17 00:00:00 2001 From: Ansh Dadwal Date: Fri, 28 Mar 2025 02:42:16 +0530 Subject: [PATCH 3/3] `sdl`: bump to 2.30.11 --- pythonforandroid/recipes/sdl2/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonforandroid/recipes/sdl2/__init__.py b/pythonforandroid/recipes/sdl2/__init__.py index 8d5fbc2dc2..ceabeef1ed 100644 --- a/pythonforandroid/recipes/sdl2/__init__.py +++ b/pythonforandroid/recipes/sdl2/__init__.py @@ -6,9 +6,9 @@ class LibSDL2Recipe(BootstrapNDKRecipe): - version = "2.28.5" + version = "2.30.11" url = "https://github.com/libsdl-org/SDL/releases/download/release-{version}/SDL2-{version}.tar.gz" - md5sum = 'a344eb827a03045c9b399e99af4af13d' + md5sum = 'bea190b480f6df249db29eb3bacfe41e' dir_name = 'SDL'