diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 4b4d47cace..597293a9d7 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -109,11 +109,17 @@ jobs: target: testapps-with-numpy - name: webview target: testapps-webview + include: + - runs_on: macos-latest + ndk_version: '23b' + - runs_on: apple-silicon-m1 + ndk_version: '24' env: ANDROID_HOME: ${HOME}/.android ANDROID_SDK_ROOT: ${HOME}/.android/android-sdk ANDROID_SDK_HOME: ${HOME}/.android/android-sdk ANDROID_NDK_HOME: ${HOME}/.android/android-ndk + ANDROID_NDK_VERSION: ${{ matrix.ndk_version }} steps: - name: Checkout python-for-android uses: actions/checkout@v2 @@ -127,7 +133,7 @@ jobs: source ci/osx_ci.sh arm64_set_path_and_python_version 3.9.7 python3 pythonforandroid/prerequisites.py - - name: Install dependencies + - name: Install dependencies (Legacy) run: | source ci/osx_ci.sh arm64_set_path_and_python_version 3.9.7 @@ -241,11 +247,17 @@ jobs: target: testapps-with-numpy-aab - name: webview target: testapps-webview-aab + include: + - runs_on: macos-latest + ndk_version: '23b' + - runs_on: apple-silicon-m1 + ndk_version: '24' env: ANDROID_HOME: ${HOME}/.android ANDROID_SDK_ROOT: ${HOME}/.android/android-sdk ANDROID_SDK_HOME: ${HOME}/.android/android-sdk ANDROID_NDK_HOME: ${HOME}/.android/android-ndk + ANDROID_NDK_VERSION: ${{ matrix.ndk_version }} steps: - name: Checkout python-for-android uses: actions/checkout@v2 @@ -259,7 +271,7 @@ jobs: source ci/osx_ci.sh arm64_set_path_and_python_version 3.9.7 python3 pythonforandroid/prerequisites.py - - name: Install dependencies + - name: Install dependencies (Legacy) run: | source ci/osx_ci.sh arm64_set_path_and_python_version 3.9.7 @@ -318,12 +330,18 @@ jobs: matrix: android_arch: ["arm64-v8a", "armeabi-v7a", "x86_64", "x86"] runs_on: [macos-latest, apple-silicon-m1] + include: + - runs_on: macos-latest + ndk_version: '23b' + - runs_on: apple-silicon-m1 + ndk_version: '24' env: ANDROID_HOME: ${HOME}/.android ANDROID_SDK_ROOT: ${HOME}/.android/android-sdk ANDROID_SDK_HOME: ${HOME}/.android/android-sdk ANDROID_NDK_HOME: ${HOME}/.android/android-ndk REBUILD_UPDATED_RECIPES_EXTRA_ARGS: --arch=${{ matrix.android_arch }} + ANDROID_NDK_VERSION: ${{ matrix.ndk_version }} steps: - name: Checkout python-for-android uses: actions/checkout@v2 @@ -339,7 +357,7 @@ jobs: source ci/osx_ci.sh arm64_set_path_and_python_version 3.9.7 python3 pythonforandroid/prerequisites.py - - name: Install dependencies + - name: Install dependencies (Legacy) run: | source ci/osx_ci.sh arm64_set_path_and_python_version 3.9.7 diff --git a/pythonforandroid/androidndk.py b/pythonforandroid/androidndk.py new file mode 100644 index 0000000000..83cb355740 --- /dev/null +++ b/pythonforandroid/androidndk.py @@ -0,0 +1,83 @@ +import sys +import os + + +class AndroidNDK: + """ + This class is used to get the current NDK information. + """ + + ndk_dir = "" + + def __init__(self, ndk_dir): + self.ndk_dir = ndk_dir + + @property + def host_tag(self): + """ + Returns the host tag for the current system. + Note: The host tag is ``darwin-x86_64`` even on Apple Silicon macs. + """ + return f"{sys.platform}-x86_64" + + @property + def llvm_prebuilt_dir(self): + return os.path.join( + self.ndk_dir, "toolchains", "llvm", "prebuilt", self.host_tag + ) + + @property + def llvm_bin_dir(self): + return os.path.join(self.llvm_prebuilt_dir, "bin") + + @property + def clang(self): + return os.path.join(self.llvm_bin_dir, "clang") + + @property + def clang_cxx(self): + return os.path.join(self.llvm_bin_dir, "clang++") + + @property + def llvm_binutils_prefix(self): + return os.path.join(self.llvm_bin_dir, "llvm-") + + @property + def llvm_ar(self): + return f"{self.llvm_binutils_prefix}ar" + + @property + def llvm_ranlib(self): + return f"{self.llvm_binutils_prefix}ranlib" + + @property + def llvm_objcopy(self): + return f"{self.llvm_binutils_prefix}objcopy" + + @property + def llvm_objdump(self): + return f"{self.llvm_binutils_prefix}objdump" + + @property + def llvm_readelf(self): + return f"{self.llvm_binutils_prefix}readelf" + + @property + def llvm_strip(self): + return f"{self.llvm_binutils_prefix}strip" + + @property + def sysroot(self): + return os.path.join(self.llvm_prebuilt_dir, "sysroot") + + @property + def sysroot_include_dir(self): + return os.path.join(self.sysroot, "usr", "include") + + @property + def sysroot_lib_dir(self): + return os.path.join(self.sysroot, "usr", "lib") + + @property + def libcxx_include_dir(self): + return os.path.join(self.sysroot_include_dir, "c++", "v1") diff --git a/pythonforandroid/archs.py b/pythonforandroid/archs.py index 81c018d43e..d5ef40ad4a 100644 --- a/pythonforandroid/archs.py +++ b/pythonforandroid/archs.py @@ -25,7 +25,7 @@ class Arch: common_cppflags = [ '-DANDROID', - '-I{ctx.ndk_sysroot}/usr/include', + '-I{ctx.ndk.sysroot_include_dir}', '-I{python_includes}', ] @@ -54,7 +54,11 @@ def __str__(self): @property def ndk_lib_dir(self): - return join(self.ctx.ndk_sysroot, 'usr', 'lib', self.command_prefix, str(self.ctx.ndk_api)) + return join(self.ctx.ndk.sysroot_lib_dir, self.command_prefix) + + @property + def ndk_lib_dir_versioned(self): + return join(self.ndk_lib_dir, str(self.ctx.ndk_api)) @property def include_dirs(self): @@ -74,18 +78,6 @@ def target(self): triplet=self.command_prefix, ndk_api=self.ctx.ndk_api ) - @property - def clang_path(self): - """Full path of the clang compiler""" - return join( - self.ctx.ndk_dir, - 'toolchains', - 'llvm', - 'prebuilt', - build_platform, - 'bin', - ) - @property def clang_exe(self): """Full path of the clang compiler depending on the android's ndk @@ -112,7 +104,7 @@ def get_clang_exe(self, with_target=False, plus_plus=False): ) if plus_plus: compiler += '++' - return join(self.clang_path, compiler) + return join(self.ctx.ndk.llvm_bin_dir, compiler) def get_env(self, with_flags_in_cc=True): env = {} @@ -195,11 +187,11 @@ def get_env(self, with_flags_in_cc=True): ccache=ccache) # Android's LLVM binutils - env['AR'] = f'{self.clang_path}/llvm-ar' - env['RANLIB'] = f'{self.clang_path}/llvm-ranlib' - env['STRIP'] = f'{self.clang_path}/llvm-strip --strip-unneeded' - env['READELF'] = f'{self.clang_path}/llvm-readelf' - env['OBJCOPY'] = f'{self.clang_path}/llvm-objcopy' + env['AR'] = self.ctx.ndk.llvm_ar + env['RANLIB'] = self.ctx.ndk.llvm_ranlib + env['STRIP'] = f'{self.ctx.ndk.llvm_strip} --strip-unneeded' + env['READELF'] = self.ctx.ndk.llvm_readelf + env['OBJCOPY'] = self.ctx.ndk.llvm_objcopy env['MAKE'] = 'make -j{}'.format(str(cpu_count())) diff --git a/pythonforandroid/build.py b/pythonforandroid/build.py index 2edc96230c..332fcd21f3 100644 --- a/pythonforandroid/build.py +++ b/pythonforandroid/build.py @@ -5,7 +5,6 @@ import copy import os import glob -import sys import re import sh import shutil @@ -23,20 +22,7 @@ from pythonforandroid.recommendations import ( check_ndk_version, check_target_api, check_ndk_api, RECOMMENDED_NDK_API, RECOMMENDED_TARGET_API) -from pythonforandroid.util import build_platform - - -def get_ndk_standalone(ndk_dir): - return join(ndk_dir, 'toolchains', 'llvm', 'prebuilt', build_platform) - - -def get_ndk_sysroot(ndk_dir): - sysroot = join(get_ndk_standalone(ndk_dir), 'sysroot') - sysroot_exists = True - if not exists(sysroot): - warning("sysroot doesn't exist: {}".format(sysroot)) - sysroot_exists = False - return sysroot, sysroot_exists +from pythonforandroid.androidndk import AndroidNDK def get_targets(sdk_dir): @@ -101,9 +87,7 @@ class Context: ccache = None # whether to use ccache - ndk_standalone = None - ndk_sysroot = None - ndk_include_dir = None # usr/include + ndk = None bootstrap = None bootstrap_build_dir = None @@ -330,7 +314,6 @@ def prepare_build_environment(self, if ndk_dir is None: raise BuildInterruptingException('Android NDK dir was not specified') self.ndk_dir = realpath(ndk_dir) - check_ndk_version(ndk_dir) ndk_api = None @@ -350,6 +333,8 @@ def prepare_build_environment(self, check_ndk_api(ndk_api, self.android_api) + self.ndk = AndroidNDK(self.ndk_dir) + # path to some tools self.ccache = sh.which("ccache") if not self.ccache: @@ -364,17 +349,9 @@ def prepare_build_environment(self, ' a python 3 target (which is the default)' ' then THINGS WILL BREAK.') - py_platform = sys.platform - if py_platform in ['linux2', 'linux3']: - py_platform = 'linux' - - self.ndk_standalone = get_ndk_standalone(self.ndk_dir) - self.ndk_sysroot, ndk_sysroot_exists = get_ndk_sysroot(self.ndk_dir) - self.ndk_include_dir = join(self.ndk_sysroot, 'usr', 'include') - self.env["PATH"] = ":".join( [ - f"{self.ndk_dir}/toolchains/llvm/prebuilt/{py_platform}-x86_64/bin", + self.ndk.llvm_bin_dir, self.ndk_dir, f"{self.sdk_dir}/tools", environ.get("PATH"), diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 71b581b0e1..c2639a0095 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -135,24 +135,9 @@ class Recipe(with_metaclass(RecipeMeta)): starting from NDK r18 the `gnustl_shared` lib has been deprecated. ''' - stl_lib_source = '{ctx.ndk_dir}/sources/cxx-stl/llvm-libc++' - ''' - The source directory of the selected stl lib, defined in property - `stl_lib_name` - ''' - - @property - def stl_include_dir(self): - return join(self.stl_lib_source.format(ctx=self.ctx), 'include') - - def get_stl_lib_dir(self, arch): - return join( - self.stl_lib_source.format(ctx=self.ctx), 'libs', arch.arch - ) - def get_stl_library(self, arch): return join( - self.get_stl_lib_dir(arch), + arch.ndk_lib_dir, 'lib{name}.so'.format(name=self.stl_lib_name), ) @@ -510,14 +495,14 @@ def get_recipe_env(self, arch=None, with_flags_in_cc=True): if self.need_stl_shared: env['CPPFLAGS'] = env.get('CPPFLAGS', '') - env['CPPFLAGS'] += ' -I{}'.format(self.stl_include_dir) + env['CPPFLAGS'] += ' -I{}'.format(self.ctx.ndk.libcxx_include_dir) env['CXXFLAGS'] = env['CFLAGS'] + ' -frtti -fexceptions' if with_flags_in_cc: env['CXX'] += ' -frtti -fexceptions' - env['LDFLAGS'] += ' -L{}'.format(self.get_stl_lib_dir(arch)) + env['LDFLAGS'] += ' -L{}'.format(arch.ndk_lib_dir) env['LIBS'] = env.get('LIBS', '') + " -l{}".format( self.stl_lib_name ) diff --git a/pythonforandroid/recipes/Pillow/__init__.py b/pythonforandroid/recipes/Pillow/__init__.py index 82f268e97f..f8f6929db5 100644 --- a/pythonforandroid/recipes/Pillow/__init__.py +++ b/pythonforandroid/recipes/Pillow/__init__.py @@ -35,9 +35,6 @@ class PillowRecipe(CompiledComponentsPythonRecipe): def get_recipe_env(self, arch=None, with_flags_in_cc=True): env = super().get_recipe_env(arch, with_flags_in_cc) - ndk_lib_dir = arch.ndk_lib_dir - ndk_include_dir = self.ctx.ndk_include_dir - png = self.get_recipe('png', self.ctx) png_lib_dir = join(png.get_build_dir(arch.arch), '.libs') png_inc_dir = png.get_build_dir(arch) @@ -71,7 +68,7 @@ def get_recipe_env(self, arch=None, with_flags_in_cc=True): cflags += f' -I{jpeg_inc_dir}' if build_with_webp_support: cflags += f' -I{join(webp_install, "include")}' - cflags += f' -I{ndk_include_dir}' + cflags += f' -I{self.ctx.ndk.sysroot_include_dir}' # Link the basic Pillow libraries...no need to add webp's libraries # since it seems that the linkage is properly made without it :) @@ -84,7 +81,7 @@ def get_recipe_env(self, arch=None, with_flags_in_cc=True): env['LDFLAGS'] += f' -L{jpeg_lib_dir}' if build_with_webp_support: env['LDFLAGS'] += f' -L{join(webp_install, "lib")}' - env['LDFLAGS'] += f' -L{ndk_lib_dir}' + env['LDFLAGS'] += f' -L{arch.ndk_lib_dir_versioned}' if cflags not in env['CFLAGS']: env['CFLAGS'] += cflags + " -lm" return env diff --git a/pythonforandroid/recipes/boost/__init__.py b/pythonforandroid/recipes/boost/__init__.py index cebbd5ca83..aa386c9bdf 100644 --- a/pythonforandroid/recipes/boost/__init__.py +++ b/pythonforandroid/recipes/boost/__init__.py @@ -1,4 +1,4 @@ -from pythonforandroid.util import current_directory, build_platform +from pythonforandroid.util import current_directory from pythonforandroid.recipe import Recipe from pythonforandroid.logger import shprint from os.path import join, exists @@ -97,12 +97,7 @@ def get_recipe_env(self, arch): env['ARCH'] = arch.arch.replace('-', '') env['TARGET_TRIPLET'] = arch.target env['CROSSHOST'] = arch.command_prefix - env['CROSSHOME'] = join( - self.ctx.ndk_dir, - 'toolchains/llvm/prebuilt/{build_platform}'.format( - build_platform=build_platform - ), - ) + env['CROSSHOME'] = self.ctx.ndk.llvm_prebuilt_dir return env diff --git a/pythonforandroid/recipes/cffi/__init__.py b/pythonforandroid/recipes/cffi/__init__.py index 0b12c1af5b..a198a3db0d 100644 --- a/pythonforandroid/recipes/cffi/__init__.py +++ b/pythonforandroid/recipes/cffi/__init__.py @@ -35,7 +35,7 @@ def get_recipe_env(self, arch=None): self.ctx.get_libs_dir(arch.arch)) env['LDFLAGS'] += ' -L{}'.format(os.path.join(self.ctx.bootstrap.build_dir, 'libs', arch.arch)) # required for libc and libdl - env['LDFLAGS'] += ' -L{}'.format(arch.ndk_lib_dir) + env['LDFLAGS'] += ' -L{}'.format(arch.ndk_lib_dir_versioned) env['PYTHONPATH'] = ':'.join([ self.ctx.get_site_packages_dir(arch), env['BUILDLIB_PATH'], diff --git a/pythonforandroid/recipes/evdev/__init__.py b/pythonforandroid/recipes/evdev/__init__.py index d759f2b81a..b69169d3c9 100644 --- a/pythonforandroid/recipes/evdev/__init__.py +++ b/pythonforandroid/recipes/evdev/__init__.py @@ -19,7 +19,7 @@ class EvdevRecipe(CompiledComponentsPythonRecipe): def get_recipe_env(self, arch=None): env = super().get_recipe_env(arch) - env['SYSROOT'] = self.ctx.ndk_sysroot + env['SYSROOT'] = self.ctx.ndk.sysroot return env diff --git a/pythonforandroid/recipes/ffmpeg/__init__.py b/pythonforandroid/recipes/ffmpeg/__init__.py index 6f64d26562..d58f1e90aa 100644 --- a/pythonforandroid/recipes/ffmpeg/__init__.py +++ b/pythonforandroid/recipes/ffmpeg/__init__.py @@ -110,14 +110,11 @@ def build_arch(self, arch): ] if 'arm64' in arch.arch: - cross_prefix = 'aarch64-linux-android-' arch_flag = 'aarch64' elif 'x86' in arch.arch: - cross_prefix = 'i686-linux-android-' arch_flag = 'x86' flags += ['--disable-asm'] else: - cross_prefix = 'arm-linux-androideabi-' arch_flag = 'arm' # android: @@ -126,10 +123,8 @@ def build_arch(self, arch): '--enable-cross-compile', '--cross-prefix={}-'.format(arch.target), '--arch={}'.format(arch_flag), - '--strip={}strip'.format(cross_prefix), - '--sysroot={}'.format(join(self.ctx.ndk_dir, 'toolchains', - 'llvm', 'prebuilt', 'linux-x86_64', - 'sysroot')), + '--strip={}'.format(self.ctx.ndk.llvm_strip), + '--sysroot={}'.format(self.ctx.ndk.sysroot), '--enable-neon', '--prefix={}'.format(realpath('.')), ] diff --git a/pythonforandroid/recipes/ffpyplayer/__init__.py b/pythonforandroid/recipes/ffpyplayer/__init__.py index 36728a1758..7aa8b0db2e 100644 --- a/pythonforandroid/recipes/ffpyplayer/__init__.py +++ b/pythonforandroid/recipes/ffpyplayer/__init__.py @@ -22,6 +22,11 @@ def get_recipe_env(self, arch, with_flags_in_cc=True): env["USE_SDL2_MIXER"] = '1' env["SDL2_MIXER_INCLUDE_DIR"] = join(self.ctx.bootstrap.build_dir, 'jni', 'SDL2_mixer') + # NDKPLATFORM and LIBLINK are our switches for detecting Android platform, so can't be empty + # FIXME: We may want to introduce a cleaner approach to this? + env['NDKPLATFORM'] = "NOTNONE" + env['LIBLINK'] = 'NOTNONE' + # ffmpeg recipe enables GPL components only if ffpyplayer_codecs recipe used. # Therefor we need to disable libpostproc if skipped. if 'ffpyplayer_codecs' not in self.ctx.recipe_build_order: diff --git a/pythonforandroid/recipes/freetype/__init__.py b/pythonforandroid/recipes/freetype/__init__.py index 130f6708c8..0b04c95da6 100644 --- a/pythonforandroid/recipes/freetype/__init__.py +++ b/pythonforandroid/recipes/freetype/__init__.py @@ -47,8 +47,8 @@ def get_recipe_env(self, arch=None, with_harfbuzz=False): ) # android's zlib support - zlib_lib_path = arch.ndk_lib_dir - zlib_includes = self.ctx.ndk_include_dir + zlib_lib_path = arch.ndk_lib_dir_versioned + zlib_includes = self.ctx.ndk.sysroot_include_dir def add_flag_if_not_added(flag, env_key): if flag not in env[env_key]: diff --git a/pythonforandroid/recipes/hostpython3/__init__.py b/pythonforandroid/recipes/hostpython3/__init__.py index 5dc3c87200..8cda969f3a 100644 --- a/pythonforandroid/recipes/hostpython3/__init__.py +++ b/pythonforandroid/recipes/hostpython3/__init__.py @@ -35,7 +35,7 @@ class HostPython3Recipe(Recipe): :class:`~pythonforandroid.python.HostPythonRecipe` ''' - version = '3.8.9' + version = '3.9.9' name = 'hostpython3' build_subdir = 'native-build' diff --git a/pythonforandroid/recipes/jpeg/__init__.py b/pythonforandroid/recipes/jpeg/__init__.py index 5e5b447a24..a81b82555c 100644 --- a/pythonforandroid/recipes/jpeg/__init__.py +++ b/pythonforandroid/recipes/jpeg/__init__.py @@ -30,7 +30,6 @@ def build_arch(self, arch): shprint(sh.rm, '-f', 'CMakeCache.txt', 'CMakeFiles/') shprint(sh.cmake, '-G', 'Unix Makefiles', '-DCMAKE_SYSTEM_NAME=Android', - '-DCMAKE_SYSTEM_PROCESSOR={cpu}'.format(cpu='arm'), '-DCMAKE_POSITION_INDEPENDENT_CODE=1', '-DCMAKE_ANDROID_ARCH_ABI={arch}'.format(arch=arch.arch), '-DCMAKE_ANDROID_NDK=' + self.ctx.ndk_dir, diff --git a/pythonforandroid/recipes/kiwisolver/__init__.py b/pythonforandroid/recipes/kiwisolver/__init__.py index 5e20f9ed64..a65c4d7ee3 100644 --- a/pythonforandroid/recipes/kiwisolver/__init__.py +++ b/pythonforandroid/recipes/kiwisolver/__init__.py @@ -19,10 +19,10 @@ def get_recipe_env(self, arch=None, with_flags_in_cc=True): # kiwisolver compile flags does not honor the standard flags: # `CPPFLAGS` and `LDLIBS`, so we put in `CFLAGS` and `LDFLAGS` to # correctly link with the `c++_shared` library - env['CFLAGS'] += f' -I{self.stl_include_dir}' + env['CFLAGS'] += f' -I{self.ctx.ndk.libcxx_include_dir}' env['CFLAGS'] += ' -frtti -fexceptions' - env['LDFLAGS'] += f' -L{self.get_stl_lib_dir(arch)}' + env['LDFLAGS'] += f' -L{arch.ndk_lib_dir}' env['LDFLAGS'] += f' -l{self.stl_lib_name}' return env diff --git a/pythonforandroid/recipes/librt/__init__.py b/pythonforandroid/recipes/librt/__init__.py index 063da54953..6c42490e6c 100644 --- a/pythonforandroid/recipes/librt/__init__.py +++ b/pythonforandroid/recipes/librt/__init__.py @@ -19,7 +19,7 @@ class LibRt(Recipe): finishes''' def build_arch(self, arch): - libc_path = join(arch.ndk_lib_dir, 'libc') + libc_path = join(arch.ndk_lib_dir_versioned, 'libc') # Create a temporary folder to add to link path with a fake librt.so: fake_librt_temp_folder = join( self.get_build_dir(arch.arch), diff --git a/pythonforandroid/recipes/libvpx/__init__.py b/pythonforandroid/recipes/libvpx/__init__.py index 9c2957a2da..f0d72f59d7 100644 --- a/pythonforandroid/recipes/libvpx/__init__.py +++ b/pythonforandroid/recipes/libvpx/__init__.py @@ -8,11 +8,13 @@ TARGETS = { 'armeabi-v7a': 'armv7-android-gcc', 'arm64-v8a': 'arm64-android-gcc', + 'x86': 'x86-android-gcc', + 'x86_64': 'x86_64-android-gcc', } class VPXRecipe(Recipe): - version = '1.9.0' + version = '1.11.0' url = 'https://github.com/webmproject/libvpx/archive/v{version}.tar.gz' patches = [ @@ -22,21 +24,7 @@ class VPXRecipe(Recipe): def get_recipe_env(self, arch=None): env = super().get_recipe_env(arch) - cxx_include_dir = join( - self.ctx.ndk_dir, - 'toolchains', - 'llvm', - 'prebuilt', - 'linux-x86_64', - 'sysroot', - 'usr', - 'include', - 'c++', - 'v1', - ) - env['CXXFLAGS'] += f' -I{cxx_include_dir}' - if 'arm64' not in arch.arch: - env['AS'] = arch.command_prefix + '-as' + env['CXXFLAGS'] += f' -I{self.ctx.ndk.libcxx_include_dir}' return env def build_arch(self, arch): @@ -56,6 +44,7 @@ def build_arch(self, arch): '--disable-docs', '--disable-install-docs', '--disable-realtime-only', + '--enable-external-build', f'--prefix={realpath(".")}', ] configure = sh.Command('./configure') diff --git a/pythonforandroid/recipes/libx264/__init__.py b/pythonforandroid/recipes/libx264/__init__.py index 13ba31f066..63413096fa 100644 --- a/pythonforandroid/recipes/libx264/__init__.py +++ b/pythonforandroid/recipes/libx264/__init__.py @@ -7,25 +7,19 @@ class LibX264Recipe(Recipe): - version = 'x264-snapshot-20171218-2245-stable' # using mirror url since can't use ftp - url = 'http://mirror.yandex.ru/mirrors/ftp.videolan.org/x264/snapshots/{version}.tar.bz2' + version = '5db6aa6cab1b146e07b60cc1736a01f21da01154' # commit of latest known stable version + url = 'https://code.videolan.org/videolan/x264/-/archive/{version}/x264-{version}.zip' built_libraries = {'libx264.a': 'lib'} def build_arch(self, arch): with current_directory(self.get_build_dir(arch.arch)): env = self.get_recipe_env(arch) - if 'arm64' in arch.arch: - cross_prefix = 'aarch64-linux-android-' - else: - cross_prefix = 'arm-linux-androideabi-' configure = sh.Command('./configure') shprint(configure, - '--cross-prefix={}'.format(cross_prefix), - '--host=arm-linux', + f'--host={arch.command_prefix}', '--disable-asm', '--disable-cli', '--enable-pic', - '--disable-shared', '--enable-static', '--prefix={}'.format(realpath('.')), _env=env) diff --git a/pythonforandroid/recipes/libxml2/__init__.py b/pythonforandroid/recipes/libxml2/__init__.py index c8788d9d78..100c528dc3 100644 --- a/pythonforandroid/recipes/libxml2/__init__.py +++ b/pythonforandroid/recipes/libxml2/__init__.py @@ -6,7 +6,7 @@ class Libxml2Recipe(Recipe): - version = '2.9.8' + version = '2.9.12' url = 'http://xmlsoft.org/sources/libxml2-{version}.tar.gz' depends = [] patches = ['add-glob.c.patch'] diff --git a/pythonforandroid/recipes/libxslt/__init__.py b/pythonforandroid/recipes/libxslt/__init__.py index ca65fbb9d8..d9127cfa5f 100644 --- a/pythonforandroid/recipes/libxslt/__init__.py +++ b/pythonforandroid/recipes/libxslt/__init__.py @@ -6,7 +6,7 @@ class LibxsltRecipe(Recipe): - version = '1.1.32' + version = '1.1.34' url = 'http://xmlsoft.org/sources/libxslt-{version}.tar.gz' depends = ['libxml2'] patches = ['fix-dlopen.patch'] @@ -44,6 +44,8 @@ def build_arch(self, arch): _env=env) shprint(sh.make, "V=1", _env=env) + shprint(sh.Command('chmod'), '+x', 'xslt-config') + def get_recipe_env(self, arch): env = super().get_recipe_env(arch) env['CONFIG_SHELL'] = '/bin/bash' diff --git a/pythonforandroid/recipes/lxml/__init__.py b/pythonforandroid/recipes/lxml/__init__.py index 7bfa85f26f..4910caf7fc 100644 --- a/pythonforandroid/recipes/lxml/__init__.py +++ b/pythonforandroid/recipes/lxml/__init__.py @@ -4,7 +4,7 @@ class LXMLRecipe(CompiledComponentsPythonRecipe): - version = '4.2.5' + version = '4.8.0' url = 'https://pypi.python.org/packages/source/l/lxml/lxml-{version}.tar.gz' # noqa depends = ['librt', 'libxml2', 'libxslt', 'setuptools'] name = 'lxml' @@ -15,11 +15,16 @@ def should_build(self, arch): super().should_build(arch) py_ver = self.ctx.python_recipe.major_minor_version_string - build_platform = '{system}-{machine}'.format( - system=uname()[0], machine=uname()[-1]).lower() - build_dir = join(self.get_build_dir(arch.arch), 'build', - 'lib.' + build_platform + '-' + py_ver, 'lxml') - py_libs = ['_elementpath.so', 'builder.so', 'etree.so', 'objectify.so'] + build_platform = "{system}-{machine}".format( + system=uname()[0], machine=uname()[-1] + ).lower() + build_dir = join( + self.get_build_dir(arch.arch), + "build", + "lib." + build_platform + "-" + py_ver, + "lxml", + ) + py_libs = ["_elementpath.so", "builder.so", "etree.so", "objectify.so"] return not all([exists(join(build_dir, lib)) for lib in py_libs]) @@ -30,33 +35,29 @@ def get_recipe_env(self, arch): libxslt_recipe = Recipe.get_recipe('libxslt', self.ctx) libxslt_build_dir = libxslt_recipe.get_build_dir(arch.arch) - cflags = ' -I' + libxslt_build_dir - cflags += ' -I' + join(libxslt_build_dir, 'libxslt') - cflags += ' -I' + join(libxslt_build_dir, 'libexslt') - - env['LDFLAGS'] += ' -L' + join(libxslt_build_dir, 'libxslt', '.libs') - env['LDFLAGS'] += ' -L' + join(libxslt_build_dir, 'libexslt', '.libs') - env['LIBS'] = '-lxslt -lexslt' - # libxml2 flags libxml2_recipe = Recipe.get_recipe('libxml2', self.ctx) libxml2_build_dir = libxml2_recipe.get_build_dir(arch.arch) - libxml2_libs_dir = join(libxml2_build_dir, '.libs') - - cflags += ' -I' + libxml2_build_dir - cflags += ' -I' + join(libxml2_build_dir, 'include') - cflags += ' -I' + join(libxml2_build_dir, 'include', 'libxml') - cflags += ' -I' + self.get_build_dir(arch.arch) - env['LDFLAGS'] += ' -L' + libxml2_libs_dir - env['LIBS'] += ' -lxml2' - - # android's ndk flags - cflags += ' -I' + self.ctx.ndk_include_dir - env['LDFLAGS'] += ' -L' + arch.ndk_lib_dir - env['LIBS'] += ' -lz -lm -lc' - - if cflags not in env['CFLAGS']: - env['CFLAGS'] += cflags + + env["STATIC"] = "true" + + env["LXML_STATIC_INCLUDE_DIRS"] = "{}:{}".format( + join(libxml2_build_dir, "include"), join(libxslt_build_dir) + ) + env["LXML_STATIC_LIBRARY_DIRS"] = "{}:{}:{}".format( + join(libxml2_build_dir, ".libs"), + join(libxslt_build_dir, "libxslt", ".libs"), + join(libxslt_build_dir, "libexslt", ".libs"), + ) + + env["WITH_XML2_CONFIG"] = join(libxml2_build_dir, "xml2-config") + env["WITH_XSLT_CONFIG"] = join(libxslt_build_dir, "xslt-config") + + env["LXML_STATIC_BINARIES"] = "{}:{}:{}".format( + join(libxml2_build_dir, ".libs", "libxml2.a"), + join(libxslt_build_dir, "libxslt", ".libs", "libxslt.a"), + join(libxslt_build_dir, "libexslt", ".libs", "libexslt.a"), + ) return env diff --git a/pythonforandroid/recipes/matplotlib/__init__.py b/pythonforandroid/recipes/matplotlib/__init__.py index 57e5755761..a5f2094db3 100644 --- a/pythonforandroid/recipes/matplotlib/__init__.py +++ b/pythonforandroid/recipes/matplotlib/__init__.py @@ -98,7 +98,7 @@ def prebuild_arch(self, arch): with open(join(self.get_build_dir(arch), 'setup.cfg'), 'w') as fileh: fileh.write(setup_cfg.format( - ndk_sysroot_usr=join(self.ctx.ndk_dir, 'sysroot', 'usr'))) + ndk_sysroot_usr=join(self.ctx.ndk.sysroot, 'usr'))) self.generate_libraries_pc_files(arch) self.download_web_backend_dependencies(arch) @@ -109,10 +109,10 @@ def get_recipe_env(self, arch=None, with_flags_in_cc=True): # matplotlib compile flags does not honor the standard flags: # `CPPFLAGS` and `LDLIBS`, so we put in `CFLAGS` and `LDFLAGS` to # correctly link with the `c++_shared` library - env['CFLAGS'] += ' -I{}'.format(self.stl_include_dir) + env['CFLAGS'] += ' -I{}'.format(self.ctx.ndk.libcxx_include_dir) env['CFLAGS'] += ' -frtti -fexceptions' - env['LDFLAGS'] += ' -L{}'.format(self.get_stl_lib_dir(arch)) + env['LDFLAGS'] += ' -L{}'.format(arch.ndk_lib_dir) env['LDFLAGS'] += ' -l{}'.format(self.stl_lib_name) # we modify `XDG_CACHE_HOME` to download `jquery-ui` into that folder, diff --git a/pythonforandroid/recipes/numpy/__init__.py b/pythonforandroid/recipes/numpy/__init__.py index 6570134498..06f86466b4 100644 --- a/pythonforandroid/recipes/numpy/__init__.py +++ b/pythonforandroid/recipes/numpy/__init__.py @@ -19,6 +19,7 @@ class NumpyRecipe(CompiledComponentsPythonRecipe): patches = [ join("patches", "remove-default-paths.patch"), join("patches", "add_libm_explicitly_to_build.patch"), + join("patches", "ranlib.patch"), ] def get_recipe_env(self, arch=None, with_flags_in_cc=True): @@ -39,9 +40,6 @@ def _build_compiled_components(self, arch): env = self.get_recipe_env(arch) with current_directory(self.get_build_dir(arch.arch)): - hostpython = sh.Command(self.real_hostpython_location) - if self.install_in_hostpython: - shprint(hostpython, 'setup.py', 'clean', '--all', '--force', _env=env) hostpython = sh.Command(self.hostpython_location) shprint(hostpython, 'setup.py', self.build_cmd, '-v', _env=env, *self.setup_extra_args) @@ -67,5 +65,10 @@ def rebuild_compiled_components(self, arch, env): self._rebuild_compiled_components(arch, env) self.setup_extra_args = [] + def get_hostrecipe_env(self, arch): + env = super().get_hostrecipe_env(arch) + env['RANLIB'] = sh.which('ranlib') + return env + recipe = NumpyRecipe() diff --git a/pythonforandroid/recipes/numpy/patches/ranlib.patch b/pythonforandroid/recipes/numpy/patches/ranlib.patch new file mode 100644 index 0000000000..c0b5dad6b4 --- /dev/null +++ b/pythonforandroid/recipes/numpy/patches/ranlib.patch @@ -0,0 +1,11 @@ +diff -Naur numpy.orig/numpy/distutils/unixccompiler.py numpy/numpy/distutils/unixccompiler.py +--- numpy.orig/numpy/distutils/unixccompiler.py 2022-05-28 10:22:10.000000000 +0200 ++++ numpy/numpy/distutils/unixccompiler.py 2022-05-28 10:22:24.000000000 +0200 +@@ -124,6 +124,7 @@ + # platform intelligence here to skip ranlib if it's not + # needed -- or maybe Python's configure script took care of + # it for us, hence the check for leading colon. ++ self.ranlib = [os.environ.get('RANLIB')] + if self.ranlib: + display = '%s:@ %s' % (os.path.basename(self.ranlib[0]), + output_filename) diff --git a/pythonforandroid/recipes/png/__init__.py b/pythonforandroid/recipes/png/__init__.py index 48b060768d..6138195901 100644 --- a/pythonforandroid/recipes/png/__init__.py +++ b/pythonforandroid/recipes/png/__init__.py @@ -15,14 +15,8 @@ def build_arch(self, arch): build_dir = self.get_build_dir(arch.arch) with current_directory(build_dir): env = self.get_recipe_env(arch) - build_arch = ( - shprint(sh.gcc, '-dumpmachine') - .stdout.decode('utf-8') - .split('\n')[0] - ) shprint( sh.Command('./configure'), - '--build=' + build_arch, '--host=' + arch.command_prefix, '--target=' + arch.command_prefix, '--disable-static', diff --git a/pythonforandroid/recipes/pygame/__init__.py b/pythonforandroid/recipes/pygame/__init__.py index 0439f031c1..72e624ea32 100644 --- a/pythonforandroid/recipes/pygame/__init__.py +++ b/pythonforandroid/recipes/pygame/__init__.py @@ -28,7 +28,7 @@ def prebuild_arch(self, arch): with current_directory(self.get_build_dir(arch.arch)): setup_template = open(join("buildconfig", "Setup.Android.SDL2.in")).read() env = self.get_recipe_env(arch) - env['ANDROID_ROOT'] = join(arch.ndk_platform, 'usr') + env['ANDROID_ROOT'] = join(self.ctx.ndk.sysroot, 'usr') png = self.get_recipe('png', self.ctx) png_lib_dir = join(png.get_build_dir(arch.arch), '.libs') @@ -41,7 +41,7 @@ def prebuild_arch(self, arch): sdl_includes=( " -I" + join(self.ctx.bootstrap.build_dir, 'jni', 'SDL', 'include') + " -L" + join(self.ctx.bootstrap.build_dir, "libs", str(arch)) + - " -L" + png_lib_dir + " -L" + jpeg_lib_dir + " -L" + arch.ndk_lib_dir), + " -L" + png_lib_dir + " -L" + jpeg_lib_dir + " -L" + arch.ndk_lib_dir_versioned), sdl_ttf_includes="-I"+join(self.ctx.bootstrap.build_dir, 'jni', 'SDL2_ttf'), sdl_image_includes="-I"+join(self.ctx.bootstrap.build_dir, 'jni', 'SDL2_image'), sdl_mixer_includes="-I"+join(self.ctx.bootstrap.build_dir, 'jni', 'SDL2_mixer'), diff --git a/pythonforandroid/recipes/pyjnius/__init__.py b/pythonforandroid/recipes/pyjnius/__init__.py index 4e0bc1c6c4..58103e23b0 100644 --- a/pythonforandroid/recipes/pyjnius/__init__.py +++ b/pythonforandroid/recipes/pyjnius/__init__.py @@ -6,14 +6,13 @@ class PyjniusRecipe(CythonRecipe): - version = '1.3.0' + version = '1.4.2' url = 'https://github.com/kivy/pyjnius/archive/{version}.zip' name = 'pyjnius' depends = [('genericndkbuild', 'sdl2'), 'six'] site_packages_name = 'jnius' - patches = [('sdl2_jnienv_getter.patch', will_build('sdl2')), - ('genericndkbuild_jnienv_getter.patch', will_build('genericndkbuild'))] + patches = [('genericndkbuild_jnienv_getter.patch', will_build('genericndkbuild'))] def get_recipe_env(self, arch): env = super().get_recipe_env(arch) diff --git a/pythonforandroid/recipes/pyjnius/genericndkbuild_jnienv_getter.patch b/pythonforandroid/recipes/pyjnius/genericndkbuild_jnienv_getter.patch index 3d41dbb64c..fcd5387110 100644 --- a/pythonforandroid/recipes/pyjnius/genericndkbuild_jnienv_getter.patch +++ b/pythonforandroid/recipes/pyjnius/genericndkbuild_jnienv_getter.patch @@ -1,26 +1,24 @@ -diff --git a/jnius/jnius_jvm_android.pxi b/jnius/jnius_jvm_android.pxi -index ac89fec..71daa43 100644 ---- a/jnius/jnius_jvm_android.pxi -+++ b/jnius/jnius_jvm_android.pxi -@@ -1,5 +1,5 @@ +diff -Naur pyjnius.orig/jnius/env.py pyjnius/jnius/env.py +--- pyjnius.orig/jnius/env.py 2022-05-28 11:16:02.000000000 +0200 ++++ pyjnius/jnius/env.py 2022-05-28 11:18:30.000000000 +0200 +@@ -268,7 +268,7 @@ + + class AndroidJavaLocation(UnixJavaLocation): + def get_libraries(self): +- return ['SDL2', 'log'] ++ return ['main', 'log'] + + def get_include_dirs(self): + # When cross-compiling for Android, we should not use the include dirs +diff -Naur pyjnius.orig/jnius/jnius_jvm_android.pxi pyjnius/jnius/jnius_jvm_android.pxi +--- pyjnius.orig/jnius/jnius_jvm_android.pxi 2022-05-28 11:16:02.000000000 +0200 ++++ pyjnius/jnius/jnius_jvm_android.pxi 2022-05-28 11:17:17.000000000 +0200 +@@ -1,6 +1,6 @@ # on android, rely on SDL to get the JNI env --cdef extern JNIEnv *SDL_ANDROID_GetJNIEnv() +-cdef extern JNIEnv *SDL_AndroidGetJNIEnv() +cdef extern JNIEnv *WebView_AndroidGetJNIEnv() - cdef JNIEnv *get_platform_jnienv(): -- return SDL_ANDROID_GetJNIEnv() -+ return WebView_AndroidGetJNIEnv() -diff --git a/jnius/env.py b/jnius/env.py ---- a/jnius/env.py -+++ b/jnius/env.py -@@ -185,10 +185,10 @@ except ImportError: - def get_libraries(platform): - if platform == 'android': - # for android, we use SDL... -- return ['sdl', 'log'] -+ return ['main', 'log'] - - elif platform == 'win32': - return ['jvm'] - + cdef JNIEnv *get_platform_jnienv() except NULL: +- return SDL_AndroidGetJNIEnv() ++ return WebView_AndroidGetJNIEnv() diff --git a/pythonforandroid/recipes/pyjnius/sdl2_jnienv_getter.patch b/pythonforandroid/recipes/pyjnius/sdl2_jnienv_getter.patch deleted file mode 100644 index 3fc63a2935..0000000000 --- a/pythonforandroid/recipes/pyjnius/sdl2_jnienv_getter.patch +++ /dev/null @@ -1,27 +0,0 @@ -diff --git a/jnius/jnius_jvm_android.pxi b/jnius/jnius_jvm_android.pxi -index ac89fec..71daa43 100644 ---- a/jnius/jnius_jvm_android.pxi -+++ b/jnius/jnius_jvm_android.pxi -@@ -1,5 +1,5 @@ - # on android, rely on SDL to get the JNI env --cdef extern JNIEnv *SDL_ANDROID_GetJNIEnv() -+cdef extern JNIEnv *SDL_AndroidGetJNIEnv() - - cdef JNIEnv *get_platform_jnienv(): -- return SDL_ANDROID_GetJNIEnv() -+ return SDL_AndroidGetJNIEnv() -diff --git a/env.py b/env.py -index 740510f..0c8e55f 100644 ---- a/jnius/env.py -+++ b/jnius/env.py -@@ -185,10 +185,10 @@ except ImportError: - - def get_libraries(platform): - if platform == 'android': - # for android, we use SDL... -- return ['sdl', 'log'] -+ return ['SDL2', 'log'] - - elif platform == 'win32': - return ['jvm'] - diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index d740f8c847..b836382785 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -56,7 +56,7 @@ class Python3Recipe(TargetPythonRecipe): :class:`~pythonforandroid.python.GuestPythonRecipe` ''' - version = '3.8.9' + version = '3.9.9' url = 'https://www.python.org/ftp/python/{version}/Python-{version}.tgz' name = 'python3' @@ -264,8 +264,8 @@ def add_flags(include_flags, link_dirs, link_libs): # the build of zlib module, here we search for android's zlib version # and sets the right flags, so python can be build with android's zlib info("Activating flags for android's zlib") - zlib_lib_path = arch.ndk_lib_dir - zlib_includes = self.ctx.ndk_include_dir + zlib_lib_path = arch.ndk_lib_dir_versioned + zlib_includes = self.ctx.ndk.sysroot_include_dir zlib_h = join(zlib_includes, 'zlib.h') try: with open(zlib_h) as fileh: diff --git a/pythonforandroid/recipes/scipy/__init__.py b/pythonforandroid/recipes/scipy/__init__.py index 6302d9c235..455a9887d7 100644 --- a/pythonforandroid/recipes/scipy/__init__.py +++ b/pythonforandroid/recipes/scipy/__init__.py @@ -32,6 +32,13 @@ def rebuild_compiled_components(self, arch, env): def get_recipe_env(self, arch): env = super().get_recipe_env(arch) + arch_env = arch.get_env() + + 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' @@ -56,8 +63,8 @@ def get_recipe_env(self, arch): # 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}' + 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' diff --git a/tests/recipes/recipe_ctx.py b/tests/recipes/recipe_ctx.py index 056ca88454..ed43bd6ff8 100644 --- a/tests/recipes/recipe_ctx.py +++ b/tests/recipes/recipe_ctx.py @@ -5,6 +5,7 @@ from pythonforandroid.recipe import Recipe from pythonforandroid.build import Context from pythonforandroid.archs import ArchAarch_64 +from pythonforandroid.androidndk import AndroidNDK class RecipeCtx: @@ -36,6 +37,7 @@ def setUp(self): self.ctx.android_api = 27 self.ctx._sdk_dir = "/opt/android/android-sdk" self.ctx._ndk_dir = "/opt/android/android-ndk" + self.ctx.ndk = AndroidNDK(self.ctx._ndk_dir) self.ctx.setup_dirs(os.getcwd()) self.ctx.bootstrap = Bootstrap().get_bootstrap("sdl2", self.ctx) self.ctx.bootstrap.distribution = Distribution.get_distribution( diff --git a/tests/recipes/test_icu.py b/tests/recipes/test_icu.py index d988456f85..b928b99bf5 100644 --- a/tests/recipes/test_icu.py +++ b/tests/recipes/test_icu.py @@ -4,7 +4,6 @@ from tests.recipes.recipe_ctx import RecipeCtx from pythonforandroid.recipes.icu import ICURecipe -from pythonforandroid.util import build_platform class TestIcuRecipe(RecipeCtx, unittest.TestCase): @@ -46,7 +45,7 @@ def test_build_arch( ): mock_find_executable.return_value = os.path.join( self.ctx._ndk_dir, - f"toolchains/llvm/prebuilt/{build_platform}/bin/clang", + f"toolchains/llvm/prebuilt/{self.ctx.ndk.host_tag}/bin/clang", ) self.ctx.toolchain_version = "4.9" self.recipe.build_arch(self.arch) diff --git a/tests/test_androidndk.py b/tests/test_androidndk.py new file mode 100644 index 0000000000..6d89b131c2 --- /dev/null +++ b/tests/test_androidndk.py @@ -0,0 +1,140 @@ +import unittest +from unittest import mock + +from pythonforandroid.androidndk import AndroidNDK + + +class TestAndroidNDK(unittest.TestCase): + """ + An inherited class of `unittest.TestCase`to test the module + :mod:`~pythonforandroid.androidndk`. + """ + + def setUp(self): + """Configure a :class:`~pythonforandroid.androidndk.AndroidNDK` so we can + perform our unittests""" + self.ndk = AndroidNDK("/opt/android/android-ndk") + + @mock.patch("sys.platform", "linux") + def test_host_tag_linux(self): + """Test the `host_tag` property of the :class:`~pythonforandroid.androidndk.AndroidNDK` + class when the host is Linux.""" + self.assertEqual(self.ndk.host_tag, "linux-x86_64") + + @mock.patch("sys.platform", "darwin") + def test_host_tag_darwin(self): + """Test the `host_tag` property of the :class:`~pythonforandroid.androidndk.AndroidNDK` + class when the host is Darwin.""" + self.assertEqual(self.ndk.host_tag, "darwin-x86_64") + + def test_llvm_prebuilt_dir(self): + """Test the `llvm_prebuilt_dir` property of the + :class:`~pythonforandroid.androidndk.AndroidNDK` class.""" + self.assertEqual( + self.ndk.llvm_prebuilt_dir, + f"/opt/android/android-ndk/toolchains/llvm/prebuilt/{self.ndk.host_tag}", + ) + + def test_llvm_bin_dir(self): + """Test the `llvm_bin_dir` property of the + :class:`~pythonforandroid.androidndk.AndroidNDK` class.""" + self.assertEqual( + self.ndk.llvm_bin_dir, + f"/opt/android/android-ndk/toolchains/llvm/prebuilt/{self.ndk.host_tag}/bin", + ) + + def test_clang(self): + """Test the `clang` property of the + :class:`~pythonforandroid.androidndk.AndroidNDK` class.""" + self.assertEqual( + self.ndk.clang, + f"/opt/android/android-ndk/toolchains/llvm/prebuilt/{self.ndk.host_tag}/bin/clang", + ) + + def test_clang_cxx(self): + """Test the `clang_cxx` property of the + :class:`~pythonforandroid.androidndk.AndroidNDK` class.""" + self.assertEqual( + self.ndk.clang_cxx, + f"/opt/android/android-ndk/toolchains/llvm/prebuilt/{self.ndk.host_tag}/bin/clang++", + ) + + def test_llvm_ar(self): + """Test the `llvm_ar` property of the + :class:`~pythonforandroid.androidndk.AndroidNDK` class.""" + self.assertEqual( + self.ndk.llvm_ar, + f"/opt/android/android-ndk/toolchains/llvm/prebuilt/{self.ndk.host_tag}/bin/llvm-ar", + ) + + def test_llvm_ranlib(self): + """Test the `llvm_ranlib` property of the + :class:`~pythonforandroid.androidndk.AndroidNDK` class.""" + self.assertEqual( + self.ndk.llvm_ranlib, + f"/opt/android/android-ndk/toolchains/llvm/prebuilt/{self.ndk.host_tag}/bin/llvm-ranlib", + ) + + def test_llvm_objcopy(self): + """Test the `llvm_objcopy` property of the + :class:`~pythonforandroid.androidndk.AndroidNDK` class.""" + self.assertEqual( + self.ndk.llvm_objcopy, + f"/opt/android/android-ndk/toolchains/llvm/prebuilt/{self.ndk.host_tag}/bin/llvm-objcopy", + ) + + def test_llvm_objdump(self): + """Test the `llvm_objdump` property of the + :class:`~pythonforandroid.androidndk.AndroidNDK` class.""" + self.assertEqual( + self.ndk.llvm_objdump, + f"/opt/android/android-ndk/toolchains/llvm/prebuilt/{self.ndk.host_tag}/bin/llvm-objdump", + ) + + def test_llvm_readelf(self): + """Test the `llvm_readelf` property of the + :class:`~pythonforandroid.androidndk.AndroidNDK` class.""" + self.assertEqual( + self.ndk.llvm_readelf, + f"/opt/android/android-ndk/toolchains/llvm/prebuilt/{self.ndk.host_tag}/bin/llvm-readelf", + ) + + def test_llvm_strip(self): + """Test the `llvm_strip` property of the + :class:`~pythonforandroid.androidndk.AndroidNDK` class.""" + self.assertEqual( + self.ndk.llvm_strip, + f"/opt/android/android-ndk/toolchains/llvm/prebuilt/{self.ndk.host_tag}/bin/llvm-strip", + ) + + def test_sysroot(self): + """Test the `sysroot` property of the + :class:`~pythonforandroid.androidndk.AndroidNDK` class.""" + self.assertEqual( + self.ndk.sysroot, + f"/opt/android/android-ndk/toolchains/llvm/prebuilt/{self.ndk.host_tag}/sysroot", + ) + + def test_sysroot_include_dir(self): + """Test the `sysroot_include_dir` property of the + :class:`~pythonforandroid.androidndk.AndroidNDK` class.""" + self.assertEqual( + self.ndk.sysroot_include_dir, + f"/opt/android/android-ndk/toolchains/llvm/prebuilt/{self.ndk.host_tag}/sysroot/usr/include", + ) + + def test_sysroot_lib_dir(self): + """Test the `sysroot_lib_dir` property of the + :class:`~pythonforandroid.androidndk.AndroidNDK` class.""" + self.assertEqual( + self.ndk.sysroot_lib_dir, + f"/opt/android/android-ndk/toolchains/llvm/prebuilt/{self.ndk.host_tag}/sysroot/usr/lib", + ) + + def test_libcxx_include_dir(self): + """Test the `libcxx_include_dir` property of the + :class:`~pythonforandroid.androidndk.AndroidNDK` class.""" + self.assertEqual( + self.ndk.libcxx_include_dir, + f"/opt/android/android-ndk/toolchains/llvm/prebuilt/{self.ndk.host_tag}/sysroot/usr/include/c++/v1", + ) diff --git a/tests/test_archs.py b/tests/test_archs.py index f4f62be525..44ff6237ba 100644 --- a/tests/test_archs.py +++ b/tests/test_archs.py @@ -7,7 +7,7 @@ from pythonforandroid.distribution import Distribution from pythonforandroid.recipe import Recipe from pythonforandroid.build import Context -from pythonforandroid.util import BuildInterruptingException, build_platform +from pythonforandroid.util import BuildInterruptingException from pythonforandroid.archs import ( Arch, ArchARM, @@ -16,6 +16,7 @@ Archx86, Archx86_64, ) +from pythonforandroid.androidndk import AndroidNDK expected_env_gcc_keys = { "CFLAGS", @@ -52,6 +53,7 @@ def setUp(self): self.ctx.android_api = 27 self.ctx._sdk_dir = "/opt/android/android-sdk" self.ctx._ndk_dir = "/opt/android/android-ndk" + self.ctx.ndk = AndroidNDK(self.ctx._ndk_dir) self.ctx.setup_dirs(os.getcwd()) self.ctx.bootstrap = Bootstrap().get_bootstrap("sdl2", self.ctx) self.ctx.bootstrap.distribution = Distribution.get_distribution( @@ -65,7 +67,7 @@ def setUp(self): # should be the same for all the tests (no more gcc compiler) self.expected_compiler = ( f"/opt/android/android-ndk/toolchains/" - f"llvm/prebuilt/{build_platform}/bin/clang" + f"llvm/prebuilt/{self.ctx.ndk.host_tag}/bin/clang" ) @@ -137,7 +139,7 @@ def test_arch_arm(self, mock_ensure_dir, mock_find_executable): env["STRIP"].split()[0], os.path.join( self.ctx._ndk_dir, - f"toolchains/llvm/prebuilt/{build_platform}/bin", + f"toolchains/llvm/prebuilt/{self.ctx.ndk.host_tag}/bin", "llvm-strip", ) ) @@ -145,7 +147,7 @@ def test_arch_arm(self, mock_ensure_dir, mock_find_executable): env["READELF"].split()[0], os.path.join( self.ctx._ndk_dir, - f"toolchains/llvm/prebuilt/{build_platform}/bin", + f"toolchains/llvm/prebuilt/{self.ctx.ndk.host_tag}/bin", "llvm-readelf", ) ) @@ -211,21 +213,18 @@ def test_arch_armv7a( ) # check clang - build_platform = "{system}-{machine}".format( - system=os.uname()[0], machine=os.uname()[-1] - ).lower() self.assertEqual( env["CC"].split()[0], "{ndk_dir}/toolchains/llvm/prebuilt/" - "{build_platform}/bin/clang".format( - ndk_dir=self.ctx._ndk_dir, build_platform=build_platform + "{host_tag}/bin/clang".format( + ndk_dir=self.ctx._ndk_dir, host_tag=self.ctx.ndk.host_tag ), ) self.assertEqual( env["CXX"].split()[0], "{ndk_dir}/toolchains/llvm/prebuilt/" - "{build_platform}/bin/clang++".format( - ndk_dir=self.ctx._ndk_dir, build_platform=build_platform + "{host_tag}/bin/clang++".format( + ndk_dir=self.ctx._ndk_dir, host_tag=self.ctx.ndk.host_tag ), ) diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py index 5deb44978a..e997eba8c2 100644 --- a/tests/test_bootstrap.py +++ b/tests/test_bootstrap.py @@ -12,7 +12,8 @@ from pythonforandroid.recipe import Recipe from pythonforandroid.archs import ArchARMv7_a from pythonforandroid.build import Context -from pythonforandroid.util import BuildInterruptingException, build_platform +from pythonforandroid.util import BuildInterruptingException +from pythonforandroid.androidndk import AndroidNDK from test_graph import get_fake_recipe @@ -33,6 +34,7 @@ def setUp(self): self.ctx.android_api = 27 self.ctx._sdk_dir = "/opt/android/android-sdk" self.ctx._ndk_dir = "/opt/android/android-ndk" + self.ctx.ndk = AndroidNDK(self.ctx._ndk_dir) self.ctx.setup_dirs(os.getcwd()) self.ctx.recipe_build_order = [ "hostpython3", @@ -528,7 +530,7 @@ def test_bootstrap_strip( ): mock_find_executable.return_value = os.path.join( self.ctx._ndk_dir, - f"toolchains/llvm/prebuilt/{build_platform}/bin/clang", + f"toolchains/llvm/prebuilt/{self.ctx.ndk.host_tag}/bin/clang", ) # prepare arch, bootstrap, distribution and PythonRecipe arch = ArchARMv7_a(self.ctx) @@ -547,7 +549,7 @@ def test_bootstrap_strip( mock_sh_command.assert_called_once_with( os.path.join( self.ctx._ndk_dir, - f"toolchains/llvm/prebuilt/{build_platform}/bin", + f"toolchains/llvm/prebuilt/{self.ctx.ndk.host_tag}/bin", "llvm-strip", ) ) diff --git a/tests/test_build.py b/tests/test_build.py index 634a04b643..6d30f996e7 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -1,5 +1,4 @@ import os -import sys import unittest from unittest import mock @@ -122,13 +121,9 @@ def test_sdk_ndk_paths( assert context.sdk_dir == real_sdk_dir assert context.ndk_dir == real_ndk_dir - py_platform = sys.platform - if py_platform in ['linux2', 'linux3']: - py_platform = 'linux' - context_paths = context.env['PATH'].split(':') assert context_paths[0:3] == [ - f'{real_ndk_dir}/toolchains/llvm/prebuilt/{py_platform}-x86_64/bin', + f'{real_ndk_dir}/toolchains/llvm/prebuilt/{context.ndk.host_tag}/bin', real_ndk_dir, f'{real_sdk_dir}/tools' ] diff --git a/tests/test_recipe.py b/tests/test_recipe.py index c82a32fb95..5194289d60 100644 --- a/tests/test_recipe.py +++ b/tests/test_recipe.py @@ -10,7 +10,6 @@ from pythonforandroid.recipe import Recipe, import_recipe from pythonforandroid.archs import ArchAarch_64 from pythonforandroid.bootstrap import Bootstrap -from pythonforandroid.util import build_platform from test_bootstrap import BaseClassSetupBootstrap @@ -250,24 +249,6 @@ def setUp(self): self.setUp_distribution_with_bootstrap(self.ctx.bootstrap) self.ctx.python_recipe = Recipe.get_recipe('python3', self.ctx) - def test_get_stl_lib_dir(self): - """ - Test that :meth:`~pythonforandroid.recipe.STLRecipe.get_stl_lib_dir` - returns the expected path for the stl library - """ - arch = ArchAarch_64(self.ctx) - recipe = Recipe.get_recipe('libgeos', self.ctx) - self.assertTrue(recipe.need_stl_shared) - self.assertEqual( - recipe.get_stl_lib_dir(arch), - os.path.join( - self.ctx.ndk_dir, - 'sources/cxx-stl/llvm-libc++/libs/{arch}'.format( - arch=arch.arch - ), - ), - ) - @mock.patch('pythonforandroid.archs.find_executable') @mock.patch('pythonforandroid.build.ensure_dir') def test_get_recipe_env_with( @@ -283,7 +264,7 @@ def test_get_recipe_env_with( """ expected_compiler = ( f"/opt/android/android-ndk/toolchains/" - f"llvm/prebuilt/{build_platform}/bin/clang" + f"llvm/prebuilt/{self.ctx.ndk.host_tag}/bin/clang" ) mock_find_executable.return_value = expected_compiler @@ -300,7 +281,7 @@ def test_get_recipe_env_with( # check `CPPFLAGS` expected_cppflags = { - '-I{stl_include}'.format(stl_include=recipe.stl_include_dir) + '-I{libcxx_include}'.format(libcxx_include=self.ctx.ndk.libcxx_include_dir) } self.assertIn('CPPFLAGS', env) for flags in expected_cppflags: @@ -308,7 +289,7 @@ def test_get_recipe_env_with( # check `LIBS` self.assertIn('LDFLAGS', env) - self.assertIn('-L' + recipe.get_stl_lib_dir(arch), env['LDFLAGS']) + self.assertIn('-L' + arch.ndk_lib_dir, env['LDFLAGS']) self.assertIn('LIBS', env) self.assertIn('-lc++_shared', env['LIBS']) @@ -339,12 +320,7 @@ def test_install_stl_lib( recipe.install_stl_lib(arch) mock_install_lib.assert_called_once_with( arch, - '{ndk_dir}/sources/cxx-stl/llvm-libc++/' - 'libs/{arch}/lib{stl_lib}.so'.format( - ndk_dir=self.ctx.ndk_dir, - arch=arch.arch, - stl_lib=recipe.stl_lib_name, - ), + os.path.join(arch.ndk_lib_dir, f"lib{recipe.stl_lib_name}.so"), ) mock_ensure_dir.assert_called() diff --git a/tests/test_toolchain.py b/tests/test_toolchain.py index 311edd6fe6..23d0d3ff9e 100644 --- a/tests/test_toolchain.py +++ b/tests/test_toolchain.py @@ -1,12 +1,10 @@ import io import sys -from os.path import join import pytest from unittest import mock from pythonforandroid.recipe import Recipe from pythonforandroid.toolchain import ToolchainCL from pythonforandroid.util import BuildInterruptingException -from pythonforandroid.build import get_ndk_standalone def patch_sys_argv(argv): @@ -70,18 +68,12 @@ def test_create(self): with patch_sys_argv(argv), mock.patch( 'pythonforandroid.build.get_available_apis' ) as m_get_available_apis, mock.patch( - 'pythonforandroid.build.get_ndk_sysroot' - ) as m_get_ndk_sysroot, mock.patch( 'pythonforandroid.toolchain.build_recipes' ) as m_build_recipes, mock.patch( 'pythonforandroid.bootstraps.service_only.' 'ServiceOnlyBootstrap.assemble_distribution' ) as m_run_distribute: m_get_available_apis.return_value = [27] - m_get_ndk_sysroot.return_value = ( - join(get_ndk_standalone("/tmp/android-ndk"), "sysroot"), - True, - ) tchain = ToolchainCL() assert tchain.ctx.activity_class_name == 'abc.myapp.android.CustomPythonActivity' assert tchain.ctx.service_class_name == 'xyz.myapp.android.CustomPythonService'