Removed android_old target and began update to sdk install

This commit is contained in:
Alexander Taylor 2019-02-01 13:04:06 +00:00
parent 74e26fe6f2
commit 4936d31828
3 changed files with 267 additions and 420 deletions

View file

@ -155,7 +155,6 @@ class Buildozer(object):
def set_target(self, target): def set_target(self, target):
'''Set the target to use (one of buildozer.targets, such as "android") '''Set the target to use (one of buildozer.targets, such as "android")
''' '''
target = self.translate_target(target)
self.targetname = target self.targetname = target
m = __import__('buildozer.targets.{0}'.format(target), m = __import__('buildozer.targets.{0}'.format(target),
fromlist=['buildozer']) fromlist=['buildozer'])
@ -919,23 +918,6 @@ class Buildozer(object):
# command line invocation # command line invocation
# #
def translate_target(self, target, inverse=False):
# FIXME at some point, refactor to remove completely android old toolchain
if inverse:
if target == "android":
target = "android_old"
elif target == "android_new":
target = "android"
else:
if target == "android":
target = "android_new"
elif target == "android_new":
self.error("ERROR: The target android_new is now android")
exit(1)
elif target == "android_old":
target = "android"
return target
def targets(self): def targets(self):
for fn in listdir(join(dirname(__file__), 'targets')): for fn in listdir(join(dirname(__file__), 'targets')):
if fn.startswith('.') or fn.startswith('__'): if fn.startswith('.') or fn.startswith('__'):
@ -946,7 +928,7 @@ class Buildozer(object):
try: try:
m = __import__('buildozer.targets.{0}'.format(target), m = __import__('buildozer.targets.{0}'.format(target),
fromlist=['buildozer']) fromlist=['buildozer'])
yield self.translate_target(target, inverse=True), m yield target, m
except NotImplementedError: except NotImplementedError:
pass pass
except: except:
@ -1054,7 +1036,7 @@ class Buildozer(object):
# maybe it's a target? # maybe it's a target?
targets = [x[0] for x in self.targets()] targets = [x[0] for x in self.targets()]
if self.translate_target(command, inverse=True) not in targets: if command not in targets:
print('Unknown command/target {}'.format(self.translate_target(command, inverse=True))) print('Unknown command/target {}'.format(self.translate_target(command, inverse=True)))
exit(1) exit(1)

View file

