diff --git a/.travis.yml b/.travis.yml index e20a8cc75f..99dcd99930 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,5 +31,5 @@ before_script: - tox script: - - docker build --tag=p4a . + - docker build --tag=p4a --file Dockerfile.py2 . - docker run p4a /bin/sh -c "$COMMAND" diff --git a/Dockerfile b/Dockerfile.py2 similarity index 98% rename from Dockerfile rename to Dockerfile.py2 index e0d04d4b21..9d001c695b 100644 --- a/Dockerfile +++ b/Dockerfile.py2 @@ -3,7 +3,7 @@ # - python-for-android dependencies # # Build with: -# docker build --tag=p4a . +# docker build --tag=p4a --file Dockerfile.py2 . # # Run with: # docker run -it --rm p4a /bin/sh -c '. venv/bin/activate && p4a apk --help' diff --git a/Dockerfile.py3 b/Dockerfile.py3 new file mode 100644 index 0000000000..beace1537b --- /dev/null +++ b/Dockerfile.py3 @@ -0,0 +1,132 @@ +# Dockerfile with: +# - Android build environment +# - python-for-android dependencies +# +# Build with: +# docker build --tag=p4apy3 . +# +# Run with: +# docker run -it --rm p4apy3 /bin/sh -c '. venv/bin/activate && p4a apk --help' +# +# Or for interactive shell: +# docker run -it --rm p4apy3 +# +# Note: +# Use 'docker run' without '--rm' flag for keeping the container and use +# 'docker commit ' to extend the original image + +FROM ubuntu:18.04 + +ENV ANDROID_HOME="/opt/android" + +RUN apt -y update -qq \ + && apt -y install -qq --no-install-recommends curl unzip \ + && apt -y autoremove \ + && apt -y clean + + +ENV ANDROID_NDK_HOME="${ANDROID_HOME}/android-ndk" +ENV ANDROID_NDK_VERSION="16b" +ENV ANDROID_NDK_HOME_V="${ANDROID_NDK_HOME}-r${ANDROID_NDK_VERSION}" + +# get the latest version from https://developer.android.com/ndk/downloads/index.html +ENV ANDROID_NDK_ARCHIVE="android-ndk-r${ANDROID_NDK_VERSION}-linux-x86_64.zip" +ENV ANDROID_NDK_DL_URL="https://dl.google.com/android/repository/${ANDROID_NDK_ARCHIVE}" + +# download and install Android NDK +RUN curl --location --progress-bar --insecure \ + "${ANDROID_NDK_DL_URL}" \ + --output "${ANDROID_NDK_ARCHIVE}" \ + && mkdir --parents "${ANDROID_NDK_HOME_V}" \ + && unzip -q "${ANDROID_NDK_ARCHIVE}" -d "${ANDROID_HOME}" \ + && ln -sfn "${ANDROID_NDK_HOME_V}" "${ANDROID_NDK_HOME}" \ + && rm -rf "${ANDROID_NDK_ARCHIVE}" + + +ENV ANDROID_SDK_HOME="${ANDROID_HOME}/android-sdk" + +# get the latest version from https://developer.android.com/studio/index.html +ENV ANDROID_SDK_TOOLS_VERSION="3859397" +ENV ANDROID_SDK_TOOLS_ARCHIVE="sdk-tools-linux-${ANDROID_SDK_TOOLS_VERSION}.zip" +ENV ANDROID_SDK_TOOLS_DL_URL="https://dl.google.com/android/repository/${ANDROID_SDK_TOOLS_ARCHIVE}" + +# download and install Android SDK +RUN curl --location --progress-bar --insecure \ + "${ANDROID_SDK_TOOLS_DL_URL}" \ + --output "${ANDROID_SDK_TOOLS_ARCHIVE}" \ + && mkdir --parents "${ANDROID_SDK_HOME}" \ + && unzip -q "${ANDROID_SDK_TOOLS_ARCHIVE}" -d "${ANDROID_SDK_HOME}" \ + && rm -rf "${ANDROID_SDK_TOOLS_ARCHIVE}" + +# update Android SDK, install Android API, Build Tools... +RUN mkdir --parents "${ANDROID_SDK_HOME}/.android/" \ + && echo '### User Sources for Android SDK Manager' \ + > "${ANDROID_SDK_HOME}/.android/repositories.cfg" + +# accept Android licenses (JDK necessary!) +RUN apt -y update -qq \ + && apt -y install -qq --no-install-recommends openjdk-8-jdk \ + && apt -y autoremove \ + && apt -y clean +RUN yes | "${ANDROID_SDK_HOME}/tools/bin/sdkmanager" --licenses > /dev/null + +# download platforms, API, build tools +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" + + +ENV USER="user" +ENV HOME_DIR="/home/${USER}" +ENV WORK_DIR="${HOME_DIR}" \ + PATH="${HOME_DIR}/.local/bin:${PATH}" + +# install system dependencies +RUN apt -y update -qq \ + && apt -y install -qq --no-install-recommends \ + python3 virtualenv python3-pip wget lbzip2 patch sudo \ + && apt -y autoremove \ + && apt -y clean + +# build dependencies +# https://buildozer.readthedocs.io/en/latest/installation.html#android-on-ubuntu-16-04-64bit +RUN dpkg --add-architecture i386 \ + && apt -y update -qq \ + && apt -y install -qq --no-install-recommends \ + build-essential ccache git python3 python3-dev \ + libncurses5:i386 libstdc++6:i386 libgtk2.0-0:i386 \ + libpangox-1.0-0:i386 libpangoxft-1.0-0:i386 libidn11:i386 \ + zip zlib1g-dev zlib1g:i386 \ + && apt -y autoremove \ + && apt -y clean + +# specific recipes dependencies (e.g. libffi requires autoreconf binary) +RUN apt -y update -qq \ + && apt -y install -qq --no-install-recommends \ + autoconf automake cmake gettext libltdl-dev libtool pkg-config \ + && apt -y autoremove \ + && apt -y clean + + +# prepare non root env +RUN useradd --create-home --shell /bin/bash ${USER} + +# with sudo access and no password +RUN usermod -append --groups sudo ${USER} +RUN echo "%sudo ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + + +RUN pip3 install --upgrade cython==0.28.6 + +WORKDIR ${WORK_DIR} +COPY . ${WORK_DIR} + +# user needs ownership/write access to these directories +RUN chown --recursive ${USER} ${WORK_DIR} ${ANDROID_SDK_HOME} +USER ${USER} + +# install python-for-android from current branch +RUN virtualenv --python=python3 venv \ + && . venv/bin/activate \ + && pip3 install -e . diff --git a/pythonforandroid/recipes/android/__init__.py b/pythonforandroid/recipes/android/__init__.py index 772c3dd277..51b4192e5b 100644 --- a/pythonforandroid/recipes/android/__init__.py +++ b/pythonforandroid/recipes/android/__init__.py @@ -1,10 +1,10 @@ +from __future__ import unicode_literals from pythonforandroid.recipe import CythonRecipe, IncludedFilesBehaviour from pythonforandroid.util import current_directory from pythonforandroid.patching import will_build from pythonforandroid import logger from os.path import join -from sys import version_info class AndroidRecipe(IncludedFilesBehaviour, CythonRecipe): @@ -26,13 +26,18 @@ def get_recipe_env(self, arch): def prebuild_arch(self, arch): super(AndroidRecipe, self).prebuild_arch(arch) + ctx_bootstrap = self.ctx.bootstrap.name + # define macros for Cython, C, Python tpxi = 'DEF {} = {}\n' th = '#define {} {}\n' tpy = '{} = {}\n' - bootstrap = self.ctx.bootstrap.name - bootstrap = bootstrap_name = bootstrap if version_info >= (3, ) else bootstrap.decode('utf-8') + # make sure bootstrap name is in unicode + if isinstance(ctx_bootstrap, bytes): + ctx_bootstrap = ctx_bootstrap.decode('utf-8') + bootstrap = bootstrap_name = ctx_bootstrap + is_sdl2 = bootstrap_name in ('sdl2', 'sdl2python3', 'sdl2_gradle') is_pygame = bootstrap_name in ('pygame',) is_webview = bootstrap_name in ('webview',) @@ -40,13 +45,16 @@ def prebuild_arch(self, arch): if is_sdl2 or is_webview: if is_sdl2: bootstrap = 'sdl2' - java_ns = u'org.kivy.android' - jni_ns = u'org/kivy/android' + java_ns = 'org.kivy.android' + jni_ns = 'org/kivy/android' elif is_pygame: - java_ns = 'org.renpy.android' - jni_ns = 'org/renpy/android' + java_ns = b'org.renpy.android' + jni_ns = b'org/renpy/android' else: - logger.error('unsupported bootstrap for android recipe: {}'.format(bootstrap_name)) + logger.error(( + 'unsupported bootstrap for android recipe: {}' + ''.format(bootstrap_name) + )) exit(1) config = { @@ -58,22 +66,28 @@ def prebuild_arch(self, arch): 'JNI_NAMESPACE': jni_ns, } + # create config files for Cython, C and Python with ( current_directory(self.get_build_dir(arch.arch))), ( open(join('android', 'config.pxi'), 'w')) as fpxi, ( open(join('android', 'config.h'), 'w')) as fh, ( open(join('android', 'config.py'), 'w')) as fpy: + for key, value in config.items(): fpxi.write(tpxi.format(key, repr(value))) fpy.write(tpy.format(key, repr(value))) - fh.write(th.format(key, - value if isinstance(value, int) - else '"{}"'.format(value))) + + fh.write(th.format( + key, + value if isinstance(value, int) else '"{}"'.format(value) + )) self.config_env[key] = str(value) if is_sdl2: fh.write('JNIEnv *SDL_AndroidGetJNIEnv(void);\n') - fh.write('#define SDL_ANDROID_GetJNIEnv SDL_AndroidGetJNIEnv\n') + fh.write( + '#define SDL_ANDROID_GetJNIEnv SDL_AndroidGetJNIEnv\n' + ) elif is_pygame: fh.write('JNIEnv *SDL_ANDROID_GetJNIEnv(void);\n')