Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

scipy: update to v1.15.2 #3136

Draft
wants to merge 3 commits into
base: develop
Choose a base branch
from
Draft
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
2 changes: 1 addition & 1 deletion ci/makefiles/android.mk
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion doc/source/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://developer.android.com/ndk/downloads/>`_
- Windows users should create a virtual machine with an GNU Linux os
Expand Down
73 changes: 55 additions & 18 deletions pythonforandroid/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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'
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
188 changes: 188 additions & 0 deletions pythonforandroid/recipes/fortran/__init__.py
Original file line number Diff line number Diff line change
@@ -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()
50 changes: 50 additions & 0 deletions pythonforandroid/recipes/libopenblas/__init__.py
Original file line number Diff line number Diff line change
@@ -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()
Loading
Loading