From dc8619b98e7a3e2302ac3d49e0075cbaa343a36f Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sun, 23 Sep 2018 00:24:43 +0100 Subject: [PATCH 01/50] Added python3 and hostpython3 files --- pythonforandroid/recipes/hostpython3/__init__.py | 0 pythonforandroid/recipes/python3/__init__.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 pythonforandroid/recipes/hostpython3/__init__.py create mode 100644 pythonforandroid/recipes/python3/__init__.py diff --git a/pythonforandroid/recipes/hostpython3/__init__.py b/pythonforandroid/recipes/hostpython3/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py new file mode 100644 index 0000000000..e69de29bb2 From 008b41d3e2661186034e53316332dfa676aaaee1 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sun, 30 Sep 2018 15:11:20 +0100 Subject: [PATCH 02/50] Added hostpython3 recipe --- pythonforandroid/bootstraps/sdl2/__init__.py | 2 +- pythonforandroid/recipe.py | 2 +- .../recipes/hostpython3/__init__.py | 53 +++++++++++++++++++ pythonforandroid/recipes/pyjnius/__init__.py | 2 +- pythonforandroid/recipes/python3/__init__.py | 18 +++++++ pythonforandroid/recipes/sdl2/__init__.py | 2 +- pythonforandroid/recipes/six/__init__.py | 1 - testapps/setup_testapp_python3.py | 4 +- testapps/setup_testapp_python3crystax.py | 31 +++++++++++ 9 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 testapps/setup_testapp_python3crystax.py diff --git a/pythonforandroid/bootstraps/sdl2/__init__.py b/pythonforandroid/bootstraps/sdl2/__init__.py index 95748a63df..b8c4bbeff8 100644 --- a/pythonforandroid/bootstraps/sdl2/__init__.py +++ b/pythonforandroid/bootstraps/sdl2/__init__.py @@ -13,7 +13,7 @@ class SDL2GradleBootstrap(Bootstrap): name = 'sdl2_gradle' - recipe_depends = ['sdl2', ('python2', 'python3crystax')] + recipe_depends = ['sdl2', ('python2', 'python3', 'python3crystax')] def run_distribute(self): info_main("# Creating Android project ({})".format(self.name)) diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 6a066ee47c..1057536caf 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -952,7 +952,7 @@ class CythonRecipe(PythonRecipe): def __init__(self, *args, **kwargs): super(CythonRecipe, self).__init__(*args, **kwargs) depends = self.depends - depends.append(('python2', 'python3crystax')) + depends.append(('python2', 'python3', 'python3crystax')) depends = list(set(depends)) self.depends = depends diff --git a/pythonforandroid/recipes/hostpython3/__init__.py b/pythonforandroid/recipes/hostpython3/__init__.py index e69de29bb2..4312134f24 100644 --- a/pythonforandroid/recipes/hostpython3/__init__.py +++ b/pythonforandroid/recipes/hostpython3/__init__.py @@ -0,0 +1,53 @@ +from pythonforandroid.toolchain import Recipe, shprint, info, warning +from pythonforandroid.util import ensure_dir, current_directory +from os.path import join, exists +import os +import sh + + +class Hostpython3Recipe(Recipe): + version = 'bpo-30386' + url = 'https://github.com/inclement/cpython/archive/{version}.zip' + name = 'hostpython3' + + conflicts = ['hostpython2'] + + def get_build_container_dir(self, arch=None): + choices = self.check_recipe_choices() + dir_name = '-'.join([self.name] + choices) + return join(self.ctx.build_dir, 'other_builds', dir_name, 'desktop') + + def get_build_dir(self, arch=None): + # Unlike other recipes, the hostpython build dir doesn't depend on the target arch + return join(self.get_build_container_dir(), self.name) + + def build_arch(self, arch): + recipe_build_dir = self.get_build_dir(arch.arch) + with current_directory(recipe_build_dir): + # Create a subdirectory to actually perform the build + build_dir = join(recipe_build_dir, 'native-build') + ensure_dir(build_dir) + + env = {} # The command line environment we will use + + + # Configure the build + with current_directory(build_dir): + if not exists('config.status'): + shprint(sh.Command(join(recipe_build_dir, 'configure'))) + + # Create the Setup file. This copying from Setup.dist + # seems to be the normal and expected procedure. + assert exists(join(build_dir, 'Modules')), ( + 'Expected dir {} does not exist'.format(join(build_dir, 'Modules'))) + shprint(sh.cp, join('Modules', 'Setup.dist'), join(build_dir, 'Modules', 'Setup')) + + result = shprint(sh.make, '-C', build_dir) + + self.ctx.hostpython = join(build_dir, 'python') + self.ctx.hostpgen = '/usr/bin/false' # is this actually used for anything? + + print('result is', result) + exit(1) + +recipe = Hostpython3Recipe() diff --git a/pythonforandroid/recipes/pyjnius/__init__.py b/pythonforandroid/recipes/pyjnius/__init__.py index 90d63a866c..db9bd639eb 100644 --- a/pythonforandroid/recipes/pyjnius/__init__.py +++ b/pythonforandroid/recipes/pyjnius/__init__.py @@ -9,7 +9,7 @@ class PyjniusRecipe(CythonRecipe): version = '1.1.3' url = 'https://github.com/kivy/pyjnius/archive/{version}.zip' name = 'pyjnius' - depends = [('python2', 'python3crystax'), ('genericndkbuild', 'sdl2', 'sdl'), 'six'] + depends = [('python2', 'python3', 'python3crystax'), ('genericndkbuild', 'sdl2', 'sdl'), 'six'] site_packages_name = 'jnius' patches = [('sdl2_jnienv_getter.patch', will_build('sdl2')), diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index e69de29bb2..4af077d23c 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -0,0 +1,18 @@ +from pythonforandroid.recipe import TargetPythonRecipe, Recipe +from pythonforandroid.toolchain import shprint, current_directory, info +from pythonforandroid.patching import (is_darwin, is_api_gt, + check_all, is_api_lt, is_ndk) +from os.path import exists, join, realpath +import sh + + +class Python3Recipe(TargetPythonRecipe): + version = 'bpo-30386' + url = 'https://github.com/inclement/cpython/archive/{version}.zip' + name = 'python3' + + depends = ['hostpython3'] + conflicts = ['python3crystax', 'python2'] + opt_depends = ['openssl', 'sqlite3'] + +recipe = Python3Recipe() diff --git a/pythonforandroid/recipes/sdl2/__init__.py b/pythonforandroid/recipes/sdl2/__init__.py index 79bf64c3c7..59b91dac6d 100644 --- a/pythonforandroid/recipes/sdl2/__init__.py +++ b/pythonforandroid/recipes/sdl2/__init__.py @@ -10,7 +10,7 @@ class LibSDL2Recipe(BootstrapNDKRecipe): dir_name = 'SDL' - depends = [('python2', 'python3crystax'), 'sdl2_image', 'sdl2_mixer', 'sdl2_ttf'] + depends = [('python2', 'python3', 'python3crystax'), 'sdl2_image', 'sdl2_mixer', 'sdl2_ttf'] conflicts = ['sdl', 'pygame', 'pygame_bootstrap_components'] patches = ['add_nativeSetEnv.patch'] diff --git a/pythonforandroid/recipes/six/__init__.py b/pythonforandroid/recipes/six/__init__.py index e1e13b9ea6..fb1ad8a3dc 100644 --- a/pythonforandroid/recipes/six/__init__.py +++ b/pythonforandroid/recipes/six/__init__.py @@ -5,7 +5,6 @@ class SixRecipe(PythonRecipe): version = '1.9.0' url = 'https://pypi.python.org/packages/source/s/six/six-{version}.tar.gz' - depends = [('python2', 'python3crystax')] recipe = SixRecipe() diff --git a/testapps/setup_testapp_python3.py b/testapps/setup_testapp_python3.py index f5e09a22d3..bd7a22ffd0 100644 --- a/testapps/setup_testapp_python3.py +++ b/testapps/setup_testapp_python3.py @@ -2,8 +2,8 @@ from distutils.core import setup from setuptools import find_packages -options = {'apk': { - 'requirements': 'sdl2,pyjnius,kivy,python3crystax', +options = {'apk': {'debug': None, + 'requirements': 'sdl2,pyjnius,kivy,python3', 'android-api': 19, 'ndk-dir': '/home/asandy/android/crystax-ndk-10.3.2', 'dist-name': 'bdisttest_python3', diff --git a/testapps/setup_testapp_python3crystax.py b/testapps/setup_testapp_python3crystax.py new file mode 100644 index 0000000000..530381d973 --- /dev/null +++ b/testapps/setup_testapp_python3crystax.py @@ -0,0 +1,31 @@ + +from distutils.core import setup +from setuptools import find_packages + +options = {'apk': {'debug': None, + 'requirements': 'sdl2,pyjnius,kivy,python3crystax', + 'android-api': 19, + 'ndk-dir': '/home/asandy/android/crystax-ndk-10.3.2', + 'dist-name': 'bdisttest_python3', + 'ndk-version': '10.3.2', + 'arch': 'armeabi-v7a', + 'permission': 'VIBRATE', + }} + +package_data = {'': ['*.py', + '*.png'] + } + +packages = find_packages() +print('packages are', packages) + +setup( + name='testapp_python3', + version='1.1', + description='p4a setup.py test', + author='Alexander Taylor', + author_email='alexanderjohntaylor@gmail.com', + packages=find_packages(), + options=options, + package_data={'testapp': ['*.py', '*.png']} +) From 901a153ef373b0c44a507158fc0cfe6aed587ecb Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sun, 14 Oct 2018 15:47:29 +0100 Subject: [PATCH 03/50] Got python3 recipe to the point of configure working --- pythonforandroid/recipe.py | 7 ++ .../recipes/hostpython3/__init__.py | 37 ++++----- pythonforandroid/recipes/python3/__init__.py | 80 ++++++++++++++++++- pythonforandroid/recipes/six/__init__.py | 1 + 4 files changed, 106 insertions(+), 19 deletions(-) diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 1057536caf..d5ea9df267 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -712,6 +712,13 @@ class PythonRecipe(Recipe): setup_extra_args = [] '''List of extra arugments to pass to setup.py''' + def __init__(self, *args, **kwargs): + super(PythonRecipe, self).__init__(*args, **kwargs) + depends = self.depends + depends.append(('python2', 'python3', 'python3crystax')) + depends = list(set(depends)) + self.depends = depends + def clean_build(self, arch=None): super(PythonRecipe, self).clean_build(arch=arch) name = self.folder_name diff --git a/pythonforandroid/recipes/hostpython3/__init__.py b/pythonforandroid/recipes/hostpython3/__init__.py index 4312134f24..5bde9da70a 100644 --- a/pythonforandroid/recipes/hostpython3/__init__.py +++ b/pythonforandroid/recipes/hostpython3/__init__.py @@ -23,31 +23,32 @@ def get_build_dir(self, arch=None): def build_arch(self, arch): recipe_build_dir = self.get_build_dir(arch.arch) - with current_directory(recipe_build_dir): - # Create a subdirectory to actually perform the build - build_dir = join(recipe_build_dir, 'native-build') - ensure_dir(build_dir) - env = {} # The command line environment we will use + # Create a subdirectory to actually perform the build + build_dir = join(recipe_build_dir, 'native-build') + ensure_dir(build_dir) + if not exists(join(build_dir, 'python')): + with current_directory(recipe_build_dir): + env = {} # The command line environment we will use - # Configure the build - with current_directory(build_dir): - if not exists('config.status'): - shprint(sh.Command(join(recipe_build_dir, 'configure'))) - # Create the Setup file. This copying from Setup.dist - # seems to be the normal and expected procedure. - assert exists(join(build_dir, 'Modules')), ( - 'Expected dir {} does not exist'.format(join(build_dir, 'Modules'))) - shprint(sh.cp, join('Modules', 'Setup.dist'), join(build_dir, 'Modules', 'Setup')) + # Configure the build + with current_directory(build_dir): + if not exists('config.status'): + shprint(sh.Command(join(recipe_build_dir, 'configure'))) - result = shprint(sh.make, '-C', build_dir) + # Create the Setup file. This copying from Setup.dist + # seems to be the normal and expected procedure. + assert exists(join(build_dir, 'Modules')), ( + 'Expected dir {} does not exist'.format(join(build_dir, 'Modules'))) + shprint(sh.cp, join('Modules', 'Setup.dist'), join(build_dir, 'Modules', 'Setup')) + + result = shprint(sh.make, '-C', build_dir) + else: + info('Skipping hostpython3 build as it has already been completed') self.ctx.hostpython = join(build_dir, 'python') self.ctx.hostpgen = '/usr/bin/false' # is this actually used for anything? - print('result is', result) - exit(1) - recipe = Hostpython3Recipe() diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index 4af077d23c..33f5b69f0f 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -2,7 +2,9 @@ from pythonforandroid.toolchain import shprint, current_directory, info from pythonforandroid.patching import (is_darwin, is_api_gt, check_all, is_api_lt, is_ndk) +from pythonforandroid.util import ensure_dir from os.path import exists, join, realpath +from os import environ import sh @@ -13,6 +15,82 @@ class Python3Recipe(TargetPythonRecipe): depends = ['hostpython3'] conflicts = ['python3crystax', 'python2'] - opt_depends = ['openssl', 'sqlite3'] + # opt_depends = ['openssl', 'sqlite3'] + + def build_arch(self, arch): + recipe_build_dir = self.get_build_dir(arch.arch) + + # Create a subdirectory to actually perform the build + build_dir = join(recipe_build_dir, 'android-build') + ensure_dir(build_dir) + + # TODO: Get these dynamically, like bpo-30386 does + sys_prefix = '/usr/local' + sys_exec_prefix = '/usr/local' + + # Skipping "Ensure that nl_langinfo is broken" from the original bpo-30386 + + with current_directory(build_dir): + env = environ.copy() + + + # TODO: Get this information from p4a's arch system + android_host = 'arm-linux-androideabi' + android_build = sh.Command(join(recipe_build_dir, 'config.guess'))().stdout.strip().decode('utf-8') + platform_dir = join(self.ctx.ndk_dir, 'platforms', 'android-19', 'arch-arm') + toolchain = '{android_host}-4.9'.format(android_host=android_host) + toolchain = join(self.ctx.ndk_dir, 'toolchains', toolchain, 'prebuilt', 'linux-x86_64') + CC = '{clang} -target {target} -gcc-toolchain {toolchain}'.format( + clang=join(self.ctx.ndk_dir, 'toolchains', 'llvm', 'prebuilt', 'linux-x86_64', 'bin', 'clang'), + target='armv7-none-linux-androideabi', + toolchain=toolchain) + + AR = join(toolchain, 'bin', android_host) + '-ar' + LD = join(toolchain, 'bin', android_host) + '-ld' + RANLIB = join(toolchain, 'bin', android_host) + '-ranlib' + READELF = join(toolchain, 'bin', android_host) + '-readelf' + STRIP = join(toolchain, 'bin', android_host) + '-strip --strip-debug --strip-unneeded' + + env['CC'] = CC + env['AR'] = AR + env['LD'] = LD + env['RANLIB'] = RANLIB + env['READELF'] = READELF + env['STRIP'] = STRIP + + ndk_flags = '--sysroot={ndk_sysroot} -D__ANDROID_API__=19 -isystem {ndk_android_host}'.format( + ndk_sysroot=join(self.ctx.ndk_dir, 'sysroot'), + ndk_android_host=join(self.ctx.ndk_dir, 'sysroot', 'usr', 'include', android_host)) + sysroot = join(self.ctx.ndk_dir, 'platforms', 'android-19', 'arch-arm') + env['CFLAGS'] = env.get('CFLAGS', '') + ' ' + ndk_flags + env['CPPFLAGS'] = env.get('CPPFLAGS', '') + ' ' + ndk_flags + env['LDFLAGS'] = env.get('LDFLAGS', '') + ' --sysroot={} -march=armv7-a -Wl,--fix-cortex-a8'.format(sysroot) + + print('CPPflags', env['CPPFLAGS']) + print('LDFLAGS', env['LDFLAGS']) + + print('LD is', env['LD']) + + shprint(sh.Command(join(recipe_build_dir, 'configure')), + *(' '.join(('--host={android_host}', + '--build={android_build}', + '--enable-shared', + '--disable-ipv6', + 'ac_cv_file__dev_ptmx=yes', + 'ac_cv_file__dev_ptc=no', + '--without-ensurepip', + 'ac_cv_little_endian_double=yes', + '--prefix={prefix}', + '--exec-prefix={exec_prefix}')).format( + android_host=android_host, + android_build=android_build, + prefix=sys_prefix, + exec_prefix=sys_exec_prefix)).split(' '), _env=env) + + # if not exists('config.status'): + + + # shprint(sh.make, '-C', build_dir) + recipe = Python3Recipe() diff --git a/pythonforandroid/recipes/six/__init__.py b/pythonforandroid/recipes/six/__init__.py index fb1ad8a3dc..512177af07 100644 --- a/pythonforandroid/recipes/six/__init__.py +++ b/pythonforandroid/recipes/six/__init__.py @@ -5,6 +5,7 @@ class SixRecipe(PythonRecipe): version = '1.9.0' url = 'https://pypi.python.org/packages/source/s/six/six-{version}.tar.gz' + depends = [('python2', 'python3', 'python3crystax')] recipe = SixRecipe() From 8f8a0503fa3151a4c7225753f1635fce45d1420c Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sun, 14 Oct 2018 20:50:26 +0100 Subject: [PATCH 04/50] Progressed the python3 recipe enough that python3 can be built --- pythonforandroid/recipes/python3/__init__.py | 63 +++++++++++++------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index 33f5b69f0f..9aba59ba75 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -2,6 +2,7 @@ from pythonforandroid.toolchain import shprint, current_directory, info from pythonforandroid.patching import (is_darwin, is_api_gt, check_all, is_api_lt, is_ndk) +from pythonforandroid.logger import logger from pythonforandroid.util import ensure_dir from os.path import exists, join, realpath from os import environ @@ -33,11 +34,10 @@ def build_arch(self, arch): with current_directory(build_dir): env = environ.copy() - # TODO: Get this information from p4a's arch system android_host = 'arm-linux-androideabi' android_build = sh.Command(join(recipe_build_dir, 'config.guess'))().stdout.strip().decode('utf-8') - platform_dir = join(self.ctx.ndk_dir, 'platforms', 'android-19', 'arch-arm') + platform_dir = join(self.ctx.ndk_dir, 'platforms', 'android-21', 'arch-arm') toolchain = '{android_host}-4.9'.format(android_host=android_host) toolchain = join(self.ctx.ndk_dir, 'toolchains', toolchain, 'prebuilt', 'linux-x86_64') CC = '{clang} -target {target} -gcc-toolchain {toolchain}'.format( @@ -58,34 +58,57 @@ def build_arch(self, arch): env['READELF'] = READELF env['STRIP'] = STRIP - ndk_flags = '--sysroot={ndk_sysroot} -D__ANDROID_API__=19 -isystem {ndk_android_host}'.format( + ndk_flags = '--sysroot={ndk_sysroot} -D__ANDROID_API__=21 -isystem {ndk_android_host}'.format( ndk_sysroot=join(self.ctx.ndk_dir, 'sysroot'), ndk_android_host=join(self.ctx.ndk_dir, 'sysroot', 'usr', 'include', android_host)) - sysroot = join(self.ctx.ndk_dir, 'platforms', 'android-19', 'arch-arm') + sysroot = join(self.ctx.ndk_dir, 'platforms', 'android-21', 'arch-arm') env['CFLAGS'] = env.get('CFLAGS', '') + ' ' + ndk_flags env['CPPFLAGS'] = env.get('CPPFLAGS', '') + ' ' + ndk_flags - env['LDFLAGS'] = env.get('LDFLAGS', '') + ' --sysroot={} -march=armv7-a -Wl,--fix-cortex-a8'.format(sysroot) + env['LDFLAGS'] = env.get('LDFLAGS', '') + ' --sysroot={} -L{}'.format(sysroot, join(sysroot, 'usr', 'lib')) + + # Manually add the libs directory, and copy some object + # files to the current directory otherwise they aren't + # picked up. This seems necessary because the --sysroot + # setting in LDFLAGS is overridden by the other flags. + # TODO: Work out why this doesn't happen in the original + # bpo-30386 Makefile system. + logger.warning('Doing some hacky stuff to link properly') + lib_dir = join(sysroot, 'usr', 'lib') + env['LDFLAGS'] += ' -L{}'.format(lib_dir) + shprint(sh.cp, join(lib_dir, 'crtbegin_so.o'), './') + shprint(sh.cp, join(lib_dir, 'crtend_so.o'), './') + + env['SYSROOT'] = sysroot print('CPPflags', env['CPPFLAGS']) print('LDFLAGS', env['LDFLAGS']) print('LD is', env['LD']) - shprint(sh.Command(join(recipe_build_dir, 'configure')), - *(' '.join(('--host={android_host}', - '--build={android_build}', - '--enable-shared', - '--disable-ipv6', - 'ac_cv_file__dev_ptmx=yes', - 'ac_cv_file__dev_ptc=no', - '--without-ensurepip', - 'ac_cv_little_endian_double=yes', - '--prefix={prefix}', - '--exec-prefix={exec_prefix}')).format( - android_host=android_host, - android_build=android_build, - prefix=sys_prefix, - exec_prefix=sys_exec_prefix)).split(' '), _env=env) + if not exists('config.status'): + shprint(sh.Command(join(recipe_build_dir, 'configure')), + *(' '.join(('--host={android_host}', + '--build={android_build}', + '--enable-shared', + '--disable-ipv6', + 'ac_cv_file__dev_ptmx=yes', + 'ac_cv_file__dev_ptc=no', + '--without-ensurepip', + 'ac_cv_little_endian_double=yes', + '--prefix={prefix}', + '--exec-prefix={exec_prefix}')).format( + android_host=android_host, + android_build=android_build, + prefix=sys_prefix, + exec_prefix=sys_exec_prefix)).split(' '), _env=env) + + import ipdb + ipdb.set_trace() + + shprint(sh.make, 'all', _env=env) + + exit(1) + # if not exists('config.status'): From a99478d10f79428579345857c7ff90a19c0c558f Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sun, 14 Oct 2018 21:16:00 +0100 Subject: [PATCH 05/50] Added a temporary hardcoded APP_PLATFORM setting --- pythonforandroid/bootstraps/sdl2/build/build.py | 2 +- .../bootstraps/sdl2/build/jni/Application.mk | 1 + pythonforandroid/recipes/python3/__init__.py | 9 --------- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/pythonforandroid/bootstraps/sdl2/build/build.py b/pythonforandroid/bootstraps/sdl2/build/build.py index 48f00bb484..898311eb39 100644 --- a/pythonforandroid/bootstraps/sdl2/build/build.py +++ b/pythonforandroid/bootstraps/sdl2/build/build.py @@ -409,7 +409,7 @@ def make_package(args): def parse_args(args=None): global BLACKLIST_PATTERNS, WHITELIST_PATTERNS, PYTHON - default_android_api = 12 + default_android_api = 21 import argparse ap = argparse.ArgumentParser(description='''\ Package a Python application for Android. diff --git a/pythonforandroid/bootstraps/sdl2/build/jni/Application.mk b/pythonforandroid/bootstraps/sdl2/build/jni/Application.mk index e79e378f94..c99aaa4061 100644 --- a/pythonforandroid/bootstraps/sdl2/build/jni/Application.mk +++ b/pythonforandroid/bootstraps/sdl2/build/jni/Application.mk @@ -5,3 +5,4 @@ # APP_ABI := armeabi armeabi-v7a x86 APP_ABI := $(ARCH) +APP_PLATFORM := android-21 diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index 9aba59ba75..36bd647fcc 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -106,14 +106,5 @@ def build_arch(self, arch): ipdb.set_trace() shprint(sh.make, 'all', _env=env) - - exit(1) - - # if not exists('config.status'): - - - # shprint(sh.make, '-C', build_dir) - - recipe = Python3Recipe() From 43ce635946d40894e331272bd075432a8d70d765 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sun, 14 Oct 2018 22:50:14 +0100 Subject: [PATCH 06/50] Made other recipe types build properly when using python3 This is just a preliminary, hacky implementation. --- .../bootstraps/sdl2/build/jni/src/Android.mk | 6 ++-- pythonforandroid/build.py | 2 +- pythonforandroid/recipe.py | 28 +++++++++++-------- pythonforandroid/recipes/python3/__init__.py | 13 ++++----- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/pythonforandroid/bootstraps/sdl2/build/jni/src/Android.mk b/pythonforandroid/bootstraps/sdl2/build/jni/src/Android.mk index 41d689d688..d8408f6019 100644 --- a/pythonforandroid/bootstraps/sdl2/build/jni/src/Android.mk +++ b/pythonforandroid/bootstraps/sdl2/build/jni/src/Android.mk @@ -12,13 +12,13 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.c \ start.c -LOCAL_CFLAGS += -I$(LOCAL_PATH)/../../../../other_builds/$(PYTHON2_NAME)/$(ARCH)/python2/python-install/include/python2.7 $(EXTRA_CFLAGS) +LOCAL_CFLAGS += -I$(LOCAL_PATH)/../../../../other_builds/$(PYTHON2_NAME)/$(ARCH)/python2/python-install/include/python2.7 $(EXTRA_CFLAGS) -I$(LOCAL_PATH)/../../../../other_builds/python3/$(ARCH)/python3/Include -I$(LOCAL_PATH)/../../../../other_builds/python3/$(ARCH)/python3/android-build LOCAL_SHARED_LIBRARIES := SDL2 python_shared -LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -llog $(EXTRA_LDLIBS) +LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -llog $(EXTRA_LDLIBS) -lpython3.7m -LOCAL_LDFLAGS += -L$(LOCAL_PATH)/../../../../other_builds/$(PYTHON2_NAME)/$(ARCH)/python2/python-install/lib $(APPLICATION_ADDITIONAL_LDFLAGS) +LOCAL_LDFLAGS += -L$(LOCAL_PATH)/../../../../other_builds/$(PYTHON2_NAME)/$(ARCH)/python2/python-install/lib $(APPLICATION_ADDITIONAL_LDFLAGS) -L$(LOCAL_PATH)/../../../../other_builds/python3/$(ARCH)/python3/android-build include $(BUILD_SHARED_LIBRARY) diff --git a/pythonforandroid/build.py b/pythonforandroid/build.py index 9a2e84c770..9546de3699 100644 --- a/pythonforandroid/build.py +++ b/pythonforandroid/build.py @@ -355,7 +355,7 @@ def prepare_build_environment(self, user_sdk_dir, user_ndk_dir, if not self.ccache: info('ccache is missing, the build will not be optimized in the ' 'future.') - for cython_fn in ("cython2", "cython-2.7", "cython"): + for cython_fn in ("cython", "cython3", "cython2", "cython-2.7"): cython = sh.which(cython_fn) if cython: self.cython = cython diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index d5ea9df267..a47dfb54b1 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -742,6 +742,9 @@ def real_hostpython_location(self): return join( Recipe.get_recipe('hostpython3crystax', self.ctx).get_build_dir(), 'hostpython') + elif 'hostpython3' in self.ctx.recipe_build_order: + return join(Recipe.get_recipe('hostpython3', self.ctx).get_build_dir(), + 'native-build', 'python') else: python_recipe = self.ctx.python_recipe return 'python{}'.format(python_recipe.version) @@ -785,15 +788,15 @@ def get_recipe_env(self, arch=None, with_flags_in_cc=True): join(ndk_dir_python, 'libs', arch.arch)) env['LDFLAGS'] += ' -lpython{}m'.format(python_short_version) elif 'python3' in self.ctx.recipe_build_order: - # This headers are unused cause python3 recipe was removed - # TODO: should be reviewed when python3 recipe added - env['PYTHON_ROOT'] = self.ctx.get_python_install_dir() - env['CFLAGS'] += ' -I' + env[ - 'PYTHON_ROOT'] + '/include/python{}m'.format( - python_short_version) + # TODO: Make the recipe env get these details from the + # python recipe instead of hardcoding + env['PYTHON_ROOT'] = Recipe.get_recipe('python3', self.ctx).get_build_dir(arch.arch) + env['CFLAGS'] += ' -I' + env['PYTHON_ROOT'] + '/Include' env['LDFLAGS'] += ( - ' -L' + env['PYTHON_ROOT'] + '/lib' + - ' -lpython{}m'.format(python_short_version)) + ' -L' + + join(env['PYTHON_ROOT'], 'android-build') + + ' -lpython3.7m') + hppath = [] hppath.append(join(dirname(self.hostpython_location), 'Lib')) hppath.append(join(hppath[0], 'site-packages')) @@ -834,7 +837,8 @@ def install_python_package(self, arch, name=None, env=None, is_dir=True): with current_directory(self.get_build_dir(arch.arch)): hostpython = sh.Command(self.hostpython_location) - if self.ctx.python_recipe.from_crystax: + if (self.ctx.python_recipe.from_crystax or + self.ctx.python_recipe.name == 'python3'): hpenv = env.copy() shprint(hostpython, 'setup.py', 'install', '-O2', '--root={}'.format(self.ctx.get_python_install_dir()), @@ -1016,7 +1020,7 @@ def build_cython_components(self, arch): shprint(sh.find, build_lib[0], '-name', '*.o', '-exec', env['STRIP'], '{}', ';', _env=env) - if 'python3crystax' in self.ctx.recipe_build_order: + else: # python3crystax or python3 info('Stripping object files') shprint(sh.find, '.', '-iname', '*.so', '-exec', '/usr/bin/echo', '{}', ';', _env=env) @@ -1060,8 +1064,8 @@ def get_recipe_env(self, arch, with_flags_in_cc=True): if self.ctx.python_recipe.from_crystax: env['LDFLAGS'] = (env['LDFLAGS'] + ' -L{}'.format(join(self.ctx.bootstrap.build_dir, 'libs', arch.arch))) - # ' -L/home/asandy/.local/share/python-for-android/build/bootstrap_builds/sdl2/libs/armeabi ' - if self.ctx.python_recipe.from_crystax: + + if self.ctx.python_recipe.from_crystax or self.ctx.python_recipe.name == 'python3': env['LDSHARED'] = env['CC'] + ' -shared' else: env['LDSHARED'] = join(self.ctx.root_dir, 'tools', 'liblink.sh') diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index 36bd647fcc..8898cd95f8 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -80,11 +80,6 @@ def build_arch(self, arch): env['SYSROOT'] = sysroot - print('CPPflags', env['CPPFLAGS']) - print('LDFLAGS', env['LDFLAGS']) - - print('LD is', env['LD']) - if not exists('config.status'): shprint(sh.Command(join(recipe_build_dir, 'configure')), *(' '.join(('--host={android_host}', @@ -102,9 +97,11 @@ def build_arch(self, arch): prefix=sys_prefix, exec_prefix=sys_exec_prefix)).split(' '), _env=env) - import ipdb - ipdb.set_trace() + if not exists('python'): + shprint(sh.make, 'all', _env=env) - shprint(sh.make, 'all', _env=env) + # TODO: Look into passing the path to pyconfig.h in a + # better way, although this is probably acceptable + sh.cp('pyconfig.h', join(recipe_build_dir, 'Include')) recipe = Python3Recipe() From 3a09e10f53cf919f5f69bfe35e5a21bf6f38e690 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Mon, 15 Oct 2018 22:39:17 +0100 Subject: [PATCH 07/50] Made bootstrap collation work with python3 recipe --- pythonforandroid/bootstrap.py | 12 +++- pythonforandroid/bootstraps/sdl2/__init__.py | 72 ++++++++++++++++--- .../java/org/kivy/android/PythonUtil.java | 4 +- 3 files changed, 75 insertions(+), 13 deletions(-) diff --git a/pythonforandroid/bootstrap.py b/pythonforandroid/bootstrap.py index ef89fef829..a7266ff265 100644 --- a/pythonforandroid/bootstrap.py +++ b/pythonforandroid/bootstrap.py @@ -254,9 +254,15 @@ def strip_libraries(self, arch): warning('Can\'t find strip in PATH...') return strip = sh.Command(strip) - filens = shprint(sh.find, join(self.dist_dir, 'private'), - join(self.dist_dir, 'libs'), - '-iname', '*.so', _env=env).stdout.decode('utf-8') + + if self.ctx.python_recipe.name == 'python2': + filens = shprint(sh.find, join(self.dist_dir, 'private'), + join(self.dist_dir, 'libs'), + '-iname', '*.so', _env=env).stdout.decode('utf-8') + else: + filens = shprint(sh.find, join(self.dist_dir, '_python_bundle', '_python_bundle', 'modules'), + join(self.dist_dir, 'libs'), + '-iname', '*.so', _env=env).stdout.decode('utf-8') logger.info('Stripping libraries in private dir') for filen in filens.split('\n'): try: diff --git a/pythonforandroid/bootstraps/sdl2/__init__.py b/pythonforandroid/bootstraps/sdl2/__init__.py index b8c4bbeff8..a7b0833a5a 100644 --- a/pythonforandroid/bootstraps/sdl2/__init__.py +++ b/pythonforandroid/bootstraps/sdl2/__init__.py @@ -3,6 +3,7 @@ from pythonforandroid.util import ensure_dir from os.path import join, exists, curdir, abspath from os import walk +import os import glob import sh @@ -11,7 +12,7 @@ class SDL2GradleBootstrap(Bootstrap): - name = 'sdl2_gradle' + name = 'sdl2' recipe_depends = ['sdl2', ('python2', 'python3', 'python3crystax')] @@ -23,6 +24,8 @@ def run_distribute(self): from_crystax = self.ctx.python_recipe.from_crystax crystax_python_dir = join("crystax_python", "crystax_python") + python_bundle_dir = join('_python_bundle', '_python_bundle') + if len(self.ctx.archs) > 1: raise ValueError("SDL2/gradle support only one arch") @@ -30,29 +33,32 @@ def run_distribute(self): shprint(sh.rm, "-rf", self.dist_dir) shprint(sh.cp, "-r", self.build_dir, self.dist_dir) - # either the build use environemnt variable (ANDROID_HOME) + # either the build use environment variable (ANDROID_HOME) # or the local.properties if exists with current_directory(self.dist_dir): with open('local.properties', 'w') as fileh: fileh.write('sdk.dir={}'.format(self.ctx.sdk_dir)) + # TODO: Move the packaged python building to the python recipes with current_directory(self.dist_dir): info("Copying Python distribution") - if not exists("private") and not from_crystax: + if 'python2' in self.ctx.recipe_build_order: ensure_dir("private") - if not exists("crystax_python") and from_crystax: + elif not exists("crystax_python") and from_crystax: ensure_dir(crystax_python_dir) + elif 'python3' in self.ctx.recipe_build_order: + ensure_dir(python_bundle_dir) hostpython = sh.Command(self.ctx.hostpython) - if not from_crystax: + if self.ctx.python_recipe.name == 'python2': try: shprint(hostpython, '-OO', '-m', 'compileall', python_install_dir, _tail=10, _filterout="^Listing") except sh.ErrorReturnCode: pass - if not exists('python-install'): + if 'python2' in self.ctx.recipe_build_order and not exists('python-install'): shprint( sh.cp, '-a', python_install_dir, './python-install') @@ -60,7 +66,7 @@ def run_distribute(self): self.distribute_javaclasses(self.ctx.javaclass_dir, dest_dir=join("src", "main", "java")) - if not from_crystax: + if self.ctx.python_recipe.name == 'python2': info("Filling private directory") if not exists(join("private", "lib")): info("private/lib does not exist, making") @@ -100,7 +106,55 @@ def run_distribute(self): shprint(sh.rm, '-f', filename) shprint(sh.rm, '-rf', 'config/python.o') - else: # Python *is* loaded from crystax + elif self.ctx.python_recipe.name == 'python3': + ndk_dir = self.ctx.ndk_dir + py_recipe = self.ctx.python_recipe + + ## Build the python bundle: + + # Bundle compiled python modules to a folder + modules_dir = join(python_bundle_dir, 'modules') + ensure_dir(modules_dir) + + modules_build_dir = join( + self.ctx.python_recipe.get_build_dir(arch.arch), + 'android-build', + 'lib.linux-arm-3.7') + module_filens = (glob.glob(join(modules_build_dir, '*.so')) + + glob.glob(join(modules_build_dir, '*.py'))) + for filen in module_filens: + shprint(sh.cp, filen, modules_dir) + + # zip up the standard library + stdlib_zip = join(self.dist_dir, python_bundle_dir, 'stdlib.zip') + with current_directory( + join(self.ctx.python_recipe.get_build_dir(arch.arch), + 'Lib')): + shprint(sh.zip, '-r', stdlib_zip, *os.listdir()) + + # copy the site-packages into place + shprint(sh.cp, '-r', self.ctx.get_python_install_dir(), + join(python_bundle_dir, 'site-packages')) + + # copy the python .so files into place + python_build_dir = join(py_recipe.get_build_dir(arch.arch), + 'android-build') + shprint(sh.cp, join(python_build_dir, 'libpython3.7m.so'), + 'libs/{}'.format(arch.arch)) + shprint(sh.cp, join(python_build_dir, 'libpython3.7m.so.1.0'), + 'libs/{}'.format(arch.arch)) + + info('Renaming .so files to reflect cross-compile') + site_packages_dir = join(python_bundle_dir, 'site-packages') + py_so_files = shprint(sh.find, site_packages_dir, '-iname', '*.so') + filens = py_so_files.stdout.decode('utf-8').split('\n')[:-1] + for filen in filens: + parts = filen.split('.') + if len(parts) <= 2: + continue + shprint(sh.mv, filen, parts[0] + '.so') + + elif self.ctx.python_recipe.from_crystax: # Python *is* loaded from crystax ndk_dir = self.ctx.ndk_dir py_recipe = self.ctx.python_recipe python_dir = join(ndk_dir, 'sources', 'python', @@ -129,7 +183,7 @@ def run_distribute(self): fileh.write('\nsqlite3/*\nlib-dynload/_sqlite3.so\n') self.strip_libraries(arch) - self.fry_eggs(site_packages_dir) + # self.fry_eggs(site_packages_dir) # TODO uncomment this and make it work with python3 super(SDL2GradleBootstrap, self).run_distribute() diff --git a/pythonforandroid/bootstraps/sdl2/build/src/main/java/org/kivy/android/PythonUtil.java b/pythonforandroid/bootstraps/sdl2/build/src/main/java/org/kivy/android/PythonUtil.java index 6574dae787..978a843fe6 100644 --- a/pythonforandroid/bootstraps/sdl2/build/src/main/java/org/kivy/android/PythonUtil.java +++ b/pythonforandroid/bootstraps/sdl2/build/src/main/java/org/kivy/android/PythonUtil.java @@ -45,6 +45,8 @@ protected static ArrayList getLibraries(File filesDir) { addLibraryIfExists(libsList, "crypto.*", libsDir); libsList.add("python2.7"); libsList.add("python3.5m"); + libsList.add("python3.6m"); + libsList.add("python3.7m"); libsList.add("main"); return libsList; } @@ -66,7 +68,7 @@ public static void loadLibraries(File filesDir) { // load, and it has failed, give a more // general error Log.v(TAG, "Library loading error: " + e.getMessage()); - if (lib.startsWith("python3.6") && !foundPython) { + if (lib.startsWith("python3.7") && !foundPython) { throw new java.lang.RuntimeException("Could not load any libpythonXXX.so"); } else if (lib.startsWith("python")) { continue; From 4a78c134b752946a7fb4e2512dbef8bcf30b0ec7 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Tue, 16 Oct 2018 22:07:42 +0100 Subject: [PATCH 08/50] Fixed bootstrap code to correctly load and run a python3 bundle --- pythonforandroid/bootstraps/sdl2/__init__.py | 1 + .../bootstraps/sdl2/build/build.py | 4 +- .../bootstraps/sdl2/build/jni/src/start.c | 78 +++++++++++++------ testapps/setup_testapp_python3.py | 4 +- 4 files changed, 59 insertions(+), 28 deletions(-) diff --git a/pythonforandroid/bootstraps/sdl2/__init__.py b/pythonforandroid/bootstraps/sdl2/__init__.py index a7b0833a5a..7289eaab8a 100644 --- a/pythonforandroid/bootstraps/sdl2/__init__.py +++ b/pythonforandroid/bootstraps/sdl2/__init__.py @@ -119,6 +119,7 @@ def run_distribute(self): modules_build_dir = join( self.ctx.python_recipe.get_build_dir(arch.arch), 'android-build', + 'build', 'lib.linux-arm-3.7') module_filens = (glob.glob(join(modules_build_dir, '*.so')) + glob.glob(join(modules_build_dir, '*.py'))) diff --git a/pythonforandroid/bootstraps/sdl2/build/build.py b/pythonforandroid/bootstraps/sdl2/build/build.py index 898311eb39..1363342484 100644 --- a/pythonforandroid/bootstraps/sdl2/build/build.py +++ b/pythonforandroid/bootstraps/sdl2/build/build.py @@ -125,7 +125,7 @@ def make_python_zip(): if not exists('private'): print('No compiled python is present to zip, skipping.') - print('this should only be the case if you are using the CrystaX python') + print('this should only be the case if you are using the CrystaX python or python3') return global python_files @@ -243,6 +243,8 @@ def make_package(args): tar_dirs.append('private') if exists('crystax_python'): tar_dirs.append('crystax_python') + if exists('_python_bundle'): + tar_dirs.append('_python_bundle') if args.private: make_tar('src/main/assets/private.mp3', tar_dirs, args.ignore_path) diff --git a/pythonforandroid/bootstraps/sdl2/build/jni/src/start.c b/pythonforandroid/bootstraps/sdl2/build/jni/src/start.c index 121d925d99..9816cd45cd 100644 --- a/pythonforandroid/bootstraps/sdl2/build/jni/src/start.c +++ b/pythonforandroid/bootstraps/sdl2/build/jni/src/start.c @@ -76,10 +76,9 @@ int main(int argc, char *argv[]) { int ret = 0; FILE *fd; - /* AND: Several filepaths are hardcoded here, these must be made - configurable */ - /* AND: P4A uses env vars...not sure what's best */ - LOGP("Initialize Python for Android"); + setenv("P4A_BOOTSTRAP", "SDL2", 1); // env var to identify p4a to applications + + LOGP("Initializing Python for Android"); env_argument = getenv("ANDROID_ARGUMENT"); setenv("ANDROID_APP_PATH", env_argument, 1); env_entrypoint = getenv("ANDROID_ENTRYPOINT"); @@ -109,32 +108,47 @@ int main(int argc, char *argv[]) { LOGP("Preparing to initialize python"); + // Set up the python path + char paths[256]; + char crystax_python_dir[256]; snprintf(crystax_python_dir, 256, "%s/crystax_python", getenv("ANDROID_UNPACK")); - if (dir_exists(crystax_python_dir)) { - LOGP("crystax_python exists"); - char paths[256]; - snprintf(paths, 256, - "%s/stdlib.zip:%s/modules", - crystax_python_dir, crystax_python_dir); - /* snprintf(paths, 256, "%s/stdlib.zip:%s/modules", env_argument, - * env_argument); */ - LOGP("calculated paths to be..."); - LOGP(paths); + char python_bundle_dir[256]; + snprintf(python_bundle_dir, 256, + "%s/_python_bundle", getenv("ANDROID_UNPACK")); + if (dir_exists(crystax_python_dir) || dir_exists(python_bundle_dir)) { + if (dir_exists(crystax_python_dir)) { + LOGP("crystax_python exists"); + snprintf(paths, 256, + "%s/stdlib.zip:%s/modules", + crystax_python_dir, crystax_python_dir); + LOGP("calculated paths to be..."); + LOGP(paths); + } + + if (dir_exists(python_bundle_dir)) { + LOGP("_python_bundle dir exists"); + snprintf(paths, 256, + "%s/stdlib.zip:%s/modules", + python_bundle_dir, python_bundle_dir); + LOGP("calculated paths to be..."); + LOGP(paths); + } -#if PY_MAJOR_VERSION >= 3 - wchar_t *wchar_paths = Py_DecodeLocale(paths, NULL); - Py_SetPath(wchar_paths); -#else - char *wchar_paths = paths; - LOGP("Can't Py_SetPath in python2, so crystax python2 doesn't work yet"); - exit(1); -#endif - LOGP("set wchar paths..."); + #if PY_MAJOR_VERSION >= 3 + wchar_t *wchar_paths = Py_DecodeLocale(paths, NULL); + Py_SetPath(wchar_paths); + #else + char *wchar_paths = paths; + LOGP("Can't Py_SetPath in python2, so crystax python2 doesn't work yet"); + exit(1); + #endif + + LOGP("set wchar paths..."); } else { - LOGP("crystax_python does not exist"); + LOGP("crystax_python does not exist"); } Py_Initialize(); @@ -174,8 +188,8 @@ int main(int argc, char *argv[]) { " argument ]\n"); } + char add_site_packages_dir[256]; if (dir_exists(crystax_python_dir)) { - char add_site_packages_dir[256]; snprintf(add_site_packages_dir, 256, "sys.path.append('%s/site-packages')", crystax_python_dir); @@ -188,6 +202,19 @@ int main(int argc, char *argv[]) { PyRun_SimpleString("sys.path = ['.'] + sys.path"); } + if (dir_exists(python_bundle_dir)) { + snprintf(add_site_packages_dir, 256, + "sys.path.append('%s/site-packages')", + python_bundle_dir); + + PyRun_SimpleString("import sys\n" + "sys.argv = ['notaninterpreterreally']\n" + "from os.path import realpath, join, dirname"); + PyRun_SimpleString(add_site_packages_dir); + /* "sys.path.append(join(dirname(realpath(__file__)), 'site-packages'))") */ + PyRun_SimpleString("sys.path = ['.'] + sys.path"); + } + PyRun_SimpleString( "class LogFile(object):\n" " def __init__(self):\n" @@ -317,6 +344,7 @@ JNIEXPORT void JNICALL Java_org_kivy_android_PythonService_nativeStart( setenv("PYTHONHOME", python_home, 1); setenv("PYTHONPATH", python_path, 1); setenv("PYTHON_SERVICE_ARGUMENT", arg, 1); + setenv("P4A_BOOTSTRAP", "SDL2", 1); char *argv[] = {"."}; /* ANDROID_ARGUMENT points to service subdir, diff --git a/testapps/setup_testapp_python3.py b/testapps/setup_testapp_python3.py index bd7a22ffd0..1bc04324d2 100644 --- a/testapps/setup_testapp_python3.py +++ b/testapps/setup_testapp_python3.py @@ -6,7 +6,7 @@ 'requirements': 'sdl2,pyjnius,kivy,python3', 'android-api': 19, 'ndk-dir': '/home/asandy/android/crystax-ndk-10.3.2', - 'dist-name': 'bdisttest_python3', + 'dist-name': 'bdisttest_python3_googlendk', 'ndk-version': '10.3.2', 'arch': 'armeabi-v7a', 'permission': 'VIBRATE', @@ -20,7 +20,7 @@ print('packages are', packages) setup( - name='testapp_python3', + name='testapp_python3_googlendk', version='1.1', description='p4a setup.py test', author='Alexander Taylor', From 1d22bdd7c80fee1de40451bda247fe6cfa2bdb25 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Wed, 17 Oct 2018 22:53:59 +0100 Subject: [PATCH 09/50] Did initial implementation of an ndk-api build target option --- pythonforandroid/archs.py | 1 + .../bootstraps/sdl2/build/jni/Application.mk | 2 +- .../bootstraps/sdl2/build/jni/src/Android.mk | 6 +++--- pythonforandroid/build.py | 10 ++++++++-- pythonforandroid/recipe.py | 11 ++++++++-- pythonforandroid/recipes/python3/__init__.py | 8 ++++++++ pythonforandroid/recipes/sdl2/__init__.py | 9 +++++++++ pythonforandroid/toolchain.py | 20 ++++++++++++++----- 8 files changed, 54 insertions(+), 13 deletions(-) diff --git a/pythonforandroid/archs.py b/pythonforandroid/archs.py index aae2d1e9de..ad77953f83 100644 --- a/pythonforandroid/archs.py +++ b/pythonforandroid/archs.py @@ -133,6 +133,7 @@ def get_env(self, with_flags_in_cc=True): env['PATH'] = environ['PATH'] env['ARCH'] = self.arch + env['NDK_API'] = str(self.ctx.ndk_api) if self.ctx.python_recipe and self.ctx.python_recipe.from_crystax: env['CRYSTAX_PYTHON_VERSION'] = self.ctx.python_recipe.version diff --git a/pythonforandroid/bootstraps/sdl2/build/jni/Application.mk b/pythonforandroid/bootstraps/sdl2/build/jni/Application.mk index c99aaa4061..15598537ca 100644 --- a/pythonforandroid/bootstraps/sdl2/build/jni/Application.mk +++ b/pythonforandroid/bootstraps/sdl2/build/jni/Application.mk @@ -5,4 +5,4 @@ # APP_ABI := armeabi armeabi-v7a x86 APP_ABI := $(ARCH) -APP_PLATFORM := android-21 +APP_PLATFORM := $(NDK_API) diff --git a/pythonforandroid/bootstraps/sdl2/build/jni/src/Android.mk b/pythonforandroid/bootstraps/sdl2/build/jni/src/Android.mk index d8408f6019..be2bb9b27a 100644 --- a/pythonforandroid/bootstraps/sdl2/build/jni/src/Android.mk +++ b/pythonforandroid/bootstraps/sdl2/build/jni/src/Android.mk @@ -12,13 +12,13 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.c \ start.c -LOCAL_CFLAGS += -I$(LOCAL_PATH)/../../../../other_builds/$(PYTHON2_NAME)/$(ARCH)/python2/python-install/include/python2.7 $(EXTRA_CFLAGS) -I$(LOCAL_PATH)/../../../../other_builds/python3/$(ARCH)/python3/Include -I$(LOCAL_PATH)/../../../../other_builds/python3/$(ARCH)/python3/android-build +LOCAL_CFLAGS += -I$(PYTHON_INCLUDE_ROOT) $(EXTRA_CFLAGS) LOCAL_SHARED_LIBRARIES := SDL2 python_shared -LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -llog $(EXTRA_LDLIBS) -lpython3.7m +LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -llog $(EXTRA_LDLIBS) -LOCAL_LDFLAGS += -L$(LOCAL_PATH)/../../../../other_builds/$(PYTHON2_NAME)/$(ARCH)/python2/python-install/lib $(APPLICATION_ADDITIONAL_LDFLAGS) -L$(LOCAL_PATH)/../../../../other_builds/python3/$(ARCH)/python3/android-build +LOCAL_LDFLAGS += -L$(PYTHON_LINK_ROOT) $(APPLICATION_ADDITIONAL_LDFLAGS) include $(BUILD_SHARED_LIBRARY) diff --git a/pythonforandroid/build.py b/pythonforandroid/build.py index 9546de3699..8044025b42 100644 --- a/pythonforandroid/build.py +++ b/pythonforandroid/build.py @@ -163,8 +163,12 @@ def ndk_dir(self): def ndk_dir(self, value): self._ndk_dir = value - def prepare_build_environment(self, user_sdk_dir, user_ndk_dir, - user_android_api, user_ndk_ver): + def prepare_build_environment(self, + user_sdk_dir, + user_ndk_dir, + user_android_api, + user_ndk_ver, + user_ndk_api): '''Checks that build dependencies exist and sets internal variables for the Android SDK etc. @@ -335,6 +339,8 @@ def prepare_build_environment(self, user_sdk_dir, user_ndk_dir, 'set it with `--ndk-version=...`.') self.ndk_ver = ndk_ver + self.ndk_api = user_ndk_api + info('Using {} NDK {}'.format(self.ndk.capitalize(), self.ndk_ver)) virtualenv = None diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index a47dfb54b1..52b95c4fb1 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -277,7 +277,8 @@ def get_build_container_dir(self, arch): alternative or optional dependencies are being built. ''' dir_name = self.get_dir_name() - return join(self.ctx.build_dir, 'other_builds', dir_name, arch) + return join(self.ctx.build_dir, 'other_builds', + dir_name, '{}__ndk_target_{}'.format(arch, self.ctx.ndk_api)) def get_dir_name(self): choices = self.check_recipe_choices() @@ -1084,7 +1085,6 @@ def get_recipe_env(self, arch, with_flags_in_cc=True): return env - class TargetPythonRecipe(Recipe): '''Class for target python recipes. Sets ctx.python_recipe to point to itself, so as to know later what kind of Python was built or used.''' @@ -1105,6 +1105,13 @@ def prebuild_arch(self, arch): exit(1) self.ctx.python_recipe = self + def include_root(self, arch): + '''The root directory from which to include headers.''' + raise NotImplementedError('Not implemented in TargetPythonRecipe') + + def link_root(self): + raise NotImplementedError('Not implemented in TargetPythonRecipe') + # @property # def ctx(self): # return self._ctx diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index 8898cd95f8..ad21a52d47 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -103,5 +103,13 @@ def build_arch(self, arch): # TODO: Look into passing the path to pyconfig.h in a # better way, although this is probably acceptable sh.cp('pyconfig.h', join(recipe_build_dir, 'Include')) + + def include_root(self, arch_name): + return join(self.get_build_dir(arch_name), + 'Include') + + def link_root(self, arch_name): + return join(self.get_build_dir(arch_name), + 'android-build') recipe = Python3Recipe() diff --git a/pythonforandroid/recipes/sdl2/__init__.py b/pythonforandroid/recipes/sdl2/__init__.py index 59b91dac6d..97e21e0b82 100644 --- a/pythonforandroid/recipes/sdl2/__init__.py +++ b/pythonforandroid/recipes/sdl2/__init__.py @@ -17,11 +17,20 @@ class LibSDL2Recipe(BootstrapNDKRecipe): def get_recipe_env(self, arch=None): env = super(LibSDL2Recipe, self).get_recipe_env(arch) + py2 = self.get_recipe('python2', arch.ctx) env['PYTHON2_NAME'] = py2.get_dir_name() + py3 = self.get_recipe('python3', arch.ctx) + + env['PYTHON_INCLUDE_ROOT'] = self.ctx.python_recipe.include_root(arch.arch) + env['PYTHON_LINK_ROOT'] = self.ctx.python_recipe.link_root(arch.arch) + if 'python2' in self.ctx.recipe_build_order: env['EXTRA_LDLIBS'] = ' -lpython2.7' + if 'python3' in self.ctx.recipe_build_order: + env['EXTRA_LDLIBS'] = ' -lpython3.7m' # TODO: don't hardcode the python version + env['APP_ALLOW_MISSING_DEPS'] = 'true' return env diff --git a/pythonforandroid/toolchain.py b/pythonforandroid/toolchain.py index 18f69cf9f2..5f08bcf1b1 100644 --- a/pythonforandroid/toolchain.py +++ b/pythonforandroid/toolchain.py @@ -137,7 +137,8 @@ def wrapper_func(self, args): ctx.prepare_build_environment(user_sdk_dir=self.sdk_dir, user_ndk_dir=self.ndk_dir, user_android_api=self.android_api, - user_ndk_ver=self.ndk_version) + user_ndk_ver=self.ndk_version, + user_ndk_api=self.ndk_api) dist = self._dist if dist.needs_build: info_notify('No dist exists that meets your requirements, ' @@ -257,8 +258,14 @@ def __init__(self): help=('The version of the Android NDK. This is optional: ' 'we try to work it out automatically from the ndk_dir.')) generic_parser.add_argument( - '--symlink-java-src', '--symlink_java_src', action='store_true', - dest='symlink_java_src', default=False, + '--ndk-api', type=int, default=21, + help=('The Android API level to compile against. This should be your ' + '*minimal supported* API, not normally the same as your --android-api.')) + generic_parser.add_argument( + '--symlink-java-src', '--symlink_java_src', + action='store_true', + dest='symlink_java_src', + default=False, help=('If True, symlinks the java src folder during build and dist ' 'creation. This is useful for development only, it could also' ' cause weird problems.')) @@ -520,6 +527,7 @@ def add_parser(subparsers, *args, **kwargs): self.ndk_dir = args.ndk_dir self.android_api = args.android_api self.ndk_version = args.ndk_version + self.ndk_api = args.ndk_api self.ctx.symlink_java_src = args.symlink_java_src self.ctx.java_build_tool = args.java_build_tool @@ -928,7 +936,8 @@ def sdk_tools(self, args): ctx.prepare_build_environment(user_sdk_dir=self.sdk_dir, user_ndk_dir=self.ndk_dir, user_android_api=self.android_api, - user_ndk_ver=self.ndk_version) + user_ndk_ver=self.ndk_version, + user_ndk_api=self.ndk_api) android = sh.Command(join(ctx.sdk_dir, 'tools', args.tool)) output = android( *args.unknown_args, _iter=True, _out_bufsize=1, _err_to_out=True) @@ -955,7 +964,8 @@ def _adb(self, commands): ctx.prepare_build_environment(user_sdk_dir=self.sdk_dir, user_ndk_dir=self.ndk_dir, user_android_api=self.android_api, - user_ndk_ver=self.ndk_version) + user_ndk_ver=self.ndk_version, + user_ndk_api=self.ndk_api) if platform in ('win32', 'cygwin'): adb = sh.Command(join(ctx.sdk_dir, 'platform-tools', 'adb.exe')) else: From 9491855e2fc8c457565e0de8d515eb77b3b471bf Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Thu, 18 Oct 2018 22:05:39 +0100 Subject: [PATCH 10/50] Made bootstraps check the ndk api against the requested minsdk --- pythonforandroid/bootstrap.py | 5 +-- .../bootstraps/sdl2/build/build.py | 28 +++++++++++- pythonforandroid/distribution.py | 45 ++++++++++--------- 3 files changed, 51 insertions(+), 27 deletions(-) diff --git a/pythonforandroid/bootstrap.py b/pythonforandroid/bootstrap.py index a7266ff265..5f76989a43 100644 --- a/pythonforandroid/bootstrap.py +++ b/pythonforandroid/bootstrap.py @@ -106,15 +106,14 @@ def prepare_dist_dir(self, name): ensure_dir(self.dist_dir) def run_distribute(self): - # print('Default bootstrap being used doesn\'t know how ' - # 'to distribute...failing.') - # exit(1) + # TODO: Move this to Distribution.save_info with current_directory(self.dist_dir): info('Saving distribution info') with open('dist_info.json', 'w') as fileh: json.dump({'dist_name': self.ctx.dist_name, 'bootstrap': self.ctx.bootstrap.name, 'archs': [arch.arch for arch in self.ctx.archs], + 'ndk_api': self.ctx.ndk_api, 'recipes': self.ctx.recipe_build_order + self.ctx.python_modules}, fileh) diff --git a/pythonforandroid/bootstraps/sdl2/build/build.py b/pythonforandroid/bootstraps/sdl2/build/build.py index 1363342484..b360758f75 100644 --- a/pythonforandroid/bootstraps/sdl2/build/build.py +++ b/pythonforandroid/bootstraps/sdl2/build/build.py @@ -8,6 +8,7 @@ import os import tarfile import time +import json import subprocess import shutil from zipfile import ZipFile @@ -497,6 +498,10 @@ def parse_args(args=None): help=('Minimum Android SDK version to use. Default to ' 'the value of ANDROIDAPI, or {} if not set' .format(default_android_api))) + ap.add_argument('--allow-minsdk-ndkapi-mismatch', default=False, + action='store_true', + help=('Allow the --minsdk argument to be different from ' + 'the discovered ndk_api in the dist')) ap.add_argument('--intent-filters', dest='intent_filters', help=('Add intent-filters xml rules to the ' 'AndroidManifest.xml file. The argument is a ' @@ -531,8 +536,27 @@ def parse_args(args=None): if args.name and args.name[0] == '"' and args.name[-1] == '"': args.name = args.name[1:-1] - # if args.sdk_version == -1: - # args.sdk_version = args.min_sdk_version + with open('dist_info.json', 'r') as fileh: + info = json.load(fileh) + if 'ndk_api' not in info: + print('Failed to read ndk_api from dist info') + ndk_api = args.min_sdk_version + else: + ndk_api = info['ndk_api'] + if ndk_api != args.min_sdk_version: + print(('WARNING: --minsdk argument does not match the api that is ' + 'compiled against. Only proceed if you know what you are ' + 'doing, otherwise use --minsdk={} or recompile against api ' + '{}').format(ndk_api, args.min_sdk_version)) + if not args.allow_minsdk_ndkapi_mismatch: + print('You must pass --allow-minsdk-ndkapi-mismatch to build ' + 'with --minsdk different to the target NDK api from the ' + 'build step') + exit(1) + else: + print('Proceeding with --minsdk not matching build target api') + + if args.sdk_version != -1: print('WARNING: Received a --sdk argument, but this argument is ' diff --git a/pythonforandroid/distribution.py b/pythonforandroid/distribution.py index 735b64a79f..02de2f139c 100644 --- a/pythonforandroid/distribution.py +++ b/pythonforandroid/distribution.py @@ -182,28 +182,29 @@ def get_distributions(cls, ctx, extra_dist_dirs=[]): dists.append(dist) return dists - def save_info(self): - ''' - Save information about the distribution in its dist_dir. - ''' - with current_directory(self.dist_dir): - info('Saving distribution info') - with open('dist_info.json', 'w') as fileh: - json.dump({'dist_name': self.name, - 'archs': [arch.arch for arch in self.ctx.archs], - 'recipes': self.ctx.recipe_build_order}, - fileh) - - def load_info(self): - '''Load information about the dist from the info file that p4a - automatically creates.''' - with current_directory(self.dist_dir): - filen = 'dist_info.json' - if not exists(filen): - return None - with open('dist_info.json', 'r') as fileh: - dist_info = json.load(fileh) - return dist_info + # def save_info(self): + # ''' + # Save information about the distribution in its dist_dir. + # ''' + # with current_directory(self.dist_dir): + # info('Saving distribution info') + # with open('dist_info.json', 'w') as fileh: + # json.dump({'dist_name': self.name, + # 'archs': [arch.arch for arch in self.ctx.archs], + # 'ndk_api': self.ctx.ndk_api, + # 'recipes': self.ctx.recipe_build_order}, + # fileh) + + # def load_info(self): + # '''Load information about the dist from the info file that p4a + # automatically creates.''' + # with current_directory(self.dist_dir): + # filen = 'dist_info.json' + # if not exists(filen): + # return None + # with open('dist_info.json', 'r') as fileh: + # dist_info = json.load(fileh) + # return dist_info def pretty_log_dists(dists, log_func=info): From 9423cc809ce32f1d87e66244d7eff2606a8c3c6c Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Thu, 18 Oct 2018 22:34:49 +0100 Subject: [PATCH 11/50] Fixed APP_PLATFORM setting --- pythonforandroid/archs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/archs.py b/pythonforandroid/archs.py index ad77953f83..864c41f1cb 100644 --- a/pythonforandroid/archs.py +++ b/pythonforandroid/archs.py @@ -133,7 +133,7 @@ def get_env(self, with_flags_in_cc=True): env['PATH'] = environ['PATH'] env['ARCH'] = self.arch - env['NDK_API'] = str(self.ctx.ndk_api) + env['NDK_API'] = 'android-{}'.format(str(self.ctx.ndk_api)) if self.ctx.python_recipe and self.ctx.python_recipe.from_crystax: env['CRYSTAX_PYTHON_VERSION'] = self.ctx.python_recipe.version From 545aa02850879e0a58fb8f7b6d77281ee42a23b2 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Thu, 18 Oct 2018 22:42:33 +0100 Subject: [PATCH 12/50] Moved from bpo-30386 branch to python 3.7.0 official distribution --- pythonforandroid/recipes/hostpython3/__init__.py | 4 ++-- pythonforandroid/recipes/python3/__init__.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pythonforandroid/recipes/hostpython3/__init__.py b/pythonforandroid/recipes/hostpython3/__init__.py index 5bde9da70a..b0a44a3088 100644 --- a/pythonforandroid/recipes/hostpython3/__init__.py +++ b/pythonforandroid/recipes/hostpython3/__init__.py @@ -6,8 +6,8 @@ class Hostpython3Recipe(Recipe): - version = 'bpo-30386' - url = 'https://github.com/inclement/cpython/archive/{version}.zip' + version = '3.7.0' + url = 'https://www.python.org/ftp/python/3.7.0/Python-{version}.tgz' name = 'hostpython3' conflicts = ['hostpython2'] diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index ad21a52d47..01c1bdcadb 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -10,8 +10,8 @@ class Python3Recipe(TargetPythonRecipe): - version = 'bpo-30386' - url = 'https://github.com/inclement/cpython/archive/{version}.zip' + version = '3.7.0' + url = 'https://www.python.org/ftp/python/3.7.0/Python-{version}.tgz' name = 'python3' depends = ['hostpython3'] From 5a4d443700f105e07c25e3f5434575bfa281a908 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Thu, 18 Oct 2018 22:47:44 +0100 Subject: [PATCH 13/50] Made all version references in python url dynamic --- pythonforandroid/recipes/hostpython3/__init__.py | 2 +- pythonforandroid/recipes/python3/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonforandroid/recipes/hostpython3/__init__.py b/pythonforandroid/recipes/hostpython3/__init__.py index b0a44a3088..791b065963 100644 --- a/pythonforandroid/recipes/hostpython3/__init__.py +++ b/pythonforandroid/recipes/hostpython3/__init__.py @@ -7,7 +7,7 @@ class Hostpython3Recipe(Recipe): version = '3.7.0' - url = 'https://www.python.org/ftp/python/3.7.0/Python-{version}.tgz' + url = 'https://www.python.org/ftp/python/{version}/Python-{version}.tgz' name = 'hostpython3' conflicts = ['hostpython2'] diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index 01c1bdcadb..50298f2a9d 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -11,7 +11,7 @@ class Python3Recipe(TargetPythonRecipe): version = '3.7.0' - url = 'https://www.python.org/ftp/python/3.7.0/Python-{version}.tgz' + url = 'https://www.python.org/ftp/python/{version}/Python-{version}.tgz' name = 'python3' depends = ['hostpython3'] From 24a57be57a986294c42e52973230c8a845a95657 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sat, 20 Oct 2018 00:22:51 +0100 Subject: [PATCH 14/50] Began moving python bundle creation to the Python recipe --- pythonforandroid/bootstraps/sdl2/__init__.py | 49 +---------------- pythonforandroid/recipe.py | 18 ++++--- pythonforandroid/recipes/python3/__init__.py | 56 +++++++++++++++++++- 3 files changed, 67 insertions(+), 56 deletions(-) diff --git a/pythonforandroid/bootstraps/sdl2/__init__.py b/pythonforandroid/bootstraps/sdl2/__init__.py index 7289eaab8a..ec1fb4238a 100644 --- a/pythonforandroid/bootstraps/sdl2/__init__.py +++ b/pythonforandroid/bootstraps/sdl2/__init__.py @@ -107,53 +107,8 @@ def run_distribute(self): shprint(sh.rm, '-rf', 'config/python.o') elif self.ctx.python_recipe.name == 'python3': - ndk_dir = self.ctx.ndk_dir - py_recipe = self.ctx.python_recipe - - ## Build the python bundle: - - # Bundle compiled python modules to a folder - modules_dir = join(python_bundle_dir, 'modules') - ensure_dir(modules_dir) - - modules_build_dir = join( - self.ctx.python_recipe.get_build_dir(arch.arch), - 'android-build', - 'build', - 'lib.linux-arm-3.7') - module_filens = (glob.glob(join(modules_build_dir, '*.so')) + - glob.glob(join(modules_build_dir, '*.py'))) - for filen in module_filens: - shprint(sh.cp, filen, modules_dir) - - # zip up the standard library - stdlib_zip = join(self.dist_dir, python_bundle_dir, 'stdlib.zip') - with current_directory( - join(self.ctx.python_recipe.get_build_dir(arch.arch), - 'Lib')): - shprint(sh.zip, '-r', stdlib_zip, *os.listdir()) - - # copy the site-packages into place - shprint(sh.cp, '-r', self.ctx.get_python_install_dir(), - join(python_bundle_dir, 'site-packages')) - - # copy the python .so files into place - python_build_dir = join(py_recipe.get_build_dir(arch.arch), - 'android-build') - shprint(sh.cp, join(python_build_dir, 'libpython3.7m.so'), - 'libs/{}'.format(arch.arch)) - shprint(sh.cp, join(python_build_dir, 'libpython3.7m.so.1.0'), - 'libs/{}'.format(arch.arch)) - - info('Renaming .so files to reflect cross-compile') - site_packages_dir = join(python_bundle_dir, 'site-packages') - py_so_files = shprint(sh.find, site_packages_dir, '-iname', '*.so') - filens = py_so_files.stdout.decode('utf-8').split('\n')[:-1] - for filen in filens: - parts = filen.split('.') - if len(parts) <= 2: - continue - shprint(sh.mv, filen, parts[0] + '.so') + self.ctx.python_recipe.create_python_bundle( + join(self.dist_dir, python_bundle_dir), arch) elif self.ctx.python_recipe.from_crystax: # Python *is* loaded from crystax ndk_dir = self.ctx.ndk_dir diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 52b95c4fb1..82a8be1c98 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -1112,14 +1112,18 @@ def include_root(self, arch): def link_root(self): raise NotImplementedError('Not implemented in TargetPythonRecipe') - # @property - # def ctx(self): - # return self._ctx + @property + def major_minor_version_string(self): + from distutils.version import LooseVersion + return '.'.join([str(v) for v in LooseVersion(self.version).version[:2]]) - # @ctx.setter - # def ctx(self, ctx): - # self._ctx = ctx - # ctx.python_recipe = self + def create_python_bundle(self, dirn, arch): + """ + Create a packaged python bundle in the target directory, by + copying all the modules and standard library to the right + place. + """ + raise NotImplementedError('{} does not implement create_python_bundle'.format(self)) def md5sum(filen): diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index 50298f2a9d..cda1e7e134 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -4,8 +4,9 @@ check_all, is_api_lt, is_ndk) from pythonforandroid.logger import logger from pythonforandroid.util import ensure_dir -from os.path import exists, join, realpath -from os import environ +from os.path import exists, join, realpath, split +from os import environ, listdir +import glob import sh @@ -111,5 +112,56 @@ def include_root(self, arch_name): def link_root(self, arch_name): return join(self.get_build_dir(arch_name), 'android-build') + + def create_python_bundle(self, dirn, arch): + ndk_dir = self.ctx.ndk_dir + + # Bundle compiled python modules to a folder + modules_dir = join(dirn, 'modules') + ensure_dir(modules_dir) + + modules_build_dir = join( + self.get_build_dir(arch.arch), + 'android-build', + 'build', + 'lib.linux-arm-3.7') + module_filens = (glob.glob(join(modules_build_dir, '*.so')) + + glob.glob(join(modules_build_dir, '*.py'))) + for filen in module_filens: + shprint(sh.cp, filen, modules_dir) + + # zip up the standard library + stdlib_zip = join(dirn, 'stdlib.zip') + with current_directory( + join(self.get_build_dir(arch.arch), + 'Lib')): + shprint(sh.zip, '-r', stdlib_zip, *listdir('.')) + + # copy the site-packages into place + shprint(sh.cp, '-r', self.ctx.get_python_install_dir(), + join(dirn, 'site-packages')) + + # copy the python .so files into place + python_build_dir = join(self.get_build_dir(arch.arch), + 'android-build') + shprint(sh.cp, + join(python_build_dir, + 'libpython{}m.so'.format(self.major_minor_version_string)), + 'libs/{}'.format(arch.arch)) + shprint(sh.cp, + join(python_build_dir, + 'libpython{}m.so.1.0'.format(self.major_minor_version_string)), + 'libs/{}'.format(arch.arch)) + + info('Renaming .so files to reflect cross-compile') + site_packages_dir = join(dirn, 'site-packages') + py_so_files = shprint(sh.find, site_packages_dir, '-iname', '*.so') + filens = py_so_files.stdout.decode('utf-8').split('\n')[:-1] + for filen in filens: + file_dirname, file_basename = split(filen) + parts = file_basename.split('.') + if len(parts) <= 2: + continue + shprint(sh.mv, filen, join(file_dirname, parts[0] + '.so')) recipe = Python3Recipe() From 0e0036a854b02ff96c4e32409fe9b69190e83f0d Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sat, 20 Oct 2018 00:33:29 +0100 Subject: [PATCH 15/50] Moved python3crystax python bundling to the recipe class --- pythonforandroid/bootstraps/sdl2/__init__.py | 29 ++++--------------- pythonforandroid/recipe.py | 16 +++++++++- pythonforandroid/recipes/python3/__init__.py | 15 +++------- .../recipes/python3crystax/__init__.py | 15 ++++++++++ 4 files changed, 39 insertions(+), 36 deletions(-) diff --git a/pythonforandroid/bootstraps/sdl2/__init__.py b/pythonforandroid/bootstraps/sdl2/__init__.py index ec1fb4238a..9d95ed3e49 100644 --- a/pythonforandroid/bootstraps/sdl2/__init__.py +++ b/pythonforandroid/bootstraps/sdl2/__init__.py @@ -110,30 +110,11 @@ def run_distribute(self): self.ctx.python_recipe.create_python_bundle( join(self.dist_dir, python_bundle_dir), arch) - elif self.ctx.python_recipe.from_crystax: # Python *is* loaded from crystax - ndk_dir = self.ctx.ndk_dir - py_recipe = self.ctx.python_recipe - python_dir = join(ndk_dir, 'sources', 'python', - py_recipe.version, 'libs', arch.arch) - shprint(sh.cp, '-r', join(python_dir, - 'stdlib.zip'), crystax_python_dir) - shprint(sh.cp, '-r', join(python_dir, - 'modules'), crystax_python_dir) - shprint(sh.cp, '-r', self.ctx.get_python_install_dir(), - join(crystax_python_dir, 'site-packages')) - - info('Renaming .so files to reflect cross-compile') - site_packages_dir = join(crystax_python_dir, "site-packages") - find_ret = shprint( - sh.find, site_packages_dir, '-iname', '*.so') - filenames = find_ret.stdout.decode('utf-8').split('\n')[:-1] - for filename in filenames: - parts = filename.split('.') - if len(parts) <= 2: - continue - shprint(sh.mv, filename, filename.split('.')[0] + '.so') - site_packages_dir = join(abspath(curdir), - site_packages_dir) + elif self.ctx.python_recipe.from_crystax: + self.ctx.python_recipe.create_python_bundle( + join(self.dist_dir, python_bundle_dir), arch) + # TODO: Also set site_packages_dir again so fry_eggs can work + if 'sqlite3' not in self.ctx.recipe_build_order: with open('blacklist.txt', 'a') as fileh: fileh.write('\nsqlite3/*\nlib-dynload/_sqlite3.so\n') diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 82a8be1c98..273e03596f 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -1,4 +1,4 @@ -from os.path import basename, dirname, exists, isdir, isfile, join, realpath +from os.path import basename, dirname, exists, isdir, isfile, join, realpath, split import importlib import glob from shutil import rmtree @@ -1125,6 +1125,20 @@ def create_python_bundle(self, dirn, arch): """ raise NotImplementedError('{} does not implement create_python_bundle'.format(self)) + def reduce_object_file_names(self, dirn): + """Recursively renames all files named XXX.cpython-...-linux-gnu.so" + to "XXX.so", i.e. removing the erroneous architecture name + coming from the local system. + """ + py_so_files = shprint(sh.find, dirn, '-iname', '*.so') + filens = py_so_files.stdout.decode('utf-8').split('\n')[:-1] + for filen in filens: + file_dirname, file_basename = split(filen) + parts = file_basename.split('.') + if len(parts) <= 2: + continue + shprint(sh.mv, filen, join(file_dirname, parts[0] + '.so')) + def md5sum(filen): '''Calculate the md5sum of a file. diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index cda1e7e134..7fc5e14dc1 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -4,7 +4,7 @@ check_all, is_api_lt, is_ndk) from pythonforandroid.logger import logger from pythonforandroid.util import ensure_dir -from os.path import exists, join, realpath, split +from os.path import exists, join, realpath from os import environ, listdir import glob import sh @@ -154,14 +154,7 @@ def create_python_bundle(self, dirn, arch): 'libs/{}'.format(arch.arch)) info('Renaming .so files to reflect cross-compile') - site_packages_dir = join(dirn, 'site-packages') - py_so_files = shprint(sh.find, site_packages_dir, '-iname', '*.so') - filens = py_so_files.stdout.decode('utf-8').split('\n')[:-1] - for filen in filens: - file_dirname, file_basename = split(filen) - parts = file_basename.split('.') - if len(parts) <= 2: - continue - shprint(sh.mv, filen, join(file_dirname, parts[0] + '.so')) - + self.reduce_object_file_names(join(dirn, 'site-packages')) + + recipe = Python3Recipe() diff --git a/pythonforandroid/recipes/python3crystax/__init__.py b/pythonforandroid/recipes/python3crystax/__init__.py index 96cdf035b4..0cf5f4db19 100644 --- a/pythonforandroid/recipes/python3crystax/__init__.py +++ b/pythonforandroid/recipes/python3crystax/__init__.py @@ -73,5 +73,20 @@ def build_arch(self, arch): # available. Using e.g. pyenv makes this easy. self.ctx.hostpython = 'python{}'.format(self.version) + def create_python_bundle(self, dirn, arch): + ndk_dir = self.ctx.ndk_dir + py_recipe = self.ctx.python_recipe + python_dir = join(ndk_dir, 'sources', 'python', + py_recipe.version, 'libs', arch.arch) + shprint(sh.cp, '-r', join(python_dir, + 'stdlib.zip'), dirn) + shprint(sh.cp, '-r', join(python_dir, + 'modules'), dirn) + shprint(sh.cp, '-r', self.ctx.get_python_install_dir(), + join(dirn, 'site-packages')) + + info('Renaming .so files to reflect cross-compile') + self.reduce_object_file_names(self, join(dirn, "site-packages")) + recipe = Python3Recipe() From bd382656d701cbdc7d4fc495873ff10561a28627 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sat, 20 Oct 2018 00:44:01 +0100 Subject: [PATCH 16/50] Moved python2 python bundle creation to python recipe --- pythonforandroid/bootstraps/sdl2/__init__.py | 65 +++----------------- pythonforandroid/recipes/python2/__init__.py | 39 ++++++++++++ 2 files changed, 47 insertions(+), 57 deletions(-) diff --git a/pythonforandroid/bootstraps/sdl2/__init__.py b/pythonforandroid/bootstraps/sdl2/__init__.py index 9d95ed3e49..a8d383f06f 100644 --- a/pythonforandroid/bootstraps/sdl2/__init__.py +++ b/pythonforandroid/bootstraps/sdl2/__init__.py @@ -22,9 +22,6 @@ def run_distribute(self): arch = self.ctx.archs[0] python_install_dir = self.ctx.get_python_install_dir() from_crystax = self.ctx.python_recipe.from_crystax - crystax_python_dir = join("crystax_python", "crystax_python") - - python_bundle_dir = join('_python_bundle', '_python_bundle') if len(self.ctx.archs) > 1: raise ValueError("SDL2/gradle support only one arch") @@ -43,13 +40,6 @@ def run_distribute(self): with current_directory(self.dist_dir): info("Copying Python distribution") - if 'python2' in self.ctx.recipe_build_order: - ensure_dir("private") - elif not exists("crystax_python") and from_crystax: - ensure_dir(crystax_python_dir) - elif 'python3' in self.ctx.recipe_build_order: - ensure_dir(python_bundle_dir) - hostpython = sh.Command(self.ctx.hostpython) if self.ctx.python_recipe.name == 'python2': try: @@ -66,53 +56,14 @@ def run_distribute(self): self.distribute_javaclasses(self.ctx.javaclass_dir, dest_dir=join("src", "main", "java")) - if self.ctx.python_recipe.name == 'python2': - info("Filling private directory") - if not exists(join("private", "lib")): - info("private/lib does not exist, making") - shprint(sh.cp, "-a", - join("python-install", "lib"), "private") - shprint(sh.mkdir, "-p", - join("private", "include", "python2.7")) - - libpymodules_fn = join("libs", arch.arch, "libpymodules.so") - if exists(libpymodules_fn): - shprint(sh.mv, libpymodules_fn, 'private/') - shprint(sh.cp, - join('python-install', 'include', - 'python2.7', 'pyconfig.h'), - join('private', 'include', 'python2.7/')) - - info('Removing some unwanted files') - shprint(sh.rm, '-f', join('private', 'lib', 'libpython2.7.so')) - shprint(sh.rm, '-rf', join('private', 'lib', 'pkgconfig')) - - libdir = join(self.dist_dir, 'private', 'lib', 'python2.7') - site_packages_dir = join(libdir, 'site-packages') - with current_directory(libdir): - removes = [] - for dirname, root, filenames in walk("."): - for filename in filenames: - for suffix in EXCLUDE_EXTS: - if filename.endswith(suffix): - removes.append(filename) - shprint(sh.rm, '-f', *removes) - - info('Deleting some other stuff not used on android') - # To quote the original distribute.sh, 'well...' - shprint(sh.rm, '-rf', 'lib2to3') - shprint(sh.rm, '-rf', 'idlelib') - for filename in glob.glob('config/libpython*.a'): - shprint(sh.rm, '-f', filename) - shprint(sh.rm, '-rf', 'config/python.o') - - elif self.ctx.python_recipe.name == 'python3': - self.ctx.python_recipe.create_python_bundle( - join(self.dist_dir, python_bundle_dir), arch) - - elif self.ctx.python_recipe.from_crystax: - self.ctx.python_recipe.create_python_bundle( - join(self.dist_dir, python_bundle_dir), arch) + python_bundle_dir = join('_python_bundle', '_python_bundle') + if 'python2' in self.ctx.recipe_build_order: + # Python 2 is a special case with its own packaging location + python_bundle_dir = 'private' + ensure_dir(python_bundle_dir) + + self.ctx.python_recipe.create_python_bundle( + join(self.dist_dir, python_bundle_dir), arch) # TODO: Also set site_packages_dir again so fry_eggs can work if 'sqlite3' not in self.ctx.recipe_build_order: diff --git a/pythonforandroid/recipes/python2/__init__.py b/pythonforandroid/recipes/python2/__init__.py index e722314145..e58488b597 100644 --- a/pythonforandroid/recipes/python2/__init__.py +++ b/pythonforandroid/recipes/python2/__init__.py @@ -170,5 +170,44 @@ def do_python_build(self, arch): # print('python2 build done, exiting for debug') # exit(1) + def create_python_bundle(self, dirn, arch): + info("Filling private directory") + if not exists(join(dirn, "lib")): + info("lib dir does not exist, making") + shprint(sh.cp, "-a", + join("python-install", "lib"), dirn) + shprint(sh.mkdir, "-p", + join(dirn, "include", "python2.7")) + + libpymodules_fn = join("libs", arch.arch, "libpymodules.so") + if exists(libpymodules_fn): + shprint(sh.mv, libpymodules_fn, dirn) + shprint(sh.cp, + join('python-install', 'include', + 'python2.7', 'pyconfig.h'), + join(dirn, 'include', 'python2.7/')) + + info('Removing some unwanted files') + shprint(sh.rm, '-f', join(dirn, 'lib', 'libpython2.7.so')) + shprint(sh.rm, '-rf', join(dirn, 'lib', 'pkgconfig')) + + libdir = join(dirn, 'lib', 'python2.7') + site_packages_dir = join(libdir, 'site-packages') + with current_directory(libdir): + removes = [] + for dirname, root, filenames in walk("."): + for filename in filenames: + for suffix in EXCLUDE_EXTS: + if filename.endswith(suffix): + removes.append(filename) + shprint(sh.rm, '-f', *removes) + + info('Deleting some other stuff not used on android') + # To quote the original distribute.sh, 'well...' + shprint(sh.rm, '-rf', 'lib2to3') + shprint(sh.rm, '-rf', 'idlelib') + for filename in glob.glob('config/libpython*.a'): + shprint(sh.rm, '-f', filename) + shprint(sh.rm, '-rf', 'config/python.o') recipe = Python2Recipe() From a0838b671a40905506bc2706934a317bf4c25adf Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sat, 20 Oct 2018 23:06:45 +0100 Subject: [PATCH 17/50] Applied tito's fixes for python recipes installed via pip --- pythonforandroid/build.py | 17 +++++++++-------- pythonforandroid/recipes/android/__init__.py | 3 ++- .../recipes/hostpython3/__init__.py | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/pythonforandroid/build.py b/pythonforandroid/build.py index 8044025b42..6b2b428b90 100644 --- a/pythonforandroid/build.py +++ b/pythonforandroid/build.py @@ -509,14 +509,13 @@ def get_site_packages_dir(self, arch=None): '''Returns the location of site-packages in the python-install build dir. ''' + if self.python_recipe.name == 'python2': + return join(self.get_python_install_dir(), + 'lib', 'python2.7', 'site-packages') - # This needs to be replaced with something more general in - # order to support multiple python versions and/or multiple - # archs. - if self.python_recipe.from_crystax: - return self.get_python_install_dir() - return join(self.get_python_install_dir(), - 'lib', 'python2.7', 'site-packages') + # Only python2 is a special case, other python recipes use the + # python install dir + return self.get_python_install_dir() def get_libs_dir(self, arch): '''The libs dir for a given arch.''' @@ -621,7 +620,9 @@ def run_pymodules_install(ctx, modules): venv = sh.Command(ctx.virtualenv) with current_directory(join(ctx.build_dir)): - shprint(venv, '--python=python2.7', 'venv') + shprint(venv, + '--python=python{}'.format(ctx.python_recipe.major_minor_version_string()), + 'venv') info('Creating a requirements.txt file for the Python modules') with open('requirements.txt', 'w') as fileh: diff --git a/pythonforandroid/recipes/android/__init__.py b/pythonforandroid/recipes/android/__init__.py index 3c96a5fd82..3ce3bdc234 100644 --- a/pythonforandroid/recipes/android/__init__.py +++ b/pythonforandroid/recipes/android/__init__.py @@ -13,7 +13,8 @@ class AndroidRecipe(IncludedFilesBehaviour, CythonRecipe): src_filename = 'src' - depends = [('pygame', 'sdl2', 'genericndkbuild'), ('python2', 'python3crystax')] + depends = [('pygame', 'sdl2', 'genericndkbuild'), + ('python2', 'python3crystax', 'python3')] config_env = {} diff --git a/pythonforandroid/recipes/hostpython3/__init__.py b/pythonforandroid/recipes/hostpython3/__init__.py index 791b065963..54af977c64 100644 --- a/pythonforandroid/recipes/hostpython3/__init__.py +++ b/pythonforandroid/recipes/hostpython3/__init__.py @@ -10,7 +10,7 @@ class Hostpython3Recipe(Recipe): url = 'https://www.python.org/ftp/python/{version}/Python-{version}.tgz' name = 'hostpython3' - conflicts = ['hostpython2'] + conflicts = ['hostpython2', 'hostpython3crystax'] def get_build_container_dir(self, arch=None): choices = self.check_recipe_choices() From 0f21372ce301290e8de1f776a5582434617765cd Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sat, 20 Oct 2018 23:28:07 +0100 Subject: [PATCH 18/50] Fixed webbrowser registration for python3 Following tito's patch --- pythonforandroid/recipes/android/src/android/_android.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/recipes/android/src/android/_android.pyx b/pythonforandroid/recipes/android/src/android/_android.pyx index f4f37d8ab6..1a5fa5d1d5 100644 --- a/pythonforandroid/recipes/android/src/android/_android.pyx +++ b/pythonforandroid/recipes/android/src/android/_android.pyx @@ -332,7 +332,7 @@ class AndroidBrowser(object): return open_url(url) import webbrowser -webbrowser.register('android', AndroidBrowser, None, -1) +webbrowser.register('android', AndroidBrowser) cdef extern void android_start_service(char *, char *, char *) def start_service(title=None, description=None, arg=None): From 4a094d885939539690898803acca10128647720e Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sun, 21 Oct 2018 00:32:16 +0100 Subject: [PATCH 19/50] Updated python3 recipe target version to 3.7.1 --- pythonforandroid/recipes/hostpython3/__init__.py | 2 +- pythonforandroid/recipes/python3/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonforandroid/recipes/hostpython3/__init__.py b/pythonforandroid/recipes/hostpython3/__init__.py index 54af977c64..5163bce2a3 100644 --- a/pythonforandroid/recipes/hostpython3/__init__.py +++ b/pythonforandroid/recipes/hostpython3/__init__.py @@ -6,7 +6,7 @@ class Hostpython3Recipe(Recipe): - version = '3.7.0' + version = '3.7.1' url = 'https://www.python.org/ftp/python/{version}/Python-{version}.tgz' name = 'hostpython3' diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index 7fc5e14dc1..af43265a28 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -11,7 +11,7 @@ class Python3Recipe(TargetPythonRecipe): - version = '3.7.0' + version = '3.7.1' url = 'https://www.python.org/ftp/python/{version}/Python-{version}.tgz' name = 'python3' From d0f01138ae6eb152d0f325a17aa9936a11a03623 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sun, 21 Oct 2018 00:32:38 +0100 Subject: [PATCH 20/50] Made __ANDROID_API__ take the same value as APP_PLATFORM --- pythonforandroid/archs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/archs.py b/pythonforandroid/archs.py index 864c41f1cb..e59aae4191 100644 --- a/pythonforandroid/archs.py +++ b/pythonforandroid/archs.py @@ -35,7 +35,7 @@ def get_env(self, with_flags_in_cc=True): env['CFLAGS'] = ' '.join([ '-DANDROID', '-mandroid', '-fomit-frame-pointer' - ' -D__ANDROID_API__={}'.format(self.ctx._android_api), + ' -D__ANDROID_API__={}'.format(self.ctx.ndk_api), ]) env['LDFLAGS'] = ' ' From b58febca5748d8f3452e07407910b15f6ed61e66 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Tue, 23 Oct 2018 21:43:38 +0100 Subject: [PATCH 21/50] Corrected NDK platform finding to use ndk_api instead of android_api --- pythonforandroid/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/build.py b/pythonforandroid/build.py index 6b2b428b90..e63c356bc4 100644 --- a/pythonforandroid/build.py +++ b/pythonforandroid/build.py @@ -381,7 +381,7 @@ def prepare_build_environment(self, self.ndk_platform = join( self.ndk_dir, 'platforms', - 'android-{}'.format(self.android_api), + 'android-{}'.format(self.ndk_api), platform_dir) if not exists(self.ndk_platform): warning('ndk_platform doesn\'t exist: {}'.format( From d419f6265b57d258696f061510e86e22301f7c28 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Tue, 23 Oct 2018 21:49:37 +0100 Subject: [PATCH 22/50] Added include_root and link_root to python3 and python3crystax --- pythonforandroid/recipes/python2/__init__.py | 6 ++++++ pythonforandroid/recipes/python3crystax/__init__.py | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/pythonforandroid/recipes/python2/__init__.py b/pythonforandroid/recipes/python2/__init__.py index e58488b597..273666d642 100644 --- a/pythonforandroid/recipes/python2/__init__.py +++ b/pythonforandroid/recipes/python2/__init__.py @@ -210,4 +210,10 @@ def create_python_bundle(self, dirn, arch): shprint(sh.rm, '-f', filename) shprint(sh.rm, '-rf', 'config/python.o') + def include_root(self, arch_name): + return join(self.get_build_dir(arch_name), 'python-install', 'include', 'python2.7') + + def link_root(self, arch_name): + return join(self.get_build_dir(arch_name), 'python-install', 'lib') + recipe = Python2Recipe() diff --git a/pythonforandroid/recipes/python3crystax/__init__.py b/pythonforandroid/recipes/python3crystax/__init__.py index 0cf5f4db19..ab98ec7ba0 100644 --- a/pythonforandroid/recipes/python3crystax/__init__.py +++ b/pythonforandroid/recipes/python3crystax/__init__.py @@ -88,5 +88,12 @@ def create_python_bundle(self, dirn, arch): info('Renaming .so files to reflect cross-compile') self.reduce_object_file_names(self, join(dirn, "site-packages")) + def include_root(self, arch_name): + return join(self.ctx.ndk_dir, 'sources', 'python', self.major_minor_version_string(), + 'include', 'python') + + def link_root(self, arch_name): + return join(self.ctx.ndk_dir, 'sources', 'python', self.major_minor_version_string(), + 'libs', arch_name) recipe = Python3Recipe() From 1ebdcee0bbfc112a589b3f02f5cec7157cee087f Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Tue, 23 Oct 2018 23:00:30 +0100 Subject: [PATCH 23/50] Introduced some preliminary stdlib pruning during zip packaging --- pythonforandroid/recipes/python3/__init__.py | 55 ++++++++++++++++---- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index af43265a28..4de3c90b7a 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -1,15 +1,34 @@ from pythonforandroid.recipe import TargetPythonRecipe, Recipe -from pythonforandroid.toolchain import shprint, current_directory, info +from pythonforandroid.toolchain import shprint, current_directory from pythonforandroid.patching import (is_darwin, is_api_gt, check_all, is_api_lt, is_ndk) -from pythonforandroid.logger import logger +from pythonforandroid.logger import logger, info, debug from pythonforandroid.util import ensure_dir -from os.path import exists, join, realpath -from os import environ, listdir +from os.path import exists, join, realpath, basename +from os import environ, listdir, walk import glob +from fnmatch import fnmatch import sh +STDLIB_DIR_BLACKLIST = { + '__pycache__', + 'test', + 'tests', + 'lib2to3', + 'ensurepip', + 'idlelib', + 'tkinter', + } + +STDLIB_FILEN_BLACKLIST = [ + '*.pyc', + '*.exe', + '*.whl', + ] + + + class Python3Recipe(TargetPythonRecipe): version = '3.7.1' url = 'https://www.python.org/ftp/python/{version}/Python-{version}.tgz' @@ -126,16 +145,15 @@ def create_python_bundle(self, dirn, arch): 'build', 'lib.linux-arm-3.7') module_filens = (glob.glob(join(modules_build_dir, '*.so')) + - glob.glob(join(modules_build_dir, '*.py'))) + glob.glob(join(modules_build_dir, '*.py'))) for filen in module_filens: shprint(sh.cp, filen, modules_dir) # zip up the standard library stdlib_zip = join(dirn, 'stdlib.zip') - with current_directory( - join(self.get_build_dir(arch.arch), - 'Lib')): - shprint(sh.zip, '-r', stdlib_zip, *listdir('.')) + with current_directory(join(self.get_build_dir(arch.arch), 'Lib')): + stdlib_filens = self.get_stdlib_filens('.') + shprint(sh.zip, stdlib_zip, *stdlib_filens) # copy the site-packages into place shprint(sh.cp, '-r', self.ctx.get_python_install_dir(), @@ -156,5 +174,24 @@ def create_python_bundle(self, dirn, arch): info('Renaming .so files to reflect cross-compile') self.reduce_object_file_names(join(dirn, 'site-packages')) + def get_stdlib_filens(self, basedir): + return_filens = [] + for dirn, subdirs, filens in walk(basedir): + if basename(dirn) in STDLIB_DIR_BLACKLIST: + debug('stdlib.zip ignoring directory {}'.format(dirn)) + while subdirs: + subdirs.pop() + continue + for filen in filens: + for pattern in STDLIB_FILEN_BLACKLIST: + if fnmatch(filen, pattern): + debug('stdlib.zip ignoring file {}'.format(join(dirn, filen))) + break + else: + return_filens.append(join(dirn, filen)) + return return_filens + + + recipe = Python3Recipe() From d786718733a6f69a85ec9e02f6c4c62077b32f5f Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Wed, 24 Oct 2018 21:55:12 +0100 Subject: [PATCH 24/50] Refactored out function for getting valid filens from a folder --- pythonforandroid/recipes/python3/__init__.py | 26 ++------------- pythonforandroid/util.py | 35 ++++++++++++++++++-- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index 4de3c90b7a..e50a36bcf9 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -2,12 +2,11 @@ from pythonforandroid.toolchain import shprint, current_directory from pythonforandroid.patching import (is_darwin, is_api_gt, check_all, is_api_lt, is_ndk) -from pythonforandroid.logger import logger, info, debug -from pythonforandroid.util import ensure_dir +from pythonforandroid.logger import logger, info +from pythonforandroid.util import ensure_dir, walk_valid_filens from os.path import exists, join, realpath, basename from os import environ, listdir, walk import glob -from fnmatch import fnmatch import sh @@ -152,7 +151,7 @@ def create_python_bundle(self, dirn, arch): # zip up the standard library stdlib_zip = join(dirn, 'stdlib.zip') with current_directory(join(self.get_build_dir(arch.arch), 'Lib')): - stdlib_filens = self.get_stdlib_filens('.') + stdlib_filens = walk_valid_filens('.', STDLIB_DIR_BLACKLIST, STDLIB_FILEN_BLACKLIST) shprint(sh.zip, stdlib_zip, *stdlib_filens) # copy the site-packages into place @@ -174,24 +173,5 @@ def create_python_bundle(self, dirn, arch): info('Renaming .so files to reflect cross-compile') self.reduce_object_file_names(join(dirn, 'site-packages')) - def get_stdlib_filens(self, basedir): - return_filens = [] - for dirn, subdirs, filens in walk(basedir): - if basename(dirn) in STDLIB_DIR_BLACKLIST: - debug('stdlib.zip ignoring directory {}'.format(dirn)) - while subdirs: - subdirs.pop() - continue - for filen in filens: - for pattern in STDLIB_FILEN_BLACKLIST: - if fnmatch(filen, pattern): - debug('stdlib.zip ignoring file {}'.format(join(dirn, filen))) - break - else: - return_filens.append(join(dirn, filen)) - return return_filens - - - recipe = Python3Recipe() diff --git a/pythonforandroid/util.py b/pythonforandroid/util.py index bf5ba83901..f02b42fe62 100644 --- a/pythonforandroid/util.py +++ b/pythonforandroid/util.py @@ -1,10 +1,11 @@ import contextlib -from os.path import exists -from os import getcwd, chdir, makedirs +from os.path import exists, join +from os import getcwd, chdir, makedirs, walk import io import json import shutil import sys +from fnmatch import fnmatch from tempfile import mkdtemp try: from urllib.request import FancyURLopener @@ -124,3 +125,33 @@ def is_exe(fpath): return exe_file return None + +def walk_valid_filens(base_dir, invalid_dir_names, invalid_file_patterns): + """Recursively walks all the files and directories in ``dirn``, + ignoring directories that match any pattern in ``invalid_dirns`` + and files that patch any pattern in ``invalid_filens``. + + ``invalid_dirns`` and ``invalid_filens`` should both be lists of + strings to match. ``invalid_dir_patterns`` expects a list of + invalid directory names, while ``invalid_file_patterns`` expects a + list of glob patterns compared against the full filepath. + + File and directory paths are evaluated as full paths relative to ``dirn``. + + """ + + return_filens = [] + for dirn, subdirs, filens in walk(base_dir): + + # Remove invalid subdirs so that they will not be walked + for i in reversed(range(len(subdirs))): + subdir = subdirs[i] + if subdir in invalid_dir_names: + subdirs.pop(i) + + for filen in filens: + for pattern in invalid_file_patterns: + if fnmatch(filen, pattern): + break + else: + yield join(dirn, filen) From 4964ec22ff6f92984c2e9bd102f183d769b10fa6 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Fri, 26 Oct 2018 23:31:41 +0100 Subject: [PATCH 25/50] Made site-packages copying exclude some files/folders (in a temporary hacky way) --- pythonforandroid/bootstraps/sdl2/__init__.py | 1 - pythonforandroid/recipe.py | 12 +++------ pythonforandroid/recipes/python3/__init__.py | 26 +++++++++++++++----- pythonforandroid/util.py | 1 - 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/pythonforandroid/bootstraps/sdl2/__init__.py b/pythonforandroid/bootstraps/sdl2/__init__.py index a8d383f06f..eb32d65c22 100644 --- a/pythonforandroid/bootstraps/sdl2/__init__.py +++ b/pythonforandroid/bootstraps/sdl2/__init__.py @@ -36,7 +36,6 @@ def run_distribute(self): with open('local.properties', 'w') as fileh: fileh.write('sdk.dir={}'.format(self.ctx.sdk_dir)) - # TODO: Move the packaged python building to the python recipes with current_directory(self.dist_dir): info("Copying Python distribution") diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 273e03596f..08aed588e5 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -789,14 +789,10 @@ def get_recipe_env(self, arch=None, with_flags_in_cc=True): join(ndk_dir_python, 'libs', arch.arch)) env['LDFLAGS'] += ' -lpython{}m'.format(python_short_version) elif 'python3' in self.ctx.recipe_build_order: - # TODO: Make the recipe env get these details from the - # python recipe instead of hardcoding - env['PYTHON_ROOT'] = Recipe.get_recipe('python3', self.ctx).get_build_dir(arch.arch) - env['CFLAGS'] += ' -I' + env['PYTHON_ROOT'] + '/Include' - env['LDFLAGS'] += ( - ' -L' + - join(env['PYTHON_ROOT'], 'android-build') + - ' -lpython3.7m') + env['CFLAGS'] += ' -I{}'.format(self.ctx.python_recipe.include_root(arch.arch)) + env['LDFLAGS'] += ' -L{} -lpython{}m'.format( + self.ctx.python_recipe.link_root(arch.arch), + self.ctx.python_recipe.major_minor_version_string) hppath = [] hppath.append(join(dirname(self.hostpython_location), 'Lib')) diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index e50a36bcf9..ee5c037f4c 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -4,7 +4,7 @@ check_all, is_api_lt, is_ndk) from pythonforandroid.logger import logger, info from pythonforandroid.util import ensure_dir, walk_valid_filens -from os.path import exists, join, realpath, basename +from os.path import exists, join, realpath, dirname from os import environ, listdir, walk import glob import sh @@ -18,14 +18,21 @@ 'ensurepip', 'idlelib', 'tkinter', - } +} STDLIB_FILEN_BLACKLIST = [ '*.pyc', '*.exe', '*.whl', - ] +] +# TODO: Move to a generic location so all recipes use the same blacklist +SITE_PACKAGES_DIR_BLACKLIST = { + '__pycache__', + 'tests' +} + +SITE_PACKAGES_FILEN_BLACKLIST = [] class Python3Recipe(TargetPythonRecipe): @@ -151,12 +158,19 @@ def create_python_bundle(self, dirn, arch): # zip up the standard library stdlib_zip = join(dirn, 'stdlib.zip') with current_directory(join(self.get_build_dir(arch.arch), 'Lib')): - stdlib_filens = walk_valid_filens('.', STDLIB_DIR_BLACKLIST, STDLIB_FILEN_BLACKLIST) + stdlib_filens = walk_valid_filens( + '.', STDLIB_DIR_BLACKLIST, STDLIB_FILEN_BLACKLIST) shprint(sh.zip, stdlib_zip, *stdlib_filens) # copy the site-packages into place - shprint(sh.cp, '-r', self.ctx.get_python_install_dir(), - join(dirn, 'site-packages')) + ensure_dir(join(dirn, 'site-packages')) + # TODO: Improve the API around walking and copying the files + with current_directory(self.ctx.get_python_install_dir()): + filens = list(walk_valid_filens( + '.', SITE_PACKAGES_DIR_BLACKLIST, SITE_PACKAGES_FILEN_BLACKLIST)) + for filen in filens: + ensure_dir(join(dirn, 'site-packages', dirname(filen))) + sh.cp(filen, join(dirn, 'site-packages', filen)) # copy the python .so files into place python_build_dir = join(self.get_build_dir(arch.arch), diff --git a/pythonforandroid/util.py b/pythonforandroid/util.py index f02b42fe62..8276e39cfa 100644 --- a/pythonforandroid/util.py +++ b/pythonforandroid/util.py @@ -140,7 +140,6 @@ def walk_valid_filens(base_dir, invalid_dir_names, invalid_file_patterns): """ - return_filens = [] for dirn, subdirs, filens in walk(base_dir): # Remove invalid subdirs so that they will not be walked From ec7ecac17835c810346887592875d16aba598140 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Fri, 26 Oct 2018 23:41:39 +0100 Subject: [PATCH 26/50] Deleted some old commented out code --- pythonforandroid/recipe.py | 48 +------------------------------------- 1 file changed, 1 insertion(+), 47 deletions(-) diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 08aed588e5..07b7fa9b95 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -20,8 +20,6 @@ from pythonforandroid.util import (urlretrieve, current_directory, ensure_dir) # this import is necessary to keep imp.load_source from complaining :) - - if PY2: import imp import_recipe = imp.load_source @@ -167,20 +165,6 @@ def report_hook(index, blksize, size): shprint(sh.git, 'submodule', 'update', '--recursive') return target - # def get_archive_rootdir(self, filename): - # if filename.endswith(".tgz") or filename.endswith(".tar.gz") or \ - # filename.endswith(".tbz2") or filename.endswith(".tar.bz2"): - # archive = tarfile.open(filename) - # root = archive.next().path.split("/") - # return root[0] - # elif filename.endswith(".zip"): - # with zipfile.ZipFile(filename) as zf: - # return dirname(zf.namelist()[0]) - # else: - # print("Error: cannot detect root directory") - # print("Unrecognized extension for {}".format(filename)) - # raise Exception() - def apply_patch(self, filename, arch): """ Apply a patch from the current recipe directory into the current @@ -206,42 +190,12 @@ def append_file(self, filename, dest): with open(dest, "ab") as fd: fd.write(data) - # def has_marker(self, marker): - # """ - # Return True if the current build directory has the marker set - # """ - # return exists(join(self.build_dir, ".{}".format(marker))) - - # def set_marker(self, marker): - # """ - # Set a marker info the current build directory - # """ - # with open(join(self.build_dir, ".{}".format(marker)), "w") as fd: - # fd.write("ok") - - # def delete_marker(self, marker): - # """ - # Delete a specific marker - # """ - # try: - # unlink(join(self.build_dir, ".{}".format(marker))) - # except: - # pass - @property def name(self): '''The name of the recipe, the same as the folder containing it.''' modname = self.__class__.__module__ return modname.split(".", 2)[-1] - # @property - # def archive_fn(self): - # bfn = basename(self.url.format(version=self.version)) - # fn = "{}/{}-{}".format( - # self.ctx.cache_dir, - # self.name, bfn) - # return fn - @property def filtered_archs(self): '''Return archs of self.ctx that are valid build archs @@ -1095,7 +1049,7 @@ def __init__(self, *args, **kwargs): def prebuild_arch(self, arch): super(TargetPythonRecipe, self).prebuild_arch(arch) - if self.from_crystax and self.ctx.ndk != 'crystax': + if elf.from_crystax and self.ctx.ndk != 'crystax': error('The {} recipe can only be built when ' 'using the CrystaX NDK. Exiting.'.format(self.name)) exit(1) From a80fc6f94915860d873db1be0547ffc32cce6741 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Fri, 26 Oct 2018 23:49:00 +0100 Subject: [PATCH 27/50] Made create_python_bundle return the site-packages dir --- pythonforandroid/bootstraps/sdl2/__init__.py | 4 ++-- pythonforandroid/recipe.py | 2 +- pythonforandroid/recipes/python2/__init__.py | 2 ++ pythonforandroid/recipes/python3/__init__.py | 2 ++ pythonforandroid/recipes/python3crystax/__init__.py | 2 ++ 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/pythonforandroid/bootstraps/sdl2/__init__.py b/pythonforandroid/bootstraps/sdl2/__init__.py index eb32d65c22..6cdaebb266 100644 --- a/pythonforandroid/bootstraps/sdl2/__init__.py +++ b/pythonforandroid/bootstraps/sdl2/__init__.py @@ -61,7 +61,7 @@ def run_distribute(self): python_bundle_dir = 'private' ensure_dir(python_bundle_dir) - self.ctx.python_recipe.create_python_bundle( + site_packages_dir = self.ctx.python_recipe.create_python_bundle( join(self.dist_dir, python_bundle_dir), arch) # TODO: Also set site_packages_dir again so fry_eggs can work @@ -70,7 +70,7 @@ def run_distribute(self): fileh.write('\nsqlite3/*\nlib-dynload/_sqlite3.so\n') self.strip_libraries(arch) - # self.fry_eggs(site_packages_dir) # TODO uncomment this and make it work with python3 + self.fry_eggs(site_packages_dir) # TODO uncomment this and make it work with python3 super(SDL2GradleBootstrap, self).run_distribute() diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 07b7fa9b95..1b5180e020 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -1049,7 +1049,7 @@ def __init__(self, *args, **kwargs): def prebuild_arch(self, arch): super(TargetPythonRecipe, self).prebuild_arch(arch) - if elf.from_crystax and self.ctx.ndk != 'crystax': + if self.from_crystax and self.ctx.ndk != 'crystax': error('The {} recipe can only be built when ' 'using the CrystaX NDK. Exiting.'.format(self.name)) exit(1) diff --git a/pythonforandroid/recipes/python2/__init__.py b/pythonforandroid/recipes/python2/__init__.py index 273666d642..86489d4c55 100644 --- a/pythonforandroid/recipes/python2/__init__.py +++ b/pythonforandroid/recipes/python2/__init__.py @@ -210,6 +210,8 @@ def create_python_bundle(self, dirn, arch): shprint(sh.rm, '-f', filename) shprint(sh.rm, '-rf', 'config/python.o') + return site_packages_dir + def include_root(self, arch_name): return join(self.get_build_dir(arch_name), 'python-install', 'include', 'python2.7') diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index ee5c037f4c..cfe8e21228 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -187,5 +187,7 @@ def create_python_bundle(self, dirn, arch): info('Renaming .so files to reflect cross-compile') self.reduce_object_file_names(join(dirn, 'site-packages')) + return join(dirn, 'site-packages') + recipe = Python3Recipe() diff --git a/pythonforandroid/recipes/python3crystax/__init__.py b/pythonforandroid/recipes/python3crystax/__init__.py index ab98ec7ba0..b190beed1f 100644 --- a/pythonforandroid/recipes/python3crystax/__init__.py +++ b/pythonforandroid/recipes/python3crystax/__init__.py @@ -88,6 +88,8 @@ def create_python_bundle(self, dirn, arch): info('Renaming .so files to reflect cross-compile') self.reduce_object_file_names(self, join(dirn, "site-packages")) + return join(dirn, 'site-packages') + def include_root(self, arch_name): return join(self.ctx.ndk_dir, 'sources', 'python', self.major_minor_version_string(), 'include', 'python') From d2435d69e9ecdcc805bc52420389caf7920e86a0 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sat, 27 Oct 2018 00:03:57 +0100 Subject: [PATCH 28/50] Restructured dist_info.json creation --- pythonforandroid/bootstrap.py | 10 +----- pythonforandroid/bootstraps/sdl2/__init__.py | 3 +- pythonforandroid/distribution.py | 36 +++++++------------- 3 files changed, 15 insertions(+), 34 deletions(-) diff --git a/pythonforandroid/bootstrap.py b/pythonforandroid/bootstrap.py index 5f76989a43..aa03fb0267 100644 --- a/pythonforandroid/bootstrap.py +++ b/pythonforandroid/bootstrap.py @@ -107,15 +107,7 @@ def prepare_dist_dir(self, name): def run_distribute(self): # TODO: Move this to Distribution.save_info - with current_directory(self.dist_dir): - info('Saving distribution info') - with open('dist_info.json', 'w') as fileh: - json.dump({'dist_name': self.ctx.dist_name, - 'bootstrap': self.ctx.bootstrap.name, - 'archs': [arch.arch for arch in self.ctx.archs], - 'ndk_api': self.ctx.ndk_api, - 'recipes': self.ctx.recipe_build_order + self.ctx.python_modules}, - fileh) + self.distribution.save_info(self.dist_dir) @classmethod def list_bootstraps(cls): diff --git a/pythonforandroid/bootstraps/sdl2/__init__.py b/pythonforandroid/bootstraps/sdl2/__init__.py index 6cdaebb266..c1564f36db 100644 --- a/pythonforandroid/bootstraps/sdl2/__init__.py +++ b/pythonforandroid/bootstraps/sdl2/__init__.py @@ -63,14 +63,13 @@ def run_distribute(self): site_packages_dir = self.ctx.python_recipe.create_python_bundle( join(self.dist_dir, python_bundle_dir), arch) - # TODO: Also set site_packages_dir again so fry_eggs can work if 'sqlite3' not in self.ctx.recipe_build_order: with open('blacklist.txt', 'a') as fileh: fileh.write('\nsqlite3/*\nlib-dynload/_sqlite3.so\n') self.strip_libraries(arch) - self.fry_eggs(site_packages_dir) # TODO uncomment this and make it work with python3 + self.fry_eggs(site_packages_dir) super(SDL2GradleBootstrap, self).run_distribute() diff --git a/pythonforandroid/distribution.py b/pythonforandroid/distribution.py index 02de2f139c..bbba1eea84 100644 --- a/pythonforandroid/distribution.py +++ b/pythonforandroid/distribution.py @@ -182,29 +182,19 @@ def get_distributions(cls, ctx, extra_dist_dirs=[]): dists.append(dist) return dists - # def save_info(self): - # ''' - # Save information about the distribution in its dist_dir. - # ''' - # with current_directory(self.dist_dir): - # info('Saving distribution info') - # with open('dist_info.json', 'w') as fileh: - # json.dump({'dist_name': self.name, - # 'archs': [arch.arch for arch in self.ctx.archs], - # 'ndk_api': self.ctx.ndk_api, - # 'recipes': self.ctx.recipe_build_order}, - # fileh) - - # def load_info(self): - # '''Load information about the dist from the info file that p4a - # automatically creates.''' - # with current_directory(self.dist_dir): - # filen = 'dist_info.json' - # if not exists(filen): - # return None - # with open('dist_info.json', 'r') as fileh: - # dist_info = json.load(fileh) - # return dist_info + def save_info(self, dirn): + ''' + Save information about the distribution in its dist_dir. + ''' + with current_directory(dirn): + info('Saving distribution info') + with open('dist_info.json', 'w') as fileh: + json.dump({'dist_name': self.ctx.dist_name, + 'bootstrap': self.ctx.bootstrap.name, + 'archs': [arch.arch for arch in self.ctx.archs], + 'ndk_api': self.ctx.ndk_api, + 'recipes': self.ctx.recipe_build_order + self.ctx.python_modules}, + fileh) def pretty_log_dists(dists, log_func=info): From 07ae0595e06442e32998c662827d33f649d66208 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sat, 27 Oct 2018 00:04:14 +0100 Subject: [PATCH 29/50] Removed hardcoded python version from sdl2 recipe --- pythonforandroid/recipes/sdl2/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pythonforandroid/recipes/sdl2/__init__.py b/pythonforandroid/recipes/sdl2/__init__.py index 97e21e0b82..f403dfcf49 100644 --- a/pythonforandroid/recipes/sdl2/__init__.py +++ b/pythonforandroid/recipes/sdl2/__init__.py @@ -29,7 +29,8 @@ def get_recipe_env(self, arch=None): env['EXTRA_LDLIBS'] = ' -lpython2.7' if 'python3' in self.ctx.recipe_build_order: - env['EXTRA_LDLIBS'] = ' -lpython3.7m' # TODO: don't hardcode the python version + env['EXTRA_LDLIBS'] = ' -lpython{}m'.format( + self.ctx.python_recipe.major_minor_version_string) env['APP_ALLOW_MISSING_DEPS'] = 'true' return env From 3932fe259a5a12244c07d5feeb21351209f88672 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sat, 27 Oct 2018 00:18:19 +0100 Subject: [PATCH 30/50] Removed hardcoded api 21 in python3 recipe --- pythonforandroid/recipes/python3/__init__.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index cfe8e21228..b6b9dddf77 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -57,13 +57,15 @@ def build_arch(self, arch): # Skipping "Ensure that nl_langinfo is broken" from the original bpo-30386 + platform_name = 'android-{}'.format(self.ctx.ndk_api) + with current_directory(build_dir): env = environ.copy() # TODO: Get this information from p4a's arch system android_host = 'arm-linux-androideabi' android_build = sh.Command(join(recipe_build_dir, 'config.guess'))().stdout.strip().decode('utf-8') - platform_dir = join(self.ctx.ndk_dir, 'platforms', 'android-21', 'arch-arm') + platform_dir = join(self.ctx.ndk_dir, 'platforms', platform_name, 'arch-arm') toolchain = '{android_host}-4.9'.format(android_host=android_host) toolchain = join(self.ctx.ndk_dir, 'toolchains', toolchain, 'prebuilt', 'linux-x86_64') CC = '{clang} -target {target} -gcc-toolchain {toolchain}'.format( @@ -84,10 +86,13 @@ def build_arch(self, arch): env['READELF'] = READELF env['STRIP'] = STRIP - ndk_flags = '--sysroot={ndk_sysroot} -D__ANDROID_API__=21 -isystem {ndk_android_host}'.format( - ndk_sysroot=join(self.ctx.ndk_dir, 'sysroot'), - ndk_android_host=join(self.ctx.ndk_dir, 'sysroot', 'usr', 'include', android_host)) - sysroot = join(self.ctx.ndk_dir, 'platforms', 'android-21', 'arch-arm') + ndk_flags = ('--sysroot={ndk_sysroot} -D__ANDROID_API__={android_api} ' + '-isystem {ndk_android_host}').format( + ndk_sysroot=join(self.ctx.ndk_dir, 'sysroot'), + android_api=self.ctx.ndk_api, + ndk_android_host=join( + self.ctx.ndk_dir, 'sysroot', 'usr', 'include', android_host)) + sysroot = join(self.ctx.ndk_dir, 'platforms', platform_name, 'arch-arm') env['CFLAGS'] = env.get('CFLAGS', '') + ' ' + ndk_flags env['CPPFLAGS'] = env.get('CPPFLAGS', '') + ' ' + ndk_flags env['LDFLAGS'] = env.get('LDFLAGS', '') + ' --sysroot={} -L{}'.format(sysroot, join(sysroot, 'usr', 'lib')) From b25074e9810280a2f340ff789c8b039bb079e596 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sat, 27 Oct 2018 15:05:42 +0100 Subject: [PATCH 31/50] Fixed python2 build following recent changes for python3 --- pythonforandroid/bootstraps/sdl2/__init__.py | 4 ---- pythonforandroid/recipes/python2/__init__.py | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pythonforandroid/bootstraps/sdl2/__init__.py b/pythonforandroid/bootstraps/sdl2/__init__.py index c1564f36db..83170dddf3 100644 --- a/pythonforandroid/bootstraps/sdl2/__init__.py +++ b/pythonforandroid/bootstraps/sdl2/__init__.py @@ -4,13 +4,9 @@ from os.path import join, exists, curdir, abspath from os import walk import os -import glob import sh -EXCLUDE_EXTS = (".py", ".pyc", ".so.o", ".so.a", ".so.libs", ".pyx") - - class SDL2GradleBootstrap(Bootstrap): name = 'sdl2' diff --git a/pythonforandroid/recipes/python2/__init__.py b/pythonforandroid/recipes/python2/__init__.py index 86489d4c55..c5a14296e8 100644 --- a/pythonforandroid/recipes/python2/__init__.py +++ b/pythonforandroid/recipes/python2/__init__.py @@ -3,8 +3,12 @@ from pythonforandroid.patching import (is_darwin, is_api_gt, check_all, is_api_lt, is_ndk) from os.path import exists, join, realpath +from os import walk +import glob import sh +EXCLUDE_EXTS = (".py", ".pyc", ".so.o", ".so.a", ".so.libs", ".pyx") + class Python2Recipe(TargetPythonRecipe): version = "2.7.2" From 82c1fc928366f5d85a913eef5f3f9892616a58e5 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sat, 27 Oct 2018 16:49:20 +0100 Subject: [PATCH 32/50] Added more (and clearer) checks for ndk-api vs android-api --- .../bootstraps/sdl2/build/build.py | 46 ++++++++++--------- pythonforandroid/build.py | 6 +++ pythonforandroid/recipes/python2/__init__.py | 1 - 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/pythonforandroid/bootstraps/sdl2/build/build.py b/pythonforandroid/bootstraps/sdl2/build/build.py index b360758f75..2f7721550d 100644 --- a/pythonforandroid/bootstraps/sdl2/build/build.py +++ b/pythonforandroid/bootstraps/sdl2/build/build.py @@ -412,7 +412,17 @@ def make_package(args): def parse_args(args=None): global BLACKLIST_PATTERNS, WHITELIST_PATTERNS, PYTHON - default_android_api = 21 + + # Get the default minsdk, equal to the NDK API that this dist it built against + with open('dist_info.json', 'r') as fileh: + info = json.load(fileh) + if 'ndk_api' not in info: + print('Failed to read ndk_api from dist info') + default_android_api = 12 # The old default before ndk_api was introduced + else: + default_android_api = info['ndk_api'] + ndk_api = info['ndk_api'] + import argparse ap = argparse.ArgumentParser(description='''\ Package a Python application for Android. @@ -495,9 +505,8 @@ def parse_args(args=None): type=int, help=('Deprecated argument, does nothing')) ap.add_argument('--minsdk', dest='min_sdk_version', default=default_android_api, type=int, - help=('Minimum Android SDK version to use. Default to ' - 'the value of ANDROIDAPI, or {} if not set' - .format(default_android_api))) + help=('Minimum Android SDK version that the app supports. ' + 'Defaults to {}.'.format(default_android_api))) ap.add_argument('--allow-minsdk-ndkapi-mismatch', default=False, action='store_true', help=('Allow the --minsdk argument to be different from ' @@ -536,25 +545,18 @@ def parse_args(args=None): if args.name and args.name[0] == '"' and args.name[-1] == '"': args.name = args.name[1:-1] - with open('dist_info.json', 'r') as fileh: - info = json.load(fileh) - if 'ndk_api' not in info: - print('Failed to read ndk_api from dist info') - ndk_api = args.min_sdk_version + if ndk_api != args.min_sdk_version: + print(('WARNING: --minsdk argument does not match the api that is ' + 'compiled against. Only proceed if you know what you are ' + 'doing, otherwise use --minsdk={} or recompile against api ' + '{}').format(ndk_api, args.min_sdk_version)) + if not args.allow_minsdk_ndkapi_mismatch: + print('You must pass --allow-minsdk-ndkapi-mismatch to build ' + 'with --minsdk different to the target NDK api from the ' + 'build step') + exit(1) else: - ndk_api = info['ndk_api'] - if ndk_api != args.min_sdk_version: - print(('WARNING: --minsdk argument does not match the api that is ' - 'compiled against. Only proceed if you know what you are ' - 'doing, otherwise use --minsdk={} or recompile against api ' - '{}').format(ndk_api, args.min_sdk_version)) - if not args.allow_minsdk_ndkapi_mismatch: - print('You must pass --allow-minsdk-ndkapi-mismatch to build ' - 'with --minsdk different to the target NDK api from the ' - 'build step') - exit(1) - else: - print('Proceeding with --minsdk not matching build target api') + print('Proceeding with --minsdk not matching build target api') diff --git a/pythonforandroid/build.py b/pythonforandroid/build.py index e63c356bc4..e05bfe7260 100644 --- a/pythonforandroid/build.py +++ b/pythonforandroid/build.py @@ -340,6 +340,12 @@ def prepare_build_environment(self, self.ndk_ver = ndk_ver self.ndk_api = user_ndk_api + if self.ndk_api > self.android_api: + error('Target NDK API is {}, higher than the target Android API {}.'.format( + self.ndk_api, self.android_api)) + error('The NDK API is a minimum supported API number and must be lower ' + 'than the target Android API') + exit(1) info('Using {} NDK {}'.format(self.ndk.capitalize(), self.ndk_ver)) diff --git a/pythonforandroid/recipes/python2/__init__.py b/pythonforandroid/recipes/python2/__init__.py index c5a14296e8..9b14c9d162 100644 --- a/pythonforandroid/recipes/python2/__init__.py +++ b/pythonforandroid/recipes/python2/__init__.py @@ -9,7 +9,6 @@ EXCLUDE_EXTS = (".py", ".pyc", ".so.o", ".so.a", ".so.libs", ".pyx") - class Python2Recipe(TargetPythonRecipe): version = "2.7.2" url = 'https://python.org/ftp/python/{version}/Python-{version}.tar.bz2' From dbf98152a20eebb8f74de5a620005de3ee0f9e07 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sat, 27 Oct 2018 18:00:00 +0100 Subject: [PATCH 33/50] Fixed dist selection to account for ndk_api --- pythonforandroid/bootstrap.py | 2 -- pythonforandroid/distribution.py | 60 +++++++++++++++++--------------- pythonforandroid/toolchain.py | 1 + 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/pythonforandroid/bootstrap.py b/pythonforandroid/bootstrap.py index aa03fb0267..dfd638e46e 100644 --- a/pythonforandroid/bootstrap.py +++ b/pythonforandroid/bootstrap.py @@ -102,11 +102,9 @@ def prepare_build_dir(self): fileh.write('target=android-{}'.format(self.ctx.android_api)) def prepare_dist_dir(self, name): - # self.dist_dir = self.get_dist_dir(name) ensure_dir(self.dist_dir) def run_distribute(self): - # TODO: Move this to Distribution.save_info self.distribution.save_info(self.dist_dir) @classmethod diff --git a/pythonforandroid/distribution.py b/pythonforandroid/distribution.py index bbba1eea84..11eedf0871 100644 --- a/pythonforandroid/distribution.py +++ b/pythonforandroid/distribution.py @@ -3,7 +3,7 @@ import json from pythonforandroid.logger import (info, info_notify, warning, - Err_Style, Err_Fore) + Err_Style, Err_Fore, error) from pythonforandroid.util import current_directory @@ -21,6 +21,7 @@ class Distribution(object): needs_build = False # Whether the dist needs compiling url = None dist_dir = None # Where the dist dir ultimately is. Should not be None. + ndk_api = None archs = [] '''The arch targets that the dist is built for.''' @@ -42,6 +43,7 @@ def __repr__(self): @classmethod def get_distribution(cls, ctx, name=None, recipes=[], + ndk_api=None, force_build=False, extra_dist_dirs=[], require_perfect_match=False): @@ -76,13 +78,19 @@ def get_distribution(cls, ctx, name=None, recipes=[], possible_dists = existing_dists + name_match_dist = None + # 0) Check if a dist with that name already exists if name is not None and name: possible_dists = [d for d in possible_dists if d.name == name] + if possible_dists: + name_match_dist = possible_dists[0] # 1) Check if any existing dists meet the requirements _possible_dists = [] for dist in possible_dists: + if ndk_api is not None and dist.ndk_api != ndk_api: + continue for recipe in recipes: if recipe not in dist.recipes: break @@ -97,10 +105,12 @@ def get_distribution(cls, ctx, name=None, recipes=[], else: info('No existing dists meet the given requirements!') - # If any dist has perfect recipes, return it + # If any dist has perfect recipes and ndk API, return it for dist in possible_dists: if force_build: continue + if ndk_api is not None and dist.ndk_api != ndk_api: + continue if (set(dist.recipes) == set(recipes) or (set(recipes).issubset(set(dist.recipes)) and not require_perfect_match)): @@ -110,34 +120,22 @@ def get_distribution(cls, ctx, name=None, recipes=[], assert len(possible_dists) < 2 - if not name and possible_dists: - info('Asked for dist with name {} with recipes ({}), but a dist ' - 'with this name already exists and has incompatible recipes ' - '({})'.format(name, ', '.join(recipes), - ', '.join(possible_dists[0].recipes))) - info('No compatible dist found, so exiting.') + # If there was a name match but we didn't already choose it, + # then the existing dist is incompatible with the requested + # configuration and the build cannot continue + if name_match_dist is not None: + error('Asked for dist with name {name} with recipes ({req_recipes}) and ' + 'NDK API {req_ndk_api}, but a dist ' + 'with this name already exists and has either incompatible recipes ' + '({dist_recipes}) or NDK API {dist_ndk_api}'.format( + name=name, + req_ndk_api=ndk_api, + dist_ndk_api=name_match_dist.ndk_api, + req_recipes=', '.join(recipes), + dist_recipes=', '.join(name_match_dist.recipes))) + error('No compatible dist found, so exiting.') exit(1) - # # 2) Check if any downloadable dists meet the requirements - - # online_dists = [('testsdl2', ['hostpython2', 'sdl2_image', - # 'sdl2_mixer', 'sdl2_ttf', - # 'python2', 'sdl2', - # 'pyjniussdl2', 'kivysdl2'], - # 'https://github.com/inclement/sdl2-example-dist/archive/master.zip'), - # ] - # _possible_dists = [] - # for dist_name, dist_recipes, dist_url in online_dists: - # for recipe in recipes: - # if recipe not in dist_recipes: - # break - # else: - # dist = Distribution(ctx) - # dist.name = dist_name - # dist.url = dist_url - # _possible_dists.append(dist) - # # if _possible_dists - # If we got this far, we need to build a new dist dist = Distribution(ctx) dist.needs_build = True @@ -152,6 +150,7 @@ def get_distribution(cls, ctx, name=None, recipes=[], dist.name = name dist.dist_dir = join(ctx.dist_dir, dist.name) dist.recipes = recipes + dist.ndk_api = ctx.ndk_api return dist @@ -179,6 +178,7 @@ def get_distributions(cls, ctx, extra_dist_dirs=[]): dist.recipes = dist_info['recipes'] if 'archs' in dist_info: dist.archs = dist_info['archs'] + dist.ndk_api = dist_info['ndk_api'] dists.append(dist) return dists @@ -200,10 +200,12 @@ def save_info(self, dirn): def pretty_log_dists(dists, log_func=info): infos = [] for dist in dists: - infos.append('{Fore.GREEN}{Style.BRIGHT}{name}{Style.RESET_ALL}: ' + ndk_api = 'unknown' if dist.ndk_api is None else dist.ndk_api + infos.append('{Fore.GREEN}{Style.BRIGHT}{name}{Style.RESET_ALL}: min API {ndk_api}, ' 'includes recipes ({Fore.GREEN}{recipes}' '{Style.RESET_ALL}), built for archs ({Fore.BLUE}' '{archs}{Style.RESET_ALL})'.format( + ndk_api=ndk_api, name=dist.name, recipes=', '.join(dist.recipes), archs=', '.join(dist.archs) if dist.archs else 'UNKNOWN', Fore=Err_Fore, Style=Err_Style)) diff --git a/pythonforandroid/toolchain.py b/pythonforandroid/toolchain.py index 5f08bcf1b1..745a38e730 100644 --- a/pythonforandroid/toolchain.py +++ b/pythonforandroid/toolchain.py @@ -155,6 +155,7 @@ def dist_from_args(ctx, args): return Distribution.get_distribution( ctx, name=args.dist_name, + ndk_api=args.ndk_api, recipes=split_argument_list(args.requirements), require_perfect_match=args.require_perfect_match) From a96311274b769a8e002f329b59346ee6de01b8e3 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sat, 27 Oct 2018 22:54:40 +0100 Subject: [PATCH 34/50] Improved code style per review comments --- .../bootstraps/sdl2/build/build.py | 21 ++++++++----------- .../bootstraps/sdl2/build/jni/src/start.c | 12 ++++++----- .../recipes/hostpython3/__init__.py | 6 ------ .../recipes/hostpython3crystax/__init__.py | 1 - pythonforandroid/recipes/python2/__init__.py | 3 +-- 5 files changed, 17 insertions(+), 26 deletions(-) diff --git a/pythonforandroid/bootstraps/sdl2/build/build.py b/pythonforandroid/bootstraps/sdl2/build/build.py index 2f7721550d..ca12d10035 100644 --- a/pythonforandroid/bootstraps/sdl2/build/build.py +++ b/pythonforandroid/bootstraps/sdl2/build/build.py @@ -240,12 +240,9 @@ def make_package(args): # Package up the private data (public not supported). tar_dirs = [args.private] - if exists('private'): - tar_dirs.append('private') - if exists('crystax_python'): - tar_dirs.append('crystax_python') - if exists('_python_bundle'): - tar_dirs.append('_python_bundle') + for python_bundle_dir in ('private', 'crystax_python', '_python_bundle'): + if exists(python_bundle_dir): + tar_dirs.append(python_bundle_Dir) if args.private: make_tar('src/main/assets/private.mp3', tar_dirs, args.ignore_path) @@ -413,14 +410,14 @@ def make_package(args): def parse_args(args=None): global BLACKLIST_PATTERNS, WHITELIST_PATTERNS, PYTHON - # Get the default minsdk, equal to the NDK API that this dist it built against + # Get the default minsdk, equal to the NDK API that this dist is built against with open('dist_info.json', 'r') as fileh: info = json.load(fileh) if 'ndk_api' not in info: - print('Failed to read ndk_api from dist info') - default_android_api = 12 # The old default before ndk_api was introduced + print('WARNING: Failed to read ndk_api from dist info, defaulting to 12') + default_min_api = 12 # The old default before ndk_api was introduced else: - default_android_api = info['ndk_api'] + default_min_api = info['ndk_api'] ndk_api = info['ndk_api'] import argparse @@ -504,9 +501,9 @@ def parse_args(args=None): ap.add_argument('--sdk', dest='sdk_version', default=-1, type=int, help=('Deprecated argument, does nothing')) ap.add_argument('--minsdk', dest='min_sdk_version', - default=default_android_api, type=int, + default=default_min_api, type=int, help=('Minimum Android SDK version that the app supports. ' - 'Defaults to {}.'.format(default_android_api))) + 'Defaults to {}.'.format(default_min_api))) ap.add_argument('--allow-minsdk-ndkapi-mismatch', default=False, action='store_true', help=('Allow the --minsdk argument to be different from ' diff --git a/pythonforandroid/bootstraps/sdl2/build/jni/src/start.c b/pythonforandroid/bootstraps/sdl2/build/jni/src/start.c index 9816cd45cd..c4cde305f2 100644 --- a/pythonforandroid/bootstraps/sdl2/build/jni/src/start.c +++ b/pythonforandroid/bootstraps/sdl2/build/jni/src/start.c @@ -123,8 +123,6 @@ int main(int argc, char *argv[]) { snprintf(paths, 256, "%s/stdlib.zip:%s/modules", crystax_python_dir, crystax_python_dir); - LOGP("calculated paths to be..."); - LOGP(paths); } if (dir_exists(python_bundle_dir)) { @@ -132,10 +130,11 @@ int main(int argc, char *argv[]) { snprintf(paths, 256, "%s/stdlib.zip:%s/modules", python_bundle_dir, python_bundle_dir); - LOGP("calculated paths to be..."); - LOGP(paths); } + LOGP("calculated paths to be..."); + LOGP(paths); + #if PY_MAJOR_VERSION >= 3 wchar_t *wchar_paths = Py_DecodeLocale(paths, NULL); @@ -148,7 +147,10 @@ int main(int argc, char *argv[]) { LOGP("set wchar paths..."); } else { - LOGP("crystax_python does not exist"); + // We do not expect to see crystax_python any more, so no point + // reminding the user about it. If it does exist, we'll have + // logged it earlier. + LOGP("_python_bundle does not exist"); } Py_Initialize(); diff --git a/pythonforandroid/recipes/hostpython3/__init__.py b/pythonforandroid/recipes/hostpython3/__init__.py index 5163bce2a3..fa04e137cf 100644 --- a/pythonforandroid/recipes/hostpython3/__init__.py +++ b/pythonforandroid/recipes/hostpython3/__init__.py @@ -30,9 +30,6 @@ def build_arch(self, arch): if not exists(join(build_dir, 'python')): with current_directory(recipe_build_dir): - env = {} # The command line environment we will use - - # Configure the build with current_directory(build_dir): if not exists('config.status'): @@ -40,8 +37,6 @@ def build_arch(self, arch): # Create the Setup file. This copying from Setup.dist # seems to be the normal and expected procedure. - assert exists(join(build_dir, 'Modules')), ( - 'Expected dir {} does not exist'.format(join(build_dir, 'Modules'))) shprint(sh.cp, join('Modules', 'Setup.dist'), join(build_dir, 'Modules', 'Setup')) result = shprint(sh.make, '-C', build_dir) @@ -49,6 +44,5 @@ def build_arch(self, arch): info('Skipping hostpython3 build as it has already been completed') self.ctx.hostpython = join(build_dir, 'python') - self.ctx.hostpgen = '/usr/bin/false' # is this actually used for anything? recipe = Hostpython3Recipe() diff --git a/pythonforandroid/recipes/hostpython3crystax/__init__.py b/pythonforandroid/recipes/hostpython3crystax/__init__.py index 12dc172753..e4d0c8eb59 100644 --- a/pythonforandroid/recipes/hostpython3crystax/__init__.py +++ b/pythonforandroid/recipes/hostpython3crystax/__init__.py @@ -27,7 +27,6 @@ def build_arch(self, arch): Creates expected build and symlinks system Python version. """ self.ctx.hostpython = '/usr/bin/false' - self.ctx.hostpgen = '/usr/bin/false' # creates the sub buildir (used by other recipes) # https://github.com/kivy/python-for-android/issues/1154 sub_build_dir = join(self.get_build_dir(), 'build') diff --git a/pythonforandroid/recipes/python2/__init__.py b/pythonforandroid/recipes/python2/__init__.py index 9b14c9d162..2ec02d50a8 100644 --- a/pythonforandroid/recipes/python2/__init__.py +++ b/pythonforandroid/recipes/python2/__init__.py @@ -209,8 +209,7 @@ def create_python_bundle(self, dirn, arch): # To quote the original distribute.sh, 'well...' shprint(sh.rm, '-rf', 'lib2to3') shprint(sh.rm, '-rf', 'idlelib') - for filename in glob.glob('config/libpython*.a'): - shprint(sh.rm, '-f', filename) + shprint(sh.rm, '-f', *glob.glob('config/libpython*.a')) shprint(sh.rm, '-rf', 'config/python.o') return site_packages_dir From b3cd815eb47d45f640d99713546398f3961f610f Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sat, 27 Oct 2018 23:10:55 +0100 Subject: [PATCH 35/50] Fixed typo in variable name --- pythonforandroid/bootstraps/sdl2/build/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/bootstraps/sdl2/build/build.py b/pythonforandroid/bootstraps/sdl2/build/build.py index ca12d10035..847ae63dc1 100644 --- a/pythonforandroid/bootstraps/sdl2/build/build.py +++ b/pythonforandroid/bootstraps/sdl2/build/build.py @@ -242,7 +242,7 @@ def make_package(args): tar_dirs = [args.private] for python_bundle_dir in ('private', 'crystax_python', '_python_bundle'): if exists(python_bundle_dir): - tar_dirs.append(python_bundle_Dir) + tar_dirs.append(python_bundle_dir) if args.private: make_tar('src/main/assets/private.mp3', tar_dirs, args.ignore_path) From f113131d8d637684716f8ff50226ec506ad94abd Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Tue, 6 Nov 2018 20:58:32 +0000 Subject: [PATCH 36/50] Cleaned up code per review comments --- pythonforandroid/build.py | 2 +- .../recipes/hostpython3crystax/__init__.py | 4 ++-- pythonforandroid/recipes/python3crystax/__init__.py | 10 +++++----- pythonforandroid/recipes/sdl2/__init__.py | 1 - 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/pythonforandroid/build.py b/pythonforandroid/build.py index e05bfe7260..176d5542a7 100644 --- a/pythonforandroid/build.py +++ b/pythonforandroid/build.py @@ -627,7 +627,7 @@ def run_pymodules_install(ctx, modules): venv = sh.Command(ctx.virtualenv) with current_directory(join(ctx.build_dir)): shprint(venv, - '--python=python{}'.format(ctx.python_recipe.major_minor_version_string()), + '--python=python{}'.format(ctx.python_recipe.major_minor_version_string), 'venv') info('Creating a requirements.txt file for the Python modules') diff --git a/pythonforandroid/recipes/hostpython3crystax/__init__.py b/pythonforandroid/recipes/hostpython3crystax/__init__.py index e4d0c8eb59..88cee35938 100644 --- a/pythonforandroid/recipes/hostpython3crystax/__init__.py +++ b/pythonforandroid/recipes/hostpython3crystax/__init__.py @@ -3,7 +3,7 @@ import sh -class Hostpython3Recipe(Recipe): +class Hostpython3CrystaXRecipe(Recipe): version = 'auto' # the version is taken from the python3crystax recipe name = 'hostpython3crystax' @@ -41,4 +41,4 @@ def build_arch(self, arch): shprint(sh.ln, '-sf', system_python, link_dest) -recipe = Hostpython3Recipe() +recipe = Hostpython3CrystaXRecipe() diff --git a/pythonforandroid/recipes/python3crystax/__init__.py b/pythonforandroid/recipes/python3crystax/__init__.py index b190beed1f..09ad1a8393 100644 --- a/pythonforandroid/recipes/python3crystax/__init__.py +++ b/pythonforandroid/recipes/python3crystax/__init__.py @@ -11,7 +11,7 @@ 'releases/download/0.1/crystax_python_3.6_armeabi_armeabi-v7a.tar.gz')} -class Python3Recipe(TargetPythonRecipe): +class Python3CrystaXRecipe(TargetPythonRecipe): version = '3.6' url = '' name = 'python3crystax' @@ -86,16 +86,16 @@ def create_python_bundle(self, dirn, arch): join(dirn, 'site-packages')) info('Renaming .so files to reflect cross-compile') - self.reduce_object_file_names(self, join(dirn, "site-packages")) + self.reduce_object_file_names(join(dirn, "site-packages")) return join(dirn, 'site-packages') def include_root(self, arch_name): - return join(self.ctx.ndk_dir, 'sources', 'python', self.major_minor_version_string(), + return join(self.ctx.ndk_dir, 'sources', 'python', self.major_minor_version_string, 'include', 'python') def link_root(self, arch_name): - return join(self.ctx.ndk_dir, 'sources', 'python', self.major_minor_version_string(), + return join(self.ctx.ndk_dir, 'sources', 'python', self.major_minor_version_string, 'libs', arch_name) -recipe = Python3Recipe() +recipe = Python3CrystaXRecipe() diff --git a/pythonforandroid/recipes/sdl2/__init__.py b/pythonforandroid/recipes/sdl2/__init__.py index f403dfcf49..411b97966f 100644 --- a/pythonforandroid/recipes/sdl2/__init__.py +++ b/pythonforandroid/recipes/sdl2/__init__.py @@ -20,7 +20,6 @@ def get_recipe_env(self, arch=None): py2 = self.get_recipe('python2', arch.ctx) env['PYTHON2_NAME'] = py2.get_dir_name() - py3 = self.get_recipe('python3', arch.ctx) env['PYTHON_INCLUDE_ROOT'] = self.ctx.python_recipe.include_root(arch.arch) env['PYTHON_LINK_ROOT'] = self.ctx.python_recipe.link_root(arch.arch) From 9cd161e061f6c3a773c0f3dcebc086d502773eb0 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Wed, 7 Nov 2018 21:42:48 +0000 Subject: [PATCH 37/50] Made flake8 fixes --- pythonforandroid/bootstrap.py | 9 ++++----- pythonforandroid/bootstraps/sdl2/__init__.py | 4 +--- pythonforandroid/bootstraps/sdl2/build/build.py | 12 +++++------- pythonforandroid/recipe.py | 1 + pythonforandroid/recipes/hostpython3/__init__.py | 4 ++-- pythonforandroid/recipes/python2/__init__.py | 4 +++- pythonforandroid/recipes/python3/__init__.py | 11 ++++------- pythonforandroid/recipes/python3crystax/__init__.py | 9 +++++---- pythonforandroid/util.py | 3 ++- 9 files changed, 27 insertions(+), 30 deletions(-) diff --git a/pythonforandroid/bootstrap.py b/pythonforandroid/bootstrap.py index dfd638e46e..531bc0db90 100644 --- a/pythonforandroid/bootstrap.py +++ b/pythonforandroid/bootstrap.py @@ -2,7 +2,6 @@ from os import listdir import sh import glob -import json import importlib from pythonforandroid.logger import (warning, shprint, info, logger, @@ -246,12 +245,12 @@ def strip_libraries(self, arch): if self.ctx.python_recipe.name == 'python2': filens = shprint(sh.find, join(self.dist_dir, 'private'), - join(self.dist_dir, 'libs'), - '-iname', '*.so', _env=env).stdout.decode('utf-8') + join(self.dist_dir, 'libs'), + '-iname', '*.so', _env=env).stdout.decode('utf-8') else: filens = shprint(sh.find, join(self.dist_dir, '_python_bundle', '_python_bundle', 'modules'), - join(self.dist_dir, 'libs'), - '-iname', '*.so', _env=env).stdout.decode('utf-8') + join(self.dist_dir, 'libs'), + '-iname', '*.so', _env=env).stdout.decode('utf-8') logger.info('Stripping libraries in private dir') for filen in filens.split('\n'): try: diff --git a/pythonforandroid/bootstraps/sdl2/__init__.py b/pythonforandroid/bootstraps/sdl2/__init__.py index 83170dddf3..e77d9c932a 100644 --- a/pythonforandroid/bootstraps/sdl2/__init__.py +++ b/pythonforandroid/bootstraps/sdl2/__init__.py @@ -1,9 +1,7 @@ from pythonforandroid.toolchain import ( Bootstrap, shprint, current_directory, info, info_main) from pythonforandroid.util import ensure_dir -from os.path import join, exists, curdir, abspath -from os import walk -import os +from os.path import join, exists import sh diff --git a/pythonforandroid/bootstraps/sdl2/build/build.py b/pythonforandroid/bootstraps/sdl2/build/build.py index 847ae63dc1..d620cb46d6 100644 --- a/pythonforandroid/bootstraps/sdl2/build/build.py +++ b/pythonforandroid/bootstraps/sdl2/build/build.py @@ -544,18 +544,16 @@ def parse_args(args=None): if ndk_api != args.min_sdk_version: print(('WARNING: --minsdk argument does not match the api that is ' - 'compiled against. Only proceed if you know what you are ' - 'doing, otherwise use --minsdk={} or recompile against api ' - '{}').format(ndk_api, args.min_sdk_version)) + 'compiled against. Only proceed if you know what you are ' + 'doing, otherwise use --minsdk={} or recompile against api ' + '{}').format(ndk_api, args.min_sdk_version)) if not args.allow_minsdk_ndkapi_mismatch: print('You must pass --allow-minsdk-ndkapi-mismatch to build ' - 'with --minsdk different to the target NDK api from the ' - 'build step') + 'with --minsdk different to the target NDK api from the ' + 'build step') exit(1) else: print('Proceeding with --minsdk not matching build target api') - - if args.sdk_version != -1: print('WARNING: Received a --sdk argument, but this argument is ' diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 1b5180e020..746fe02876 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -1035,6 +1035,7 @@ def get_recipe_env(self, arch, with_flags_in_cc=True): return env + class TargetPythonRecipe(Recipe): '''Class for target python recipes. Sets ctx.python_recipe to point to itself, so as to know later what kind of Python was built or used.''' diff --git a/pythonforandroid/recipes/hostpython3/__init__.py b/pythonforandroid/recipes/hostpython3/__init__.py index fa04e137cf..dd50b43a37 100644 --- a/pythonforandroid/recipes/hostpython3/__init__.py +++ b/pythonforandroid/recipes/hostpython3/__init__.py @@ -1,7 +1,6 @@ -from pythonforandroid.toolchain import Recipe, shprint, info, warning +from pythonforandroid.toolchain import Recipe, shprint, info from pythonforandroid.util import ensure_dir, current_directory from os.path import join, exists -import os import sh @@ -45,4 +44,5 @@ def build_arch(self, arch): self.ctx.hostpython = join(build_dir, 'python') + recipe = Hostpython3Recipe() diff --git a/pythonforandroid/recipes/python2/__init__.py b/pythonforandroid/recipes/python2/__init__.py index 2ec02d50a8..7afd42f94d 100644 --- a/pythonforandroid/recipes/python2/__init__.py +++ b/pythonforandroid/recipes/python2/__init__.py @@ -9,6 +9,7 @@ EXCLUDE_EXTS = (".py", ".pyc", ".so.o", ".so.a", ".so.libs", ".pyx") + class Python2Recipe(TargetPythonRecipe): version = "2.7.2" url = 'https://python.org/ftp/python/{version}/Python-{version}.tar.bz2' @@ -187,7 +188,7 @@ def create_python_bundle(self, dirn, arch): shprint(sh.mv, libpymodules_fn, dirn) shprint(sh.cp, join('python-install', 'include', - 'python2.7', 'pyconfig.h'), + 'python2.7', 'pyconfig.h'), join(dirn, 'include', 'python2.7/')) info('Removing some unwanted files') @@ -220,4 +221,5 @@ def include_root(self, arch_name): def link_root(self, arch_name): return join(self.get_build_dir(arch_name), 'python-install', 'lib') + recipe = Python2Recipe() diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index b6b9dddf77..4bdf53c93f 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -1,15 +1,12 @@ -from pythonforandroid.recipe import TargetPythonRecipe, Recipe +from pythonforandroid.recipe import TargetPythonRecipe from pythonforandroid.toolchain import shprint, current_directory -from pythonforandroid.patching import (is_darwin, is_api_gt, - check_all, is_api_lt, is_ndk) from pythonforandroid.logger import logger, info from pythonforandroid.util import ensure_dir, walk_valid_filens -from os.path import exists, join, realpath, dirname -from os import environ, listdir, walk +from os.path import exists, join, dirname +from os import environ import glob import sh - STDLIB_DIR_BLACKLIST = { '__pycache__', 'test', @@ -46,7 +43,7 @@ class Python3Recipe(TargetPythonRecipe): def build_arch(self, arch): recipe_build_dir = self.get_build_dir(arch.arch) - + # Create a subdirectory to actually perform the build build_dir = join(recipe_build_dir, 'android-build') ensure_dir(build_dir) diff --git a/pythonforandroid/recipes/python3crystax/__init__.py b/pythonforandroid/recipes/python3crystax/__init__.py index 09ad1a8393..42c6a54e64 100644 --- a/pythonforandroid/recipes/python3crystax/__init__.py +++ b/pythonforandroid/recipes/python3crystax/__init__.py @@ -22,7 +22,7 @@ class Python3CrystaXRecipe(TargetPythonRecipe): from_crystax = True def get_dir_name(self): - name = super(Python3Recipe, self).get_dir_name() + name = super(Python3CrystaXRecipe, self).get_dir_name() name += '-version{}'.format(self.version) return name @@ -77,11 +77,11 @@ def create_python_bundle(self, dirn, arch): ndk_dir = self.ctx.ndk_dir py_recipe = self.ctx.python_recipe python_dir = join(ndk_dir, 'sources', 'python', - py_recipe.version, 'libs', arch.arch) + py_recipe.version, 'libs', arch.arch) shprint(sh.cp, '-r', join(python_dir, - 'stdlib.zip'), dirn) + 'stdlib.zip'), dirn) shprint(sh.cp, '-r', join(python_dir, - 'modules'), dirn) + 'modules'), dirn) shprint(sh.cp, '-r', self.ctx.get_python_install_dir(), join(dirn, 'site-packages')) @@ -98,4 +98,5 @@ def link_root(self, arch_name): return join(self.ctx.ndk_dir, 'sources', 'python', self.major_minor_version_string, 'libs', arch_name) + recipe = Python3CrystaXRecipe() diff --git a/pythonforandroid/util.py b/pythonforandroid/util.py index 8276e39cfa..dd503de7a6 100644 --- a/pythonforandroid/util.py +++ b/pythonforandroid/util.py @@ -126,6 +126,7 @@ def is_exe(fpath): return None + def walk_valid_filens(base_dir, invalid_dir_names, invalid_file_patterns): """Recursively walks all the files and directories in ``dirn``, ignoring directories that match any pattern in ``invalid_dirns`` @@ -138,7 +139,7 @@ def walk_valid_filens(base_dir, invalid_dir_names, invalid_file_patterns): File and directory paths are evaluated as full paths relative to ``dirn``. - """ + """ for dirn, subdirs, filens in walk(base_dir): From 462b1e57e784d34e240a3471fb5e0c62070eb6ea Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Wed, 7 Nov 2018 22:19:53 +0000 Subject: [PATCH 38/50] Fixed reportlab recipe tests --- pythonforandroid/build.py | 14 ++++++++++++++ tests/recipes/test_reportlab.py | 2 ++ 2 files changed, 16 insertions(+) diff --git a/pythonforandroid/build.py b/pythonforandroid/build.py index 176d5542a7..c40fccfcbc 100644 --- a/pythonforandroid/build.py +++ b/pythonforandroid/build.py @@ -124,6 +124,19 @@ def android_api(self): def android_api(self, value): self._android_api = value + @property + def ndk_api(self): + '''The API number compile against''' + if self._ndk_api is None: + raise ValueError('Tried to access ndk_api_api but it has not ' + 'been set - this should not happen, something ' + 'went wrong!') + return self._ndk_api + + @ndk_api.setter + def ndk_api(self, value): + self._ndk_api = value + @property def ndk_ver(self): '''The version of the NDK being used for compilation.''' @@ -461,6 +474,7 @@ def __init__(self): self._sdk_dir = None self._ndk_dir = None self._android_api = None + self._ndk_api = None self._ndk_ver = None self.ndk = None diff --git a/tests/recipes/test_reportlab.py b/tests/recipes/test_reportlab.py index 805b7977d2..41667a6932 100644 --- a/tests/recipes/test_reportlab.py +++ b/tests/recipes/test_reportlab.py @@ -16,6 +16,8 @@ def setUp(self): Setups recipe and context. """ self.context = Context() + self.context.ndk_api = 21 + self.context.android_api = 27 self.arch = ArchARMv7_a(self.context) self.recipe = Recipe.get_recipe('reportlab', self.context) self.recipe.ctx = self.context From f8cc0e9fb1dbe965d152e6de720870edff0c4538 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Wed, 7 Nov 2018 23:00:40 +0000 Subject: [PATCH 39/50] Added E129 to ignored list --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 00ac584626..6c9733bd0e 100644 --- a/tox.ini +++ b/tox.ini @@ -17,6 +17,7 @@ commands = flake8 pythonforandroid/ tests/ ci/ ignore = E123, E124, E126, E226, + E129, E402, E501, E722, F812, F841, W503, W504 From f7038f883c213e1a4e851ad7021c4635a6ce63b0 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Thu, 8 Nov 2018 21:31:57 +0000 Subject: [PATCH 40/50] Made ndk-api default more intelligent --- pythonforandroid/build.py | 20 +++++++++++++++++++- pythonforandroid/toolchain.py | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/pythonforandroid/build.py b/pythonforandroid/build.py index c40fccfcbc..82a3aa5ba2 100644 --- a/pythonforandroid/build.py +++ b/pythonforandroid/build.py @@ -17,6 +17,8 @@ DEFAULT_ANDROID_API = 15 +DEFAULT_NDK_API = 21 + class Context(object): '''A build context. If anything will be built, an instance this class @@ -352,7 +354,23 @@ def prepare_build_environment(self, 'set it with `--ndk-version=...`.') self.ndk_ver = ndk_ver - self.ndk_api = user_ndk_api + ndk_api = None + if user_ndk_api: + ndk_api = user_ndk_api + if ndk_api is not None: + info('Getting NDK API version (i.e. minimum supported API) from user argument') + if ndk_api is None: + ndk_api = environ.get('NDKAPI', None) + if ndk_api is not None: + info('Found Android API target in $NDKAPI') + if ndk_api is None: + ndk_api = min(self.android_api, DEFAULT_NDK_API) + warning('NDK API target was not set manually, using ' + 'the default of {} = min(android-api={}, default ndk-api={})'.format( + ndk_api, self.android_api, DEFAULT_NDK_API)) + ndk_api = int(ndk_api) + self.ndk_api = ndk_api + if self.ndk_api > self.android_api: error('Target NDK API is {}, higher than the target Android API {}.'.format( self.ndk_api, self.android_api)) diff --git a/pythonforandroid/toolchain.py b/pythonforandroid/toolchain.py index 745a38e730..165d51027f 100644 --- a/pythonforandroid/toolchain.py +++ b/pythonforandroid/toolchain.py @@ -259,7 +259,7 @@ def __init__(self): help=('The version of the Android NDK. This is optional: ' 'we try to work it out automatically from the ndk_dir.')) generic_parser.add_argument( - '--ndk-api', type=int, default=21, + '--ndk-api', type=int, default=0, help=('The Android API level to compile against. This should be your ' '*minimal supported* API, not normally the same as your --android-api.')) generic_parser.add_argument( From da981941216786676805bbd1996faa724a8c34c9 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Thu, 8 Nov 2018 21:32:42 +0000 Subject: [PATCH 41/50] Added default ndk-api settings to the testapps --- testapps/setup_testapp_python2.py | 3 ++- testapps/setup_testapp_python3.py | 7 +++---- testapps/setup_testapp_python3crystax.py | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/testapps/setup_testapp_python2.py b/testapps/setup_testapp_python2.py index eabfaec40b..041a3c933b 100644 --- a/testapps/setup_testapp_python2.py +++ b/testapps/setup_testapp_python2.py @@ -2,9 +2,10 @@ from distutils.core import setup from setuptools import find_packages -options = {'apk': { +options = {'apk': {'debug': None, 'requirements': 'sdl2,pyjnius,kivy,python2', 'android-api': 19, + 'ndk-api': 19, 'ndk-dir': '/home/asandy/android/crystax-ndk-10.3.2', 'dist-name': 'bdisttest_python2', 'ndk-version': '10.3.2', diff --git a/testapps/setup_testapp_python3.py b/testapps/setup_testapp_python3.py index 1bc04324d2..a20323f857 100644 --- a/testapps/setup_testapp_python3.py +++ b/testapps/setup_testapp_python3.py @@ -2,10 +2,9 @@ from distutils.core import setup from setuptools import find_packages -options = {'apk': {'debug': None, - 'requirements': 'sdl2,pyjnius,kivy,python3', - 'android-api': 19, - 'ndk-dir': '/home/asandy/android/crystax-ndk-10.3.2', +options = {'apk': {'requirements': 'sdl2,pyjnius,kivy,python3', + 'android-api': 26, + 'ndk-api': 19, 'dist-name': 'bdisttest_python3_googlendk', 'ndk-version': '10.3.2', 'arch': 'armeabi-v7a', diff --git a/testapps/setup_testapp_python3crystax.py b/testapps/setup_testapp_python3crystax.py index 530381d973..bb3e18baf1 100644 --- a/testapps/setup_testapp_python3crystax.py +++ b/testapps/setup_testapp_python3crystax.py @@ -5,6 +5,7 @@ options = {'apk': {'debug': None, 'requirements': 'sdl2,pyjnius,kivy,python3crystax', 'android-api': 19, + 'ndk-api': 19, 'ndk-dir': '/home/asandy/android/crystax-ndk-10.3.2', 'dist-name': 'bdisttest_python3', 'ndk-version': '10.3.2', From e48d82e8db61681e27ef27e38334ab0fbd5411ce Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Thu, 8 Nov 2018 21:52:09 +0000 Subject: [PATCH 42/50] Updated travis.yml to target python3crystax testapp --- .travis.yml | 2 +- testapps/setup_testapp_python2.py | 3 +-- testapps/setup_testapp_python3crystax.py | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index b74297e22a..7521fbddc6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,7 @@ env: # https://github.com/kivy/python-for-android/issues/1263#issuecomment-390421054 - COMMAND='. venv/bin/activate && cd testapps/ && python setup_testapp_python2_sqlite_openssl.py apk --sdk-dir $ANDROID_SDK_HOME --ndk-dir $ANDROID_NDK_HOME --requirements sdl2,pyjnius,kivy,python2,openssl,requests,sqlite3,setuptools' - COMMAND='. venv/bin/activate && cd testapps/ && python setup_testapp_python2.py apk --sdk-dir $ANDROID_SDK_HOME --ndk-dir $ANDROID_NDK_HOME --bootstrap sdl2 --requirements python2,numpy' - - COMMAND='. venv/bin/activate && cd testapps/ && python setup_testapp_python3.py apk --sdk-dir $ANDROID_SDK_HOME --ndk-dir $CRYSTAX_NDK_HOME --requirements python3crystax,setuptools,android,sdl2,pyjnius,kivy' + - COMMAND='. venv/bin/activate && cd testapps/ && python setup_testapp_python3crystax.py apk --sdk-dir $ANDROID_SDK_HOME --ndk-dir $CRYSTAX_NDK_HOME --requirements python3crystax,setuptools,android,sdl2,pyjnius,kivy' # builds only the recipes that moved - COMMAND='. venv/bin/activate && ./ci/rebuild_updated_recipes.py' diff --git a/testapps/setup_testapp_python2.py b/testapps/setup_testapp_python2.py index 041a3c933b..c75059f353 100644 --- a/testapps/setup_testapp_python2.py +++ b/testapps/setup_testapp_python2.py @@ -2,8 +2,7 @@ from distutils.core import setup from setuptools import find_packages -options = {'apk': {'debug': None, - 'requirements': 'sdl2,pyjnius,kivy,python2', +options = {'apk': {'requirements': 'sdl2,pyjnius,kivy,python2', 'android-api': 19, 'ndk-api': 19, 'ndk-dir': '/home/asandy/android/crystax-ndk-10.3.2', diff --git a/testapps/setup_testapp_python3crystax.py b/testapps/setup_testapp_python3crystax.py index bb3e18baf1..c85038efb6 100644 --- a/testapps/setup_testapp_python3crystax.py +++ b/testapps/setup_testapp_python3crystax.py @@ -2,8 +2,7 @@ from distutils.core import setup from setuptools import find_packages -options = {'apk': {'debug': None, - 'requirements': 'sdl2,pyjnius,kivy,python3crystax', +options = {'apk': {'requirements': 'sdl2,pyjnius,kivy,python3crystax', 'android-api': 19, 'ndk-api': 19, 'ndk-dir': '/home/asandy/android/crystax-ndk-10.3.2', From 47a8daf9b371e072cad8fe260baa16a923c70579 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Thu, 8 Nov 2018 22:06:51 +0000 Subject: [PATCH 43/50] Updated documentation to talk about Python 3 support --- doc/source/buildoptions.rst | 24 +++++++++++++----------- doc/source/quickstart.rst | 12 +++++++----- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/doc/source/buildoptions.rst b/doc/source/buildoptions.rst index 5b962b84ec..a9c2b472b2 100644 --- a/doc/source/buildoptions.rst +++ b/doc/source/buildoptions.rst @@ -17,16 +17,23 @@ This option builds Python 2.7.2 for your selected Android architecture. There are no special requirements, all the building is done locally. -The python2 build is also the way python-for-android originally -worked, even in the old toolchain. - python3 ~~~~~~~ -.. warning:: - Python3 support is experimental, and some of these details - may change as it is improved and fully stabilised. +Python3 is supported in two ways. The default method uses CPython 3.7+ +and works with any recent version of the Android NDK. + +Select Python 3 by adding it to your requirements, +e.g. ``--requirements=python3``. + + +CrystaX python3 +############### + +.. warning:: python-for-android originally supported Python 3 using the CrystaX + NDK. This support is now being phased out as CrystaX is no longer + actively developed. .. note:: You must manually download the `CrystaX NDK `__ and tell @@ -42,11 +49,6 @@ Google's official NDK which includes many improvements. You python3. You can get it `here `__. -The python3crystax build is handled quite differently to python2 so -there may be bugs or surprising behaviours. If you come across any, -feel free to `open an issue -`__. - .. _bootstrap_build_options: Bootstrap options diff --git a/doc/source/quickstart.rst b/doc/source/quickstart.rst index 62cde5081e..54b7e15440 100644 --- a/doc/source/quickstart.rst +++ b/doc/source/quickstart.rst @@ -114,15 +114,17 @@ Then, you can edit your ``~/.bashrc`` or other favorite shell to include new env # Adjust the paths! export ANDROIDSDK="$HOME/Documents/android-sdk-21" export ANDROIDNDK="$HOME/Documents/android-ndk-r10e" - export ANDROIDAPI="19" # Target API version of your application + export ANDROIDAPI="26" # Target API version of your application + export NDKAPI="19" # Minimum supported API version of your application export ANDROIDNDKVER="r10e" # Version of the NDK you installed You have the possibility to configure on any command the PATH to the SDK, NDK and Android API using: -- :code:`--sdk_dir PATH` as an equivalent of `$ANDROIDSDK` -- :code:`--ndk_dir PATH` as an equivalent of `$ANDROIDNDK` -- :code:`--android_api VERSION` as an equivalent of `$ANDROIDAPI` -- :code:`--ndk_version VERSION` as an equivalent of `$ANDROIDNDKVER` +- :code:`--sdk-dir PATH` as an equivalent of `$ANDROIDSDK` +- :code:`--ndk-dir PATH` as an equivalent of `$ANDROIDNDK` +- :code:`--android-api VERSION` as an equivalent of `$ANDROIDAPI` +- :code:`--ndk-api VERSION` as an equivalent of `$NDKAPI` +- :code:`--ndk-version VERSION` as an equivalent of `$ANDROIDNDKVER` Usage From 54c5b3811080ead92f137e96005413400d7de25f Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Thu, 8 Nov 2018 22:30:17 +0000 Subject: [PATCH 44/50] Improved handling of --android-api and --ndk-api --- pythonforandroid/build.py | 6 ++---- pythonforandroid/toolchain.py | 13 ++++++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/pythonforandroid/build.py b/pythonforandroid/build.py index 82a3aa5ba2..9d7d820d9c 100644 --- a/pythonforandroid/build.py +++ b/pythonforandroid/build.py @@ -280,8 +280,7 @@ def prepare_build_environment(self, ndk_dir = None if user_ndk_dir: ndk_dir = user_ndk_dir - if ndk_dir is not None: - info('Getting NDK dir from from user argument') + info('Getting NDK dir from from user argument') if ndk_dir is None: # The old P4A-specific dir ndk_dir = environ.get('ANDROIDNDK', None) if ndk_dir is not None: @@ -357,8 +356,7 @@ def prepare_build_environment(self, ndk_api = None if user_ndk_api: ndk_api = user_ndk_api - if ndk_api is not None: - info('Getting NDK API version (i.e. minimum supported API) from user argument') + info('Getting NDK API version (i.e. minimum supported API) from user argument') if ndk_api is None: ndk_api = environ.get('NDKAPI', None) if ndk_api is not None: diff --git a/pythonforandroid/toolchain.py b/pythonforandroid/toolchain.py index 165d51027f..1149fbde03 100644 --- a/pythonforandroid/toolchain.py +++ b/pythonforandroid/toolchain.py @@ -8,6 +8,7 @@ from __future__ import print_function from pythonforandroid import __version__ +from pythonforandroid.build import DEFAULT_NDK_API, DEFAULT_ANDROID_API def check_python_dependencies(): @@ -252,8 +253,13 @@ def __init__(self): '--ndk-dir', '--ndk_dir', dest='ndk_dir', default='', help='The filepath where the Android NDK is installed') generic_parser.add_argument( - '--android-api', '--android_api', dest='android_api', default=0, - type=int, help='The Android API level to build against.') + '--android-api', + '--android_api', + dest='android_api', + default=0, + type=int, + help=('The Android API level to build against defaults to {} if ' + 'not specified.').format(DEFAULT_ANDROID_API)) generic_parser.add_argument( '--ndk-version', '--ndk_version', dest='ndk_version', default='', help=('The version of the Android NDK. This is optional: ' @@ -261,7 +267,8 @@ def __init__(self): generic_parser.add_argument( '--ndk-api', type=int, default=0, help=('The Android API level to compile against. This should be your ' - '*minimal supported* API, not normally the same as your --android-api.')) + '*minimal supported* API, not normally the same as your --android-api. ' + 'Defaults to min(ANDROID_API, {}) if not specified.').format(DEFAULT_NDK_API)) generic_parser.add_argument( '--symlink-java-src', '--symlink_java_src', action='store_true', From c817ad0023dcf7411b3641950ea821d63992f18d Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Thu, 8 Nov 2018 22:36:55 +0000 Subject: [PATCH 45/50] Restructured discovery of android-api and ndk-api inputs --- pythonforandroid/build.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/pythonforandroid/build.py b/pythonforandroid/build.py index 9d7d820d9c..5f8b294b97 100644 --- a/pythonforandroid/build.py +++ b/pythonforandroid/build.py @@ -231,13 +231,11 @@ def prepare_build_environment(self, android_api = None if user_android_api: android_api = user_android_api - if android_api is not None: - info('Getting Android API version from user argument') - if android_api is None: - android_api = environ.get('ANDROIDAPI', None) - if android_api is not None: - info('Found Android API target in $ANDROIDAPI') - if android_api is None: + info('Getting Android API version from user argument') + elif 'ANDROIDAPI' in environ: + android_api = environ['ANDROIDAPI'] + info('Found Android API target in $ANDROIDAPI') + else: info('Android API target was not set manually, using ' 'the default of {}'.format(DEFAULT_ANDROID_API)) android_api = DEFAULT_ANDROID_API @@ -357,11 +355,10 @@ def prepare_build_environment(self, if user_ndk_api: ndk_api = user_ndk_api info('Getting NDK API version (i.e. minimum supported API) from user argument') - if ndk_api is None: + elif 'NDKAPI' in environ: ndk_api = environ.get('NDKAPI', None) - if ndk_api is not None: - info('Found Android API target in $NDKAPI') - if ndk_api is None: + info('Found Android API target in $NDKAPI') + else: ndk_api = min(self.android_api, DEFAULT_NDK_API) warning('NDK API target was not set manually, using ' 'the default of {} = min(android-api={}, default ndk-api={})'.format( From f74ce04f9148d6fe8faae1d680fb295469d8970b Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Thu, 8 Nov 2018 22:54:07 +0000 Subject: [PATCH 46/50] Updated CI scripts to run a python3 testapp --- .travis.yml | 2 +- Dockerfile | 1 + testapps/setup_testapp_python3.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7521fbddc6..f95004a990 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,12 +19,12 @@ env: - ANDROID_NDK_HOME=/opt/android/android-ndk - CRYSTAX_NDK_HOME=/opt/android/crystax-ndk matrix: - - COMMAND='. venv/bin/activate && cd testapps/ && python setup_testapp_python2.py apk --sdk-dir $ANDROID_SDK_HOME --ndk-dir $ANDROID_NDK_HOME' # overrides requirements to skip `peewee` pure python module, see: # https://github.com/kivy/python-for-android/issues/1263#issuecomment-390421054 - COMMAND='. venv/bin/activate && cd testapps/ && python setup_testapp_python2_sqlite_openssl.py apk --sdk-dir $ANDROID_SDK_HOME --ndk-dir $ANDROID_NDK_HOME --requirements sdl2,pyjnius,kivy,python2,openssl,requests,sqlite3,setuptools' - COMMAND='. venv/bin/activate && cd testapps/ && python setup_testapp_python2.py apk --sdk-dir $ANDROID_SDK_HOME --ndk-dir $ANDROID_NDK_HOME --bootstrap sdl2 --requirements python2,numpy' - COMMAND='. venv/bin/activate && cd testapps/ && python setup_testapp_python3crystax.py apk --sdk-dir $ANDROID_SDK_HOME --ndk-dir $CRYSTAX_NDK_HOME --requirements python3crystax,setuptools,android,sdl2,pyjnius,kivy' + - COMMAND='. venv/bin/activate && cd testapps/ && python setup_testapp_python3.py apk --sdk-dir $ANDROID_SDK_HOME --ndk-dir $ANDROID_NDK_HOME' # builds only the recipes that moved - COMMAND='. venv/bin/activate && ./ci/rebuild_updated_recipes.py' diff --git a/Dockerfile b/Dockerfile index b4cf6b619b..7edf52649c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -84,6 +84,7 @@ RUN mkdir --parents "${ANDROID_SDK_HOME}/.android/" && \ echo '### User Sources for Android SDK Manager' > "${ANDROID_SDK_HOME}/.android/repositories.cfg" RUN yes | "${ANDROID_SDK_HOME}/tools/bin/sdkmanager" --licenses RUN "${ANDROID_SDK_HOME}/tools/bin/sdkmanager" "platforms;android-19" && \ + "${ANDROID_SDK_HOME}/tools/bin/sdkmanager" "platforms;android-27" && \ "${ANDROID_SDK_HOME}/tools/bin/sdkmanager" "build-tools;26.0.2" && \ chmod +x "${ANDROID_SDK_HOME}/tools/bin/avdmanager" diff --git a/testapps/setup_testapp_python3.py b/testapps/setup_testapp_python3.py index a20323f857..d341cdaf60 100644 --- a/testapps/setup_testapp_python3.py +++ b/testapps/setup_testapp_python3.py @@ -3,7 +3,7 @@ from setuptools import find_packages options = {'apk': {'requirements': 'sdl2,pyjnius,kivy,python3', - 'android-api': 26, + 'android-api': 27, 'ndk-api': 19, 'dist-name': 'bdisttest_python3_googlendk', 'ndk-version': '10.3.2', From 12346d274d080b7dfe35b5f80c8cd021cdf72bd9 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Thu, 8 Nov 2018 23:36:40 +0000 Subject: [PATCH 47/50] Added ndk-api for sqlite,openssl testapp --- testapps/setup_testapp_python2_sqlite_openssl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testapps/setup_testapp_python2_sqlite_openssl.py b/testapps/setup_testapp_python2_sqlite_openssl.py index b6eec63c2b..ce1fc45365 100644 --- a/testapps/setup_testapp_python2_sqlite_openssl.py +++ b/testapps/setup_testapp_python2_sqlite_openssl.py @@ -2,9 +2,9 @@ from distutils.core import setup from setuptools import find_packages -options = {'apk': {#'debug': None, - 'requirements': 'sdl2,pyjnius,kivy,python2,openssl,requests,peewee,sqlite3', +options = {'apk': {'requirements': 'sdl2,pyjnius,kivy,python2,openssl,requests,peewee,sqlite3', 'android-api': 19, + 'ndk-api': 19, 'ndk-dir': '/home/sandy/android/crystax-ndk-10.3.2', 'dist-name': 'bdisttest_python2_sqlite_openssl', 'ndk-version': '10.3.2', From f150014ee3dbae3948831eccbd6c0c18640201f0 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sat, 10 Nov 2018 00:12:31 +0000 Subject: [PATCH 48/50] Added path to hostpython in python3 build env --- pythonforandroid/recipes/hostpython3/__init__.py | 7 ++++++- pythonforandroid/recipes/python3/__init__.py | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/pythonforandroid/recipes/hostpython3/__init__.py b/pythonforandroid/recipes/hostpython3/__init__.py index dd50b43a37..c34de54efd 100644 --- a/pythonforandroid/recipes/hostpython3/__init__.py +++ b/pythonforandroid/recipes/hostpython3/__init__.py @@ -3,6 +3,8 @@ from os.path import join, exists import sh +BUILD_SUBDIR = 'native-build' + class Hostpython3Recipe(Recipe): version = '3.7.1' @@ -20,11 +22,14 @@ def get_build_dir(self, arch=None): # Unlike other recipes, the hostpython build dir doesn't depend on the target arch return join(self.get_build_container_dir(), self.name) + def get_path_to_python(self): + return join(self.get_build_dir(), BUILD_SUBDIR) + def build_arch(self, arch): recipe_build_dir = self.get_build_dir(arch.arch) # Create a subdirectory to actually perform the build - build_dir = join(recipe_build_dir, 'native-build') + build_dir = join(recipe_build_dir, BUILD_SUBDIR) ensure_dir(build_dir) if not exists(join(build_dir, 'python')): diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index 4bdf53c93f..b212832ad0 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -83,6 +83,10 @@ def build_arch(self, arch): env['READELF'] = READELF env['STRIP'] = STRIP + env['PATH'] = '{hostpython_dir}:{old_path}'.format( + hostpython_dir=self.get_recipe('hostpython3', self.ctx).get_path_to_python(), + old_path=env['PATH']) + ndk_flags = ('--sysroot={ndk_sysroot} -D__ANDROID_API__={android_api} ' '-isystem {ndk_android_host}').format( ndk_sysroot=join(self.ctx.ndk_dir, 'sysroot'), From 82ad9e5d9da9742b64fb25a73295ba8b8f809e02 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sat, 10 Nov 2018 00:43:33 +0000 Subject: [PATCH 49/50] Updated python3 testapp ndk-api to 21 --- testapps/setup_testapp_python3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testapps/setup_testapp_python3.py b/testapps/setup_testapp_python3.py index d341cdaf60..891db01209 100644 --- a/testapps/setup_testapp_python3.py +++ b/testapps/setup_testapp_python3.py @@ -4,7 +4,7 @@ options = {'apk': {'requirements': 'sdl2,pyjnius,kivy,python3', 'android-api': 27, - 'ndk-api': 19, + 'ndk-api': 21, 'dist-name': 'bdisttest_python3_googlendk', 'ndk-version': '10.3.2', 'arch': 'armeabi-v7a', From b076cd98f1976ca9dce59e0073e9555354bb3949 Mon Sep 17 00:00:00 2001 From: Alexander Taylor Date: Sat, 10 Nov 2018 13:07:03 +0000 Subject: [PATCH 50/50] Removed Python3 testapp from CI for now --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f95004a990..b629513e59 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,6 @@ env: - COMMAND='. venv/bin/activate && cd testapps/ && python setup_testapp_python2_sqlite_openssl.py apk --sdk-dir $ANDROID_SDK_HOME --ndk-dir $ANDROID_NDK_HOME --requirements sdl2,pyjnius,kivy,python2,openssl,requests,sqlite3,setuptools' - COMMAND='. venv/bin/activate && cd testapps/ && python setup_testapp_python2.py apk --sdk-dir $ANDROID_SDK_HOME --ndk-dir $ANDROID_NDK_HOME --bootstrap sdl2 --requirements python2,numpy' - COMMAND='. venv/bin/activate && cd testapps/ && python setup_testapp_python3crystax.py apk --sdk-dir $ANDROID_SDK_HOME --ndk-dir $CRYSTAX_NDK_HOME --requirements python3crystax,setuptools,android,sdl2,pyjnius,kivy' - - COMMAND='. venv/bin/activate && cd testapps/ && python setup_testapp_python3.py apk --sdk-dir $ANDROID_SDK_HOME --ndk-dir $ANDROID_NDK_HOME' # builds only the recipes that moved - COMMAND='. venv/bin/activate && ./ci/rebuild_updated_recipes.py'