@ -28,8 +28,7 @@ import ast
import sh import sh
from pipes import quote from pipes import quote
from sys import platform, executable from sys import platform, executable
from buildozer import BuildozerException from buildozer import BuildozerException, USE_COLOR, IS_PY3
from buildozer import IS_PY3
from buildozer.target import Target from buildozer.target import Target
from os import environ from os import environ
from os.path import exists, join, realpath, expanduser, basename, relpath from os.path import exists, join, realpath, expanduser, basename, relpath
@ -42,10 +41,55 @@ from distutils.version import LooseVersion
class TargetAndroid(Target): class TargetAndroid(Target):
targetname = 'android_old' targetname = 'android'
p4a_directory = "python-for-android" p4a_directory = "python-for-android"
p4a_branch = 'old_toolchain' p4a_branch = 'master'
p4a_apk_cmd = "python build.py" p4a_apk_cmd = "apk --debug --bootstrap="
extra_p4a_args = ''
def __init__(self, *args, **kwargs):
super(TargetAndroid, self).__init__(*args, **kwargs)
self._build_dir = join(self.buildozer.platform_dir, 'build')
executable = sys.executable or 'python'
self._p4a_cmd = '{} -m pythonforandroid.toolchain '.format(executable)
self._p4a_bootstrap = self.buildozer.config.getdefault(
'app', 'p4a.bootstrap', 'sdl2')
self.p4a_apk_cmd += self._p4a_bootstrap
color = 'always' if USE_COLOR else 'never'
self.extra_p4a_args = ' --color={} --storage-dir="{}"'.format(
color, self._build_dir)
# minapi should match ndk-api, so can use the same default if
# nothing is specified
ndk_api = self.buildozer.config.getdefault(
'app', 'android.ndk_api', self.android_minapi)
self.extra_p4a_args += ' --ndk-api={}'.format(ndk_api)
hook = self.buildozer.config.getdefault("app", "p4a.hook", None)
if hook is not None:
self.extra_p4a_args += ' --hook={}'.format(realpath(hook))
port = self.buildozer.config.getdefault('app', 'p4a.port', None)
if port is not None:
self.extra_p4a_args += ' --port={}'.format(port)
def _p4a(self, cmd, **kwargs):
if not hasattr(self, "pa_dir"):
self.pa_dir = join(self.buildozer.platform_dir, self.p4a_directory)
kwargs.setdefault('cwd', self.pa_dir)
return self.buildozer.cmd(self._p4a_cmd + cmd + self.extra_p4a_args, **kwargs)
def _sdkmanager(self, *args):
"""Call the sdkmanager in our Android SDK with the given arguments."""
sdkmanager_path = os.path.join(self.buildozer.global_platform_dir,
'android-sdk',
'tools',
'bin',
'sdkmanager')
assert os.path.isfile(sdkmanager_path)
return self.buildozer.cmd(sdkmanager_path + ' ' + ' '.join(args),
get_stdout=True)
@property @property
def android_sdk_version(self): def android_sdk_version(self):
@ -73,10 +117,8 @@ class TargetAndroid(Target):
'app', 'android.sdk_path', '')) 'app', 'android.sdk_path', ''))
if directory: if directory:
return realpath(directory) return realpath(directory)
version = self.buildozer.config.getdefault('app', 'android.sdk',
self.android_sdk_version)
return join(self.buildozer.global_platform_dir, return join(self.buildozer.global_platform_dir,
'android-sdk-{0}'.format(version)) 'android-sdk')
@property @property
def android_ndk_dir(self): def android_ndk_dir(self):
@ -248,30 +290,31 @@ class TargetAndroid(Target):
self.buildozer.info('Android SDK is missing, downloading') self.buildozer.info('Android SDK is missing, downloading')
if platform in ('win32', 'cygwin'): if platform in ('win32', 'cygwin'):
archive = 'android-sdk_r{0}-windows.zip' archive = 'sdk-tools-windows-4333796.zip'
unpacked = 'android-sdk-windows' unpacked = 'android-sdk-windows'
elif platform in ('darwin', ): elif platform in ('darwin', ):
archive = 'android-sdk_r{0}-macosx.zip' archive = 'sdk-tools-darwin-4333796.zip'
unpacked = 'android-sdk-macosx' unpacked = 'android-sdk-macosx'
elif platform.startswith('linux'): elif platform.startswith('linux'):
archive = 'android-sdk_r{0}-linux.tgz' archive = 'sdk-tools-linux-4333796.zip'
unpacked = 'android-sdk-linux' unpacked = 'android-sdk-linux'
else: else:
raise SystemError('Unsupported platform: {0}'.format(platform)) raise SystemError('Unsupported platform: {0}'.format(platform))
if not os.path.exists(sdk_dir):
os.makedirs(sdk_dir)
archive = archive.format(self.android_sdk_version) archive = archive.format(self.android_sdk_version)
url = 'http://dl.google.com/android/' url = 'http://dl.google.com/android/repository/'
self.buildozer.download(url, self.buildozer.download(url,
archive, archive,
cwd=self.buildozer.global_platform_dir) cwd=sdk_dir)
self.buildozer.info('Unpacking Android SDK') self.buildozer.info('Unpacking Android SDK')
self.buildozer.file_extract(archive, self.buildozer.file_extract(archive,
cwd=self.buildozer.global_platform_dir) cwd=sdk_dir)
self.buildozer.file_rename(unpacked,
sdk_dir, self.buildozer.info('Android SDK tools base installation done.')
cwd=self.buildozer.global_platform_dir)
self.buildozer.info('Android SDK installation done.')
return sdk_dir return sdk_dir
@ -341,21 +384,24 @@ class TargetAndroid(Target):
self.buildozer.info('Android NDK installation done.') self.buildozer.info('Android NDK installation done.')
return ndk_dir return ndk_dir
def _android_list_sdk(self, include_all=False): def _android_list_build_tools_versions(self):
cmd = '{} list sdk -u -e'.format(self.android_cmd) available_packages = self._sdkmanager('--list')
if include_all:
cmd += ' -a'
available_packages = self.buildozer.cmd(
cmd,
cwd=self.buildozer.global_platform_dir,
get_stdout=True)[0]
# get only the line like -> id: 5 or "build-tools-19.0.1" lines = available_packages[0].split('\n')
# and extract the name part.
print(available_packages) build_tools_versions = []
return [x.split('"')[1]
for x in available_packages.splitlines() for line in lines:
if x.startswith('id: ')] if not line.strip().startswith('build-tools;'):
continue
package_name = line.strip().split(' ')[0]
assert package_name.count(';') == 1, (
'could not parse package "{}"'.format(package_name))
version = package_name.split(';')[1]
build_tools_versions.append(parse(version))
return build_tools_versions
def _android_update_sdk(self, packages): def _android_update_sdk(self, packages):
from pexpect import EOF from pexpect import EOF
@ -376,9 +422,6 @@ class TargetAndroid(Target):
break break
child.sendline('y') child.sendline('y')
def _build_package_string(self, package_name, version):
return '{}-{}'.format(package_name, version)
def _read_version_subdir(self, *args): def _read_version_subdir(self, *args):
versions = [] versions = []
if not os.path.exists(join(*args)): if not os.path.exists(join(*args)):
@ -420,68 +463,38 @@ class TargetAndroid(Target):
if self.buildozer.state.get(cache_key, None) == cache_value: if self.buildozer.state.get(cache_key, None) == cache_value:
return True return True
# 3 pass installation.
if not os.access(self.android_cmd, os.X_OK):
self.buildozer.cmd('chmod ug+x {}'.format(self.android_cmd))
# 1. update the tool and platform-tools if needed # 1. update the tool and platform-tools if needed
packages = self._android_list_sdk()
skip_upd = self.buildozer.config.getdefault('app',
'android.skip_update', False)
if 'tools' in packages or 'platform-tools' in packages:
if not skip_upd:
if WSL:
# WSL (Windows Subsystem for Linux) allows running
# linux from windows 10, but some windows
# limitations still apply, namely you can't rename a
# directory that a program was started from, which
# is what the tools updates cause, and we end up
# with an empty dir, so we need to run from a
# different place, and the updater is still looking
# for things in tools, and specifically renames the
# tool dir, hence, moving and making a symlink
# works.
sh.mv(
join(self.android_sdk_dir, 'tools'),
join(self.android_sdk_dir, 'tools.save')
)
sh.ln(
'-s',
join(self.android_sdk_dir, 'tools.save'),
join(self.android_sdk_dir, 'tools')
)
old_android_cmd = self.android_cmd
self.android_cmd = join(
self.android_sdk_dir,
'tools.save',
self.android_cmd.split('/')[-1]
)
self._android_update_sdk('tools,platform-tools') skip_upd = self.buildozer.config.getdefault(
'app', 'android.skip_update', False)
if WSL: if not skip_upd:
self.android_cmd = old_android_cmd # just calling sdkmanager with the items will install them if necessary
sh.rm('-rf', join(self.android_sdk_dir, 'tools.save')) self._sdkmanager('tools', 'platform-tools')
else: self._sdkmanager('--update')
self.buildozer.info('Skipping Android SDK update due to spec file setting') else:
self.buildozer.info('Skipping Android SDK update due to spec file setting')
self.buildozer.info('Note: this also prevents installing missing '
'SDK components')
# 2. install the latest build tool # 2. install the latest build tool
v_build_tools = self._read_version_subdir(self.android_sdk_dir, installed_v_build_tools = self._read_version_subdir(self.android_sdk_dir,
'build-tools') 'build-tools')
packages = self._android_list_sdk(include_all=True) available_v_build_tools = self._android_list_build_tools_versions()
ver = self._find_latest_package(packages, 'build-tools-') if not available_v_build_tools:
if ver and ver > v_build_tools and not skip_upd: self.buildozer.warning('Did not find any build tools available to download')
self._android_update_sdk(self._build_package_string('build-tools', ver))
latest_v_build_tools = sorted(available_v_build_tools)[-1]
if latest_v_build_tools > installed_v_build_tools and not skip_upd:
self._sdkmanager('"build-tools;{}"'.format(latest_v_build_tools))
# 2. check aidl can be run # 2. check aidl can be run
self._check_aidl(v_build_tools) self._check_aidl(v_build_tools)
# 3. finally, install the android for the current api # 3. finally, install the android for the current api
android_platform = join(self.android_sdk_dir, 'platforms', 'android-{0}'.format(self.android_api)) android_platform = join(self.android_sdk_dir, 'platforms', 'android-{0}'.format(self.android_api))
if not self.buildozer.file_exists(android_platform): if not self.buildozer.file_exists(android_platform) and not skip_upd:
packages = self._android_list_sdk() self._sdkmanager('"platforms;android-{}"'.format(self.android_api))
android_package = 'android-{}'.format(self.android_api)
if android_package in packages and not skip_upd:
self._android_update_sdk(android_package)
self.buildozer.info('Android packages installation done.') self.buildozer.info('Android packages installation done.')
@ -586,81 +599,190 @@ class TargetAndroid(Target):
options = "--user" options = "--user"
if "VIRTUAL_ENV" in os.environ or "CONDA_PREFIX" in os.environ: if "VIRTUAL_ENV" in os.environ or "CONDA_PREFIX" in os.environ:
options = "" options = ""
print('pa dir', self.pa_dir)
print('pip_deps', pip_deps)
cmd('{} -m pip install -q {} {}'.format(executable, options, " ".join(pip_deps))) cmd('{} -m pip install -q {} {}'.format(executable, options, " ".join(pip_deps)))
def get_available_packages(self):
available_modules = self.buildozer.cmd('./distribute.sh -l',
cwd=self.pa_dir,
get_stdout=True)[0]
if not available_modules.startswith('Available modules:'):
self.buildozer.error('Python-for-android invalid output for -l')
return available_modules[19:].splitlines()[0].split()
def compile_platform(self): def compile_platform(self):
# for android, the compilation depends really on the app requirements. app_requirements = self.buildozer.config.getlist(
# compile the distribution only if the requirements changed. 'app', 'requirements', '')
last_requirements = self.buildozer.state.get('android.requirements',
'')
app_requirements = self.buildozer.config.getlist('app', 'requirements',
'')
# we need to extract the requirements that python-for-android knows
# about
available_modules = self.get_available_packages()
onlyname = lambda x: x.split('==')[0]
android_requirements = [x
for x in app_requirements
if onlyname(x) in available_modules]
need_compile = 0
if last_requirements != android_requirements:
need_compile = 1
dist_name = self.buildozer.config.get('app', 'package.name') dist_name = self.buildozer.config.get('app', 'package.name')
dist_dir = join(self.pa_dir, 'dist', dist_name) local_recipes = self.get_local_recipes_dir()
dist_file = join(dist_dir, 'private', 'include', 'python2.7', requirements = ','.join(app_requirements)
'pyconfig.h') options = []
if not exists(dist_file):
need_compile = 1
# len('requirements.source.') == 20, so use name[20:]
source_dirs = { source_dirs = {
'P4A_{}_DIR'.format(name[20:]): realpath(expanduser(value)) 'P4A_{}_DIR'.format(name[20:]): realpath(expanduser(value))
for name, value in self.buildozer.config.items('app') for name, value in self.buildozer.config.items('app')
if name.startswith('requirements.source.') if name.startswith('requirements.source.')
} }
if source_dirs: if source_dirs:
self.buildozer.environ.update(source_dirs) self.buildozer.environ.update(source_dirs)
self.buildozer.info('Using custom source dirs:\n {}'.format( self.buildozer.info('Using custom source dirs:\n {}'.format(
'\n '.join(['{} = {}'.format(k, v) '\n '.join(['{} = {}'.format(k, v)
for k, v in source_dirs.items()]))) for k, v in source_dirs.items()])))
last_source_requirements = self.buildozer.state.get( if self.buildozer.config.getbooldefault('app', 'android.copy_libs', True):
'android.requirements.source', {}) options.append("--copy-libs")
if source_dirs != last_source_requirements: # support for recipes in a local directory within the project
need_compile = 1 if local_recipes:
options.append('--local-recipes')
options.append(local_recipes)
config = self.buildozer.config
self._p4a(
("create --dist_name={} --bootstrap={} --requirements={} "
"--arch {} {}").format(
dist_name, self._p4a_bootstrap, requirements,
config.getdefault('app', 'android.arch', "armeabi-v7a"), " ".join(options)),
get_stdout=True)[0]
if not need_compile: def get_available_packages(self):
self.buildozer.info('Distribution already compiled, pass.') return True
return
modules_str = ' '.join(android_requirements) def get_dist_dir(self, dist_name):
cmd = self.buildozer.cmd return join(self._build_dir, 'dists', dist_name)
self.buildozer.debug('Clean and build python-for-android')
self.buildozer.rmdir(dist_dir) # Delete existing distribution to stop
# p4a complaining
cmd('./distribute.sh -m "{0}" -d "{1}"'.format(modules_str, dist_name),
cwd=self.pa_dir)
self.buildozer.debug('Remove temporary build files')
self.buildozer.rmdir(join(self.pa_dir, 'build'))
self.buildozer.rmdir(join(self.pa_dir, '.packages'))
self.buildozer.rmdir(join(self.pa_dir, 'src', 'jni', 'obj', 'local'))
self.buildozer.info('Distribution compiled.')
# ensure we will not compile again def get_local_recipes_dir(self):
self.buildozer.state['android.requirements'] = android_requirements local_recipes = self.buildozer.config.getdefault('app', 'p4a.local_recipes')
self.buildozer.state['android.requirements.source'] = source_dirs return realpath(expanduser(local_recipes)) if local_recipes else None
self.buildozer.state.sync()
def execute_build_package(self, build_cmd):
# wrapper from previous old_toolchain to new toolchain
dist_name = self.buildozer.config.get('app', 'package.name')
local_recipes = self.get_local_recipes_dir()
cmd = [self.p4a_apk_cmd, "--dist_name", dist_name]
for args in build_cmd:
option, values = args[0], args[1:]
if option == "debug":
continue
elif option == "release":
cmd.append("--release")
if self.check_p4a_sign_env(True):
cmd.append("--sign")
continue
if option == "--window":
cmd.append("--window")
elif option == "--sdk":
cmd.append("--android_api")
cmd.extend(values)
else:
cmd.extend(args)
# support for presplash background color
presplash_color = self.buildozer.config.getdefault('app', 'android.presplash_color', None)
if presplash_color:
cmd.append('--presplash-color')
cmd.append("'{}'".format(presplash_color))
# support for services
services = self.buildozer.config.getlist('app', 'services', [])
for service in services:
cmd.append("--service")
cmd.append(service)
# support for copy-libs
if self.buildozer.config.getbooldefault('app', 'android.copy_libs', True):
cmd.append("--copy-libs")
# support for recipes in a local directory within the project
if local_recipes:
cmd.append('--local-recipes')
cmd.append(local_recipes)
# support for blacklist/whitelist filename
whitelist_src = self.buildozer.config.getdefault('app', 'android.whitelist_src', None)
blacklist_src = self.buildozer.config.getdefault('app', 'android.blacklist_src', None)
if whitelist_src:
cmd.append('--whitelist')
cmd.append(realpath(whitelist_src))
if blacklist_src:
cmd.append('--blacklist')
cmd.append(realpath(blacklist_src))
# support for aars
aars = self.buildozer.config.getlist('app', 'android.add_aars', [])
for aar in aars:
cmd.append('--add-aar')
cmd.append(realpath(aar))
# support for gradle dependencies
gradle_dependencies = self.buildozer.config.getlist('app', 'android.gradle_dependencies', [])
for gradle_dependency in gradle_dependencies:
cmd.append('--depend')
cmd.append(gradle_dependency)
cmd.append('--arch')
cmd.append(self.buildozer.config.getdefault('app', 'android.arch', "armeabi-v7a"))
cmd = " ".join(cmd)
self._p4a(cmd)
def get_release_mode(self):
if self.check_p4a_sign_env():
return "release"
return "release-unsigned"
def check_p4a_sign_env(self, error=False):
keys = ["KEYALIAS", "KEYSTORE_PASSWD", "KEYSTORE", "KEYALIAS_PASSWD"]
check = True
for key in keys:
key = "P4A_RELEASE_{}".format(key)
if key not in os.environ:
if error:
self.buildozer.error(
("Asking for release but {} is missing"
"--sign will not be passed").format(key))
check = False
return check
def cmd_run(self, *args):
entrypoint = self.buildozer.config.getdefault(
'app', 'android.entrypoint')
if not entrypoint:
self.buildozer.config.set('app', 'android.entrypoint', 'org.kivy.android.PythonActivity')
super(TargetAndroid, self).cmd_run(*args)
entrypoint = self.buildozer.config.getdefault(
'app', 'android.entrypoint', 'org.renpy.android.PythonActivity')
package = self._get_package()
# push on the device
for serial in self.serials:
self.buildozer.environ['ANDROID_SERIAL'] = serial
self.buildozer.info('Run on {}'.format(serial))
self.buildozer.cmd(
'{adb} shell am start -n {package}/{entry} -a {entry}'.format(
adb=self.adb_cmd,
package=package,
entry=entrypoint),
cwd=self.buildozer.global_platform_dir)
self.buildozer.environ.pop('ANDROID_SERIAL', None)
self.buildozer.info('Application started.')
def cmd_p4a(self, *args):
'''
Run p4a commands. Args must come after --, or
use --alias to make an alias
'''
self.check_requirements()
self.install_platform()
args = args[0]
if args and args[0] == '--alias':
print('To set up p4a in this shell session, execute:')
print(' alias p4a=$(buildozer {} p4a --alias 2>&1 >/dev/null)'
.format(self.targetname))
sys.stderr.write('PYTHONPATH={} {}\n'.format(self.pa_dir, self._p4a_cmd))
else:
self._p4a(' '.join(args) if args else '')
def cmd_clean(self, *args):
'''
Clean the build and distribution
'''
self._p4a("clean_builds")
self._p4a("clean_dists")
def _get_package(self): def _get_package(self):
config = self.buildozer.config config = self.buildozer.config
@ -678,22 +800,6 @@ class TargetAndroid(Target):
for wl in p4a_whitelist: for wl in p4a_whitelist:
fd.write(wl + '\n') fd.write(wl + '\n')
def get_dist_dir(self, dist_name):
return join(self.pa_dir, 'dist', dist_name)
@property
def dist_dir(self):
dist_name = self.buildozer.config.get('app', 'package.name')
return self.get_dist_dir(dist_name)
def execute_build_package(self, build_cmd):
dist_name = self.buildozer.config.get('app', 'package.name')
cmd = [self.p4a_apk_cmd]
for args in build_cmd:
cmd.append(" ".join(args))
cmd = " ".join(cmd)
self.buildozer.cmd(cmd, cwd=self.get_dist_dir(dist_name))
def build_package(self): def build_package(self):
dist_name = self.buildozer.config.get('app', 'package.name') dist_name = self.buildozer.config.get('app', 'package.name')
dist_dir = self.get_dist_dir(dist_name) dist_dir = self.get_dist_dir(dist_name)
@ -892,9 +998,6 @@ class TargetAndroid(Target):
self.buildozer.state['android:latestapk'] = apk_dest self.buildozer.state['android:latestapk'] = apk_dest
self.buildozer.state['android:latestmode'] = self.build_mode self.buildozer.state['android:latestmode'] = self.build_mode
def get_release_mode(self):
return "release-unsigned"
def _update_libraries_references(self, dist_dir): def _update_libraries_references(self, dist_dir):
# ensure the project.properties exist # ensure the project.properties exist
project_fn = join(dist_dir, 'project.properties') project_fn = join(dist_dir, 'project.properties')
@ -1029,27 +1132,6 @@ class TargetAndroid(Target):
self.buildozer.info('Application pushed.') self.buildozer.info('Application pushed.')
def cmd_run(self, *args):
super(TargetAndroid, self).cmd_run(*args)
entrypoint = self.buildozer.config.getdefault(
'app', 'android.entrypoint', 'org.renpy.android.PythonActivity')
package = self._get_package()
# push on the device
for serial in self.serials:
self.buildozer.environ['ANDROID_SERIAL'] = serial
self.buildozer.info('Run on {}'.format(serial))
self.buildozer.cmd(
'{adb} shell am start -n {package}/{entry} -a {entry}'.format(
adb=self.adb_cmd,
package=package,
entry=entrypoint),
cwd=self.buildozer.global_platform_dir)
self.buildozer.environ.pop('ANDROID_SERIAL', None)
self.buildozer.info('Application started.')
def cmd_logcat(self, *args): def cmd_logcat(self, *args):
'''Show the log from the device '''Show the log from the device
''' '''

