diff --git a/pythonforandroid/bootstraps/pygame/__init__.py b/pythonforandroid/bootstraps/pygame/__init__.py
index 446d157e17..a0ddaac626 100644
--- a/pythonforandroid/bootstraps/pygame/__init__.py
+++ b/pythonforandroid/bootstraps/pygame/__init__.py
@@ -1,4 +1,4 @@
-from pythonforandroid.toolchain import Bootstrap, shprint, current_directory, info, warning, ArchAndroid, logger, info_main, which
+from pythonforandroid.toolchain import Bootstrap, shprint, current_directory, info, warning, ArchAndroid, info_main
from os.path import join, exists
from os import walk
import glob
@@ -19,6 +19,9 @@ def run_distribute(self):
# self.name)
src_path = join(self.bootstrap_dir, 'build')
+ # AND: Hardcoding armeabi - naughty!
+ arch = ArchAndroid(self.ctx)
+
with current_directory(self.dist_dir):
info('Creating initial layout')
@@ -40,21 +43,14 @@ def run_distribute(self):
info('Copying python distribution')
hostpython = sh.Command(self.ctx.hostpython)
# AND: This *doesn't* need to be in arm env?
- shprint(hostpython, '-OO', '-m', 'compileall', self.ctx.get_python_install_dir())
+ shprint(hostpython, '-OO', '-m', 'compileall', self.ctx.get_python_install_dir(),
+ _tail=10, _filterout="^Listing", _critical=True)
if not exists('python-install'):
shprint(sh.cp, '-a', self.ctx.get_python_install_dir(), './python-install')
- info('Copying libs')
- # AND: Hardcoding armeabi - naughty!
- shprint(sh.mkdir, '-p', join('libs', 'armeabi'))
- for lib in glob.glob(join(self.build_dir, 'libs', 'armeabi', '*')):
- shprint(sh.cp, '-a', lib, join('libs', 'armeabi'))
- for lib in glob.glob(join(self.ctx.get_libs_dir('armeabi'), '*')):
- shprint(sh.cp, '-a', lib, join('libs', 'armeabi'))
-
- info('Copying java files')
- for filename in glob.glob(self.ctx.javaclass_dir):
- shprint(sh.cp, '-a', filename, 'src')
+ self.distribute_libs(arch, [join(self.build_dir, 'libs', arch.arch), self.ctx.get_libs_dir(arch.arch)]);
+ self.distribute_aars(arch)
+ self.distribute_javaclasses(self.ctx.javaclass_dir)
info('Filling private directory')
if not exists(join('private', 'lib')):
@@ -91,21 +87,7 @@ def run_distribute(self):
shprint(sh.rm, '-rf', 'lib-dynload/_testcapi.so')
- info('Stripping libraries')
- env = ArchAndroid(self.ctx).get_env()
- strip = which('arm-linux-androideabi-strip', env['PATH'])
- if strip is None:
- warning('Can\'t find strip in PATH...')
- 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')
- logger.info('Stripping libraries in private dir')
- for filen in filens.split('\n'):
- try:
- strip(filen, _env=env)
- except sh.ErrorReturnCode_1:
- logger.debug('Failed to strip ' + 'filen')
-
+ self.strip_libraries(arch)
super(PygameBootstrap, self).run_distribute()
bootstrap = PygameBootstrap()
diff --git a/pythonforandroid/bootstraps/pygame/build/build.py b/pythonforandroid/bootstraps/pygame/build/build.py
index 462dd29998..95f80a0444 100755
--- a/pythonforandroid/bootstraps/pygame/build/build.py
+++ b/pythonforandroid/bootstraps/pygame/build/build.py
@@ -183,7 +183,7 @@ def select(fn):
tf = tarfile.open(tfn, 'w:gz', format=tarfile.USTAR_FORMAT)
dirs = []
for fn, afn in files:
- print('%s: %s' % (tfn, fn))
+# print('%s: %s' % (tfn, fn))
dn = dirname(afn)
if dn not in dirs:
# create every dirs first if not exist yet
diff --git a/pythonforandroid/bootstraps/pygame/build/src/org/renpy/android/AssetExtract.java b/pythonforandroid/bootstraps/pygame/build/src/org/renpy/android/AssetExtract.java
index e1d810798a..dd4ec48fc3 100644
--- a/pythonforandroid/bootstraps/pygame/build/src/org/renpy/android/AssetExtract.java
+++ b/pythonforandroid/bootstraps/pygame/build/src/org/renpy/android/AssetExtract.java
@@ -60,7 +60,7 @@ public boolean extractTar(String asset, String target) {
break;
}
- Log.i("python", "extracting " + entry.getName());
+ Log.v("python", "extracting " + entry.getName());
if (entry.isDirectory()) {
diff --git a/pythonforandroid/bootstraps/sdl2/__init__.py b/pythonforandroid/bootstraps/sdl2/__init__.py
index 1a4d1c1bbf..10c9e0f118 100644
--- a/pythonforandroid/bootstraps/sdl2/__init__.py
+++ b/pythonforandroid/bootstraps/sdl2/__init__.py
@@ -1,4 +1,4 @@
-from pythonforandroid.toolchain import Bootstrap, shprint, current_directory, info, warning, ArchAndroid, logger, info_main, which
+from pythonforandroid.toolchain import Bootstrap, shprint, current_directory, info, warning, ArchAndroid, info_main
from os.path import join, exists
from os import walk
import glob
@@ -20,6 +20,9 @@ def run_distribute(self):
with open('local.properties', 'w') as fileh:
fileh.write('sdk.dir={}'.format(self.ctx.sdk_dir))
+ # AND: Hardcoding armeabi - naughty!
+ arch = ArchAndroid(self.ctx)
+
with current_directory(self.dist_dir):
info('Copying python distribution')
@@ -31,19 +34,14 @@ def run_distribute(self):
hostpython = sh.Command(self.ctx.hostpython)
# AND: This *doesn't* need to be in arm env?
shprint(hostpython, '-OO', '-m', 'compileall',
- self.ctx.get_python_install_dir())
+ self.ctx.get_python_install_dir(),
+ _tail=10, _filterout="^Listing", _critical=True)
if not exists('python-install'):
shprint(sh.cp, '-a', self.ctx.get_python_install_dir(), './python-install')
- info('Copying libs')
- # AND: Hardcoding armeabi - naughty!
- shprint(sh.mkdir, '-p', join('libs', 'armeabi'))
- for lib in glob.glob(join(self.ctx.get_libs_dir('armeabi'), '*')):
- shprint(sh.cp, '-a', lib, join('libs', 'armeabi'))
-
- info('Copying java files')
- for filename in glob.glob(self.ctx.javaclass_dir):
- shprint(sh.cp, '-a', filename, 'src')
+ self.distribute_libs(arch, [self.ctx.get_libs_dir(arch.arch)])
+ self.distribute_aars(arch)
+ self.distribute_javaclasses(self.ctx.javaclass_dir)
info('Filling private directory')
if not exists(join('private', 'lib')):
@@ -81,21 +79,7 @@ def run_distribute(self):
# shprint(sh.rm, '-rf', 'lib-dynload/_ctypes_test.so')
# shprint(sh.rm, '-rf', 'lib-dynload/_testcapi.so')
-
- info('Stripping libraries')
- env = ArchAndroid(self.ctx).get_env()
- strip = which('arm-linux-androideabi-strip', env['PATH'])
- if strip is None:
- warning('Can\'t find strip in PATH...')
- 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')
- logger.info('Stripping libraries in private dir')
- for filen in filens.split('\n'):
- try:
- strip(filen, _env=env)
- except sh.ErrorReturnCode_1:
- logger.debug('Failed to strip ' + 'filen')
+ self.strip_libraries(arch)
super(SDL2Bootstrap, self).run_distribute()
bootstrap = SDL2Bootstrap()
diff --git a/pythonforandroid/bootstraps/sdl2/build/build.py b/pythonforandroid/bootstraps/sdl2/build/build.py
index 175931bec5..8192606565 100755
--- a/pythonforandroid/bootstraps/sdl2/build/build.py
+++ b/pythonforandroid/bootstraps/sdl2/build/build.py
@@ -166,7 +166,7 @@ def select(fn):
tf = tarfile.open(tfn, 'w:gz', format=tarfile.USTAR_FORMAT)
dirs = []
for fn, afn in files:
- print('%s: %s' % (tfn, fn))
+# print('%s: %s' % (tfn, fn))
dn = dirname(afn)
if dn not in dirs:
# create every dirs first if not exist yet
@@ -249,6 +249,10 @@ def make_package(args):
default_icon = 'templates/kivy-icon.png'
shutil.copy(args.icon or default_icon, 'res/drawable/icon.png')
+ default_presplash = 'templates/kivy-presplash.jpg'
+ shutil.copy(args.presplash or default_presplash,
+ 'res/drawable/presplash.jpg')
+
versioned_name = (args.name.replace(' ', '').replace('\'', '') +
'-' + args.version)
@@ -324,6 +328,14 @@ def parse_args(args=None):
help='A png file to use as the icon for the application.')
ap.add_argument('--permission', dest='permissions', action='append',
help='The permissions to give this app.')
+ ap.add_argument('--meta-data', dest='meta_data', action='append',
+ help='Custom key=value to add in application metadata')
+ ap.add_argument('--presplash', dest='presplash',
+ help=('A jpeg file to use as a screen while the '
+ 'application is loading.'))
+ ap.add_argument('--wakelock', dest='wakelock', action='store_true',
+ help=('Indicate if the application needs the device '
+ 'to stay on'))
if args is None:
args = sys.argv[1:]
@@ -333,6 +345,9 @@ def parse_args(args=None):
if args.permissions is None:
args.permissions = []
+ if args.meta_data is None:
+ args.meta_data = []
+
make_package(args)
if __name__ == "__main__":
diff --git a/pythonforandroid/bootstraps/sdl2/build/src/org/kivy/android/PythonActivity.java b/pythonforandroid/bootstraps/sdl2/build/src/org/kivy/android/PythonActivity.java
index abe5b158d5..77979da1d7 100644
--- a/pythonforandroid/bootstraps/sdl2/build/src/org/kivy/android/PythonActivity.java
+++ b/pythonforandroid/bootstraps/sdl2/build/src/org/kivy/android/PythonActivity.java
@@ -7,23 +7,34 @@
import java.io.FileWriter;
import java.io.File;
+import android.view.ViewGroup;
+import android.view.SurfaceView;
import android.app.Activity;
import android.util.Log;
import android.widget.Toast;
import android.os.Bundle;
+import android.os.PowerManager;
+import android.graphics.PixelFormat;
+import android.view.SurfaceHolder;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ApplicationInfo;
import org.libsdl.app.SDLActivity;
import org.renpy.android.ResourceManager;
import org.renpy.android.AssetExtract;
+
public class PythonActivity extends SDLActivity {
private static final String TAG = "PythonActivity";
public static PythonActivity mActivity = null;
- private ResourceManager resourceManager;
-
+ private ResourceManager resourceManager = null;
+ private Bundle mMetaData = null;
+ private PowerManager.WakeLock mWakeLock = null;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.v(TAG, "My oncreate running");
@@ -48,6 +59,25 @@ protected void onCreate(Bundle savedInstanceState) {
// nativeSetEnv("ANDROID_ARGUMENT", getFilesDir());
+
+ try {
+ Log.v(TAG, "Access to our meta-data...");
+ this.mMetaData = this.mActivity.getPackageManager().getApplicationInfo(
+ this.mActivity.getPackageName(), PackageManager.GET_META_DATA).metaData;
+
+ PowerManager pm = (PowerManager) this.mActivity.getSystemService(Context.POWER_SERVICE);
+ if ( this.mMetaData.getInt("wakelock") == 1 ) {
+ this.mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "Screen On");
+ }
+ if ( this.mMetaData.getInt("surface.transparent") != 0 ) {
+ Log.v(TAG, "Surface will be transparent.");
+ getSurface().setZOrderOnTop(true);
+ getSurface().getHolder().setFormat(PixelFormat.TRANSPARENT);
+ } else {
+ Log.i(TAG, "Surface will NOT be transparent");
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ }
}
// This is just overrides the normal SDLActivity, which just loads
@@ -169,6 +199,13 @@ public void unpackData(final String resource, File target) {
Log.w("python", e);
}
}
+ }
+
+ public static ViewGroup getLayout() {
+ return mLayout;
+ }
+ public static SurfaceView getSurface() {
+ return mSurface;
}
}
diff --git a/pythonforandroid/bootstraps/sdl2/build/src/org/renpy/android/AssetExtract.java b/pythonforandroid/bootstraps/sdl2/build/src/org/renpy/android/AssetExtract.java
index f1f077d7a1..52d6424e09 100644
--- a/pythonforandroid/bootstraps/sdl2/build/src/org/renpy/android/AssetExtract.java
+++ b/pythonforandroid/bootstraps/sdl2/build/src/org/renpy/android/AssetExtract.java
@@ -60,7 +60,7 @@ public boolean extractTar(String asset, String target) {
break;
}
- Log.i("python", "extracting " + entry.getName());
+ Log.v("python", "extracting " + entry.getName());
if (entry.isDirectory()) {
diff --git a/pythonforandroid/bootstraps/sdl2/build/templates/AndroidManifest.xml.tmpl b/pythonforandroid/bootstraps/sdl2/build/templates/AndroidManifest.xml.tmpl
index 75e3d62fa8..4ce2fe373a 100644
--- a/pythonforandroid/bootstraps/sdl2/build/templates/AndroidManifest.xml.tmpl
+++ b/pythonforandroid/bootstraps/sdl2/build/templates/AndroidManifest.xml.tmpl
@@ -38,6 +38,11 @@
android:allowBackup="true"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:hardwareAccelerated="true" >
+
+ {% for m in args.meta_data %}
+ {% endfor %}
+
+
= value) {
+ return RAISE (PyExc_SDLError, "Invalid joystick trackball");
+@@ -300,7 +300,7 @@ joy_get_numhats (PyObject* self)
+ }
+
+ value = SDL_JoystickNumHats (joy);
+- printf("SDL_JoystickNumHats value:%d:\n", value);
++/* printf("SDL_JoystickNumHats value:%d:\n", value); */
+
+ return PyInt_FromLong (value);
+ }
+@@ -327,7 +327,7 @@ joy_get_hat (PyObject* self, PyObject* args)
+
+ px = py = 0;
+ value = SDL_JoystickGetHat (joy, _index);
+- printf("SDL_JoystickGetHat value:%d:\n", value);
++/* printf("SDL_JoystickGetHat value:%d:\n", value); */
+
+ if (value & SDL_HAT_UP) {
+ py = 1;
diff --git a/pythonforandroid/recipes/pygame_bootstrap_components/__init__.py b/pythonforandroid/recipes/pygame_bootstrap_components/__init__.py
index 4ba1cb0b82..149cce167d 100644
--- a/pythonforandroid/recipes/pygame_bootstrap_components/__init__.py
+++ b/pythonforandroid/recipes/pygame_bootstrap_components/__init__.py
@@ -14,7 +14,7 @@ def prebuild_arch(self, arch):
info('Unpacking pygame bootstrap JNI dir components')
with current_directory(self.get_build_container_dir(arch)):
if exists('sdl'):
- info('sdl dir exists, so it looks like the JNI components',
+ info('sdl dir exists, so it looks like the JNI components' +
'are already unpacked. Skipping.')
return
for dirn in glob.glob(join(self.get_build_dir(arch),
diff --git a/pythonforandroid/recipes/sdl/__init__.py b/pythonforandroid/recipes/sdl/__init__.py
index 9f46d465f4..1da3999d26 100644
--- a/pythonforandroid/recipes/sdl/__init__.py
+++ b/pythonforandroid/recipes/sdl/__init__.py
@@ -18,7 +18,7 @@ def build_armeabi(self):
env = ArchAndroid(self.ctx).get_env()
with current_directory(self.get_jni_dir()):
- shprint(sh.ndk_build, 'V=1', _env=env)
+ shprint(sh.ndk_build, 'V=1', _env=env, _tail=20, _critical=True)
libs_dir = join(self.ctx.bootstrap.build_dir, 'libs', 'armeabi')
import os
diff --git a/pythonforandroid/recipes/vlc/__init__.py b/pythonforandroid/recipes/vlc/__init__.py
new file mode 100644
index 0000000000..a10d9eb03c
--- /dev/null
+++ b/pythonforandroid/recipes/vlc/__init__.py
@@ -0,0 +1,70 @@
+from pythonforandroid.toolchain import Recipe, shprint, current_directory, warning, info, debug
+from os.path import exists, join
+from os import environ
+import sh
+from colorama import Fore, Style
+
+class VlcRecipe(Recipe):
+ version = '3.0.0'
+ url = None
+ name = 'vlc'
+
+ depends = []
+
+ port_git = 'http://git.videolan.org/git/vlc-ports/android.git'
+ vlc_git = 'http://git.videolan.org/git/vlc.git'
+ ENV_LIBVLC_AAR = 'LIBVLC_AAR'
+ aars = {} # for future use of multiple arch
+
+ def prebuild_arch(self, arch):
+ super(VlcRecipe, self).prebuild_arch(arch)
+ build_dir = self.get_build_dir(arch.arch)
+ port_dir = join(build_dir, 'vlc-port-android')
+ if self.ENV_LIBVLC_AAR in environ:
+ self.aars[arch] = aar = environ.get(self.ENV_LIBVLC_AAR)
+ if not exists(aar):
+ warning("Error: libvlc-.aar bundle " \
+ "not found in {}".format(aar))
+ info("check {} environment!".format(self.ENV_LIBVLC_AAR))
+ exit(1)
+ else:
+ aar_path = join(port_dir, 'libvlc', 'build', 'outputs', 'aar')
+ self.aars[arch] = aar = join(aar_path, 'libvlc-{}.aar'.format(self.version))
+ warning("HINT: set path to precompiled libvlc-.aar bundle " \
+ "in {} environment!".format(self.ENV_LIBVLC_AAR))
+ info("libvlc-.aar should build " \
+ "from sources at {}".format(port_dir))
+ if not exists(join(port_dir, 'compile.sh')):
+ info("clone vlc port for android sources from {}".format(
+ self.port_git))
+ shprint(sh.git, 'clone', self.port_git, port_dir,
+ _tail=20, _critical=True)
+ vlc_dir = join(port_dir, 'vlc')
+ if not exists(join(vlc_dir, 'Makefile.am')):
+ info("clone vlc sources from {}".format(self.vlc_git))
+ shprint(sh.git, 'clone', self.vlc_git, vlc_dir,
+ _tail=20, _critical=True)
+
+ def build_arch(self, arch):
+ super(VlcRecipe, self).build_arch(arch)
+ build_dir = self.get_build_dir(arch.arch)
+ port_dir = join(build_dir, 'vlc-port-android')
+ aar = self.aars[arch]
+ if not exists(aar):
+ with current_directory(port_dir):
+ env = dict(environ)
+ env.update({
+ 'ANDROID_ABI': arch.arch,
+ 'ANDROID_NDK': self.ctx.ndk_dir,
+ 'ANDROID_SDK': self.ctx.sdk_dir,
+ })
+ info("compiling vlc from sources")
+ debug("environment: {}".format(env))
+ if not exists(join('bin', 'VLC-debug.apk')):
+ shprint(sh.Command('./compile.sh'), _env=env,
+ _tail=50, _critical=True)
+ shprint(sh.Command('./compile-libvlc.sh'), _env=env,
+ _tail=50, _critical=True)
+ shprint(sh.cp, '-a', aar, self.ctx.aars_dir)
+
+recipe = VlcRecipe()
diff --git a/pythonforandroid/toolchain.py b/pythonforandroid/toolchain.py
index 1599a243ec..84b5044c34 100755
--- a/pythonforandroid/toolchain.py
+++ b/pythonforandroid/toolchain.py
@@ -11,7 +11,7 @@
import sys
from sys import stdout, stderr, platform
from os.path import (join, dirname, realpath, exists, isdir, basename,
- expanduser)
+ expanduser, splitext)
from os import listdir, unlink, makedirs, environ, chdir, getcwd, walk, uname
import os
import zipfile
@@ -30,6 +30,8 @@
from functools import wraps
from datetime import datetime
from distutils.spawn import find_executable
+from tempfile import mkdtemp
+from math import log10
try:
from urllib.request import FancyURLopener
except ImportError:
@@ -50,6 +52,7 @@ def __init__(self):
self._dict = defaultdict(str)
def __getattr__(self, key):
return self._dict[key]
+Style = Fore = colorama_shim()
Null_Style = Null_Fore = colorama_shim()
if stdout.isatty():
@@ -130,6 +133,15 @@ def pretty_log_dists(dists, log_func=info):
for line in infos:
log_func('\t' + line)
+def shorten_string(string, max_width):
+ ''' make limited length string in form:
+ "the string is very lo...(and 15 more)"
+ '''
+ string_len = len(string)
+ if string_len <= max_width: return string
+ visible = max_width - 16 - int(log10(string_len)) #expected suffix len "...(and XXXXX more)"
+ return ''.join((string[:visible], '...(and ', str(string_len - visible), ' more)'))
+
def shprint(command, *args, **kwargs):
'''Runs the command (which should be an sh.Command instance), while
logging the output.'''
@@ -137,38 +149,67 @@ def shprint(command, *args, **kwargs):
kwargs["_out_bufsize"] = 1
kwargs["_err_to_out"] = True
kwargs["_bg"] = True
+ is_critical = kwargs.pop('_critical', False)
+ tail_n = kwargs.pop('_tail', 0)
+ filter_in = kwargs.pop('_filter', None)
+ filter_out = kwargs.pop('_filterout', None)
if len(logger.handlers) > 1:
logger.removeHandler(logger.handlers[1])
+ try:
+ columns = max(25, int(os.popen('stty size', 'r').read().split()[1]))
+ except:
+ columns = 100
command_path = str(command).split('/')
command_string = command_path[-1]
string = ' '.join(['running', command_string] + list(args))
# If logging is not in DEBUG mode, trim the command if necessary
if logger.level > logging.DEBUG:
- short_string = string
- if len(string) > 100:
- short_string = string[:100] + '... (and {} more)'.format(len(string) - 100)
- logger.info(short_string + Err_Style.RESET_ALL)
+ logger.info('{}{}'.format(shorten_string(string, columns - 12), Err_Style.RESET_ALL))
else:
- logger.debug(string + Err_Style.RESET_ALL)
+ logger.debug('{}{}'.format(string, Err_Style.RESET_ALL))
- output = command(*args, **kwargs)
need_closing_newline = False
- for line in output:
- if logger.level > logging.DEBUG:
- string = ''.join([Err_Style.RESET_ALL, '\r', ' '*11, 'working ... ',
- line[:100].replace('\n', '').rstrip(), ' ...'])
- if len(string) < 20:
- continue
- if len(string) < 120:
- string = string + ' '*(120 - len(string))
- sys.stdout.write(string)
- sys.stdout.flush()
- need_closing_newline = True
+ try:
+ msg_hdr = ' working: '
+ msg_width = columns - len(msg_hdr) - 1
+ output = command(*args, **kwargs)
+ for line in output:
+ if logger.level > logging.DEBUG:
+ msg = line.replace('\n', ' ').replace('\t', ' ').replace('\b', ' ').rstrip()
+ if msg:
+# if len(msg) > msg_width: msg = msg[:(msg_width - 3)] + '...'
+ sys.stdout.write('{}\r{}{:<{width}}'.format(Err_Style.RESET_ALL, msg_hdr, shorten_string(msg, msg_width), width=msg_width))
+ sys.stdout.flush()
+ need_closing_newline = True
+ else:
+ logger.debug(''.join(['\t', line.rstrip()]))
+ if need_closing_newline: sys.stdout.write('{}\r{:>{width}}\r'.format(Style.RESET_ALL, ' ', width=(columns - 1)))
+ except sh.ErrorReturnCode, err:
+ if need_closing_newline: sys.stdout.write('{}\r{:>{width}}\r'.format(Style.RESET_ALL, ' ', width=(columns - 1)))
+ if tail_n or filter_in or filter_out:
+ def printtail(out, name, forecolor, tail_n = 0, re_filter_in = None, re_filter_out = None):
+ lines = out.splitlines()
+ if re_filter_in is not None: lines = [l for l in lines if re_filter_in.search(l)]
+ if re_filter_out is not None: lines = [l for l in lines if not re_filter_out.search(l)]
+ if tail_n == 0 or len(lines) <= tail_n:
+ info('{}:\n{}\t{}{}'.format(name, forecolor, '\t\n'.join(lines), Fore.RESET))
+ else:
+ info('{} (last {} lines of {}):\n{}\t{}{}'.format(name, tail_n, len(lines), forecolor, '\t\n'.join(lines[-tail_n:]), Fore.RESET))
+ printtail(err.stdout, 'STDOUT', Fore.YELLOW, tail_n,
+ re.compile(filter_in) if filter_in else None,
+ re.compile(filter_out) if filter_out else None)
+ printtail(err.stderr, 'STDERR', Fore.RED)
+ if is_critical:
+ env = kwargs.get("env")
+ if env is not None:
+ info("{}ENV:{}\n{}\n".format(Fore.YELLOW, Fore.RESET, "\n".join("set {}={}".format(n,v) for n,v in env.items())))
+ info("{}COMMAND:{}\ncd {} && {} {}\n".format(Fore.YELLOW, Fore.RESET, getcwd(), command, ' '.join(args)))
+ warning("{}ERROR: {} failed!{}".format(Fore.RED, command, Fore.RESET))
+ exit(1)
else:
- logger.debug(''.join(['\t', line.rstrip()]))
- if logger.level > logging.DEBUG and need_closing_newline:
- print()
+ raise
+
return output
# shprint(sh.ls, '-lah')
@@ -246,6 +287,17 @@ def current_directory(new_dir):
Err_Fore.RESET)))
chdir(cur_dir)
+@contextlib.contextmanager
+def temp_directory():
+ temp_dir = mkdtemp()
+ try:
+ logger.debug(''.join((Err_Fore.CYAN, ' + temp directory used ', temp_dir,
+ Err_Fore.RESET)))
+ yield temp_dir
+ finally:
+ shutil.rmtree(temp_dir)
+ logger.debug(''.join((Err_Fore.CYAN, ' - temp directory deleted ', temp_dir,
+ Err_Fore.RESET)))
def cache_execution(f):
@@ -557,6 +609,7 @@ class Context(object):
dist_dir = None # the Android project folder where everything ends up
libs_dir = None # where Android libs are cached after build but
# before being placed in dists
+ aars_dir = None
javaclass_dir = None
ccache = None # whether to use ccache
cython = None # the cython interpreter name
@@ -592,6 +645,12 @@ def javaclass_dir(self):
ensure_dir(dir)
return dir
+ @property
+ def aars_dir(self):
+ dir = join(self.build_dir, 'aars', self.bootstrap.distribution.name)
+ ensure_dir(dir)
+ return dir
+
@property
def python_installs_dir(self):
dir = join(self.build_dir, 'python-installs')
@@ -1340,6 +1399,71 @@ def get_bootstrap(cls, name, ctx):
bootstrap.ctx = ctx
return bootstrap
+ def distribute_libs(self, arch, src_dirs, wildcard='*'):
+ '''Copy existing arch libs from build dirs to current dist dir.'''
+ info('Copying libs')
+ tgt_dir = join('libs', arch.arch)
+ ensure_dir(tgt_dir)
+ for src_dir in src_dirs:
+ for lib in glob.glob(join(src_dir, wildcard)):
+ shprint(sh.cp, '-a', lib, tgt_dir)
+
+ def distribute_javaclasses(self, javaclass_dir):
+ '''Copy existing javaclasses from build dir to current dist dir.'''
+ info('Copying java files')
+ for filename in glob.glob(javaclass_dir):
+ shprint(sh.cp, '-a', filename, 'src')
+
+ def distribute_aars(self, arch):
+ '''Process existing .aar bundles and copy to current dist dir.'''
+ info('Unpacking aars')
+ for aar in glob.glob(join(self.ctx.aars_dir, '*.aar')):
+ self._unpack_aar(aar, arch)
+
+ def _unpack_aar(self, aar, arch):
+ '''Unpack content of .aar bundle and copy to current dist dir.'''
+ with temp_directory() as temp_dir:
+ name = splitext(basename(aar))[0]
+ jar_name = name + '.jar'
+ info("unpack {} aar".format(name))
+ debug(" from {}".format(aar))
+ debug(" to {}".format(temp_dir))
+ shprint(sh.unzip, '-o', aar, '-d', temp_dir)
+
+ jar_src = join(temp_dir, 'classes.jar')
+ jar_tgt = join('libs', jar_name)
+ debug("copy {} jar".format(name))
+ debug(" from {}".format(jar_src))
+ debug(" to {}".format(jar_tgt))
+ ensure_dir('libs')
+ shprint(sh.cp, '-a', jar_src, jar_tgt)
+
+ so_src_dir = join(temp_dir, 'jni', arch.arch)
+ so_tgt_dir = join('libs', arch.arch)
+ debug("copy {} .so".format(name))
+ debug(" from {}".format(so_src_dir))
+ debug(" to {}".format(so_tgt_dir))
+ ensure_dir(so_tgt_dir)
+ so_files = glob.glob(join(so_src_dir, '*.so'))
+ for f in so_files:
+ shprint(sh.cp, '-a', f, so_tgt_dir)
+
+ def strip_libraries(self, arch):
+ info('Stripping libraries')
+ env = arch.get_env()
+ strip = which('arm-linux-androideabi-strip', env['PATH'])
+ if strip is None:
+ 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')
+ logger.info('Stripping libraries in private dir')
+ for filen in filens.split('\n'):
+ try:
+ strip(filen, _env=env)
+ except sh.ErrorReturnCode_1:
+ logger.debug('Failed to strip ' + filen)
class Recipe(object):
url = None
@@ -1477,15 +1601,30 @@ def extract_source(self, source, cwd):
# print("Unrecognized extension for {}".format(filename))
# raise Exception()
- def apply_patch(self, filename):
+ def apply_patch(self, filename, arch='armeabi'):
"""
Apply a patch from the current recipe directory into the current
build directory.
"""
info("Applying patch {}".format(filename))
filename = join(self.recipe_dir, filename)
- # AND: get_build_dir shouldn't need to hardcode armeabi
- sh.patch("-t", "-d", self.get_build_dir('armeabi'), "-p1", "-i", filename)
+ shprint(sh.patch, "-t", "-d", self.get_build_dir(arch), "-p1",
+ "-i", filename, _tail=10)
+
+ def apply_all_patches(self, wildcard=join('patches','*.patch'), arch='armeabi'):
+ patches = glob.glob(join(self.recipe_dir, wildcard))
+ if not patches:
+ warning('requested patches {} not found for {}'.format(wildcard, self.name))
+ for filename in sorted(patches):
+ name = splitext(basename(filename))[0]
+ patched_flag = join(self.get_build_container_dir(arch), name + '.patched')
+ if exists(patched_flag):
+ info('patch {} already applied to {}, skipping'.format(name, self.name))
+ else:
+ self.apply_patch(filename, arch=arch)
+ sh.touch(patched_flag)
+ return len(patches)
+
def copy_file(self, filename, dest):
info("Copy {} to {}".format(filename, dest))
@@ -2066,7 +2205,8 @@ def build_cython_components(self, arch):
self.ctx.cython, '{}', ';', _env=env)
info('ran cython')
- shprint(hostpython, 'setup.py', 'build_ext', '-v', _env=env)
+ shprint(hostpython, 'setup.py', 'build_ext', '-v', _env=env,
+ _tail=20, _critical=True)
print('stripping')
build_lib = glob.glob('./build/lib*')
@@ -2808,7 +2948,7 @@ def apk(self, args):
build = imp.load_source('build', join(dist.dist_dir, 'build.py'))
with current_directory(dist.dist_dir):
build.parse_args(args)
- shprint(sh.ant, 'debug')
+ shprint(sh.ant, 'debug', _tail=20, _critical=True)
# AND: This is very crude, needs improving. Also only works
# for debug for now.