View file

@ -1,217 +0,0 @@
# coding=utf-8
'''
Android target, based on python-for-android project
'''
import sys
import os
from buildozer import USE_COLOR
from buildozer.targets.android import TargetAndroid
from os.path import join, expanduser, realpath
class TargetAndroidNew(TargetAndroid):
targetname = 'android'
p4a_branch = "master"
p4a_directory = "python-for-android-new-toolchain"
p4a_apk_cmd = "apk --debug --bootstrap="
extra_p4a_args = ''
def __init__(self, *args, **kwargs):
super(TargetAndroidNew, self).__init__(*args, **kwargs)
self._build_dir = join(self.buildozer.platform_dir, 'build')
executable = sys.executable or 'python'
self._p4a_cmd = '{} -m pythonforandroid.toolchain '.format(executable)
self._p4a_bootstrap = self.buildozer.config.getdefault(
'app', 'p4a.bootstrap', 'sdl2')
self.p4a_apk_cmd += self._p4a_bootstrap
color = 'always' if USE_COLOR else 'never'
self.extra_p4a_args = ' --color={} --storage-dir="{}"'.format(
color, self._build_dir)
# minapi should match ndk-api, so can use the same default if
# nothing is specified
ndk_api = self.buildozer.config.getdefault(
'app', 'android.ndk_api', self.android_minapi)
self.extra_p4a_args += ' --ndk-api={}'.format(ndk_api)
hook = self.buildozer.config.getdefault("app", "p4a.hook", None)
if hook is not None:
self.extra_p4a_args += ' --hook={}'.format(realpath(hook))
port = self.buildozer.config.getdefault('app', 'p4a.port', None)
if port is not None:
self.extra_p4a_args += ' --port={}'.format(port)
def _p4a(self, cmd, **kwargs):
if not hasattr(self, "pa_dir"):
self.pa_dir = join(self.buildozer.platform_dir, self.p4a_directory)
kwargs.setdefault('cwd', self.pa_dir)
return self.buildozer.cmd(self._p4a_cmd + cmd + self.extra_p4a_args, **kwargs)
def get_available_packages(self):
return True
def compile_platform(self):
app_requirements = self.buildozer.config.getlist(
'app', 'requirements', '')
dist_name = self.buildozer.config.get('app', 'package.name')
local_recipes = self.get_local_recipes_dir()
requirements = ','.join(app_requirements)
options = []
source_dirs = {
'P4A_{}_DIR'.format(name[20:]): realpath(expanduser(value))
for name, value in self.buildozer.config.items('app')
if name.startswith('requirements.source.')
}
if source_dirs:
self.buildozer.environ.update(source_dirs)
self.buildozer.info('Using custom source dirs:\n {}'.format(
'\n '.join(['{} = {}'.format(k, v)
for k, v in source_dirs.items()])))
if self.buildozer.config.getbooldefault('app', 'android.copy_libs', True):
options.append("--copy-libs")
# support for recipes in a local directory within the project
if local_recipes:
options.append('--local-recipes')
options.append(local_recipes)
config = self.buildozer.config
self._p4a(
("create --dist_name={} --bootstrap={} --requirements={} "
"--arch {} {}").format(
dist_name, self._p4a_bootstrap, requirements,
config.getdefault('app', 'android.arch', "armeabi-v7a"), " ".join(options)),
get_stdout=True)[0]
def get_dist_dir(self, dist_name):
return join(self._build_dir, 'dists', dist_name)
def get_local_recipes_dir(self):
local_recipes = self.buildozer.config.getdefault('app', 'p4a.local_recipes')
return realpath(expanduser(local_recipes)) if local_recipes else None
def execute_build_package(self, build_cmd):
# wrapper from previous old_toolchain to new toolchain
dist_name = self.buildozer.config.get('app', 'package.name')
local_recipes = self.get_local_recipes_dir()
cmd = [self.p4a_apk_cmd, "--dist_name", dist_name]
for args in build_cmd:
option, values = args[0], args[1:]
if option == "debug":
continue
elif option == "release":
cmd.append("--release")
if self.check_p4a_sign_env(True):
cmd.append("--sign")
continue
if option == "--window":
cmd.append("--window")
elif option == "--sdk":
cmd.append("--android_api")
cmd.extend(values)
else:
cmd.extend(args)
# support for presplash background color
presplash_color = self.buildozer.config.getdefault('app', 'android.presplash_color', None)
if presplash_color:
cmd.append('--presplash-color')
cmd.append("'{}'".format(presplash_color))
# support for services
services = self.buildozer.config.getlist('app', 'services', [])
for service in services:
cmd.append("--service")
cmd.append(service)
# support for copy-libs
if self.buildozer.config.getbooldefault('app', 'android.copy_libs', True):
cmd.append("--copy-libs")
# support for recipes in a local directory within the project
if local_recipes:
cmd.append('--local-recipes')
cmd.append(local_recipes)
# support for blacklist/whitelist filename
whitelist_src = self.buildozer.config.getdefault('app', 'android.whitelist_src', None)
blacklist_src = self.buildozer.config.getdefault('app', 'android.blacklist_src', None)
if whitelist_src:
cmd.append('--whitelist')
cmd.append(realpath(whitelist_src))
if blacklist_src:
cmd.append('--blacklist')
cmd.append(realpath(blacklist_src))
# support for aars
aars = self.buildozer.config.getlist('app', 'android.add_aars', [])
for aar in aars:
cmd.append('--add-aar')
cmd.append(realpath(aar))
# support for gradle dependencies
gradle_dependencies = self.buildozer.config.getlist('app', 'android.gradle_dependencies', [])
for gradle_dependency in gradle_dependencies:
cmd.append('--depend')
cmd.append(gradle_dependency)
cmd.append('--arch')
cmd.append(self.buildozer.config.getdefault('app', 'android.arch', "armeabi-v7a"))
cmd = " ".join(cmd)
self._p4a(cmd)
def get_release_mode(self):
if self.check_p4a_sign_env():
return "release"
return "release-unsigned"
def check_p4a_sign_env(self, error=False):
keys = ["KEYALIAS", "KEYSTORE_PASSWD", "KEYSTORE", "KEYALIAS_PASSWD"]
check = True
for key in keys:
key = "P4A_RELEASE_{}".format(key)
if key not in os.environ:
if error:
self.buildozer.error(
("Asking for release but {} is missing"
"--sign will not be passed").format(key))
check = False
return check
def cmd_run(self, *args):
entrypoint = self.buildozer.config.getdefault(
'app', 'android.entrypoint')
if not entrypoint:
self.buildozer.config.set('app', 'android.entrypoint', 'org.kivy.android.PythonActivity')
return super(TargetAndroidNew, self).cmd_run(*args)
def cmd_p4a(self, *args):
'''
Run p4a commands. Args must come after --, or
use --alias to make an alias
'''
self.check_requirements()
self.install_platform()
args = args[0]
if args and args[0] == '--alias':
print('To set up p4a in this shell session, execute:')
print(' alias p4a=$(buildozer {} p4a --alias 2>&1 >/dev/null)'
.format(self.targetname))
sys.stderr.write('PYTHONPATH={} {}\n'.format(self.pa_dir, self._p4a_cmd))
else:
self._p4a(' '.join(args) if args else '')
def cmd_clean(self, *args):
'''
Clean the build and distribution
'''
self._p4a("clean_builds")
self._p4a("clean_dists")
def get_target(buildozer):
buildozer.targetname = "android"
return TargetAndroidNew(buildozer)