Merge branch 'master' into garden_requirements

This commit is contained in:
Ian Foote 2014-01-09 19:11:40 +00:00
commit df02289ac0
5 changed files with 201 additions and 29 deletions

View file

@ -64,7 +64,7 @@ Usage
clean Clean the whole Buildozer environment.
help Show the Buildozer help.
init Create a initial buildozer.spec in the current directory
setdefault Set the default command to do when to arguments are given
setdefault Set the default command to do when no arguments are given
version Show the Buildozer version
Target commands:

View file

@ -10,13 +10,15 @@ Layout directory for buildozer:
'''
__version__ = '0.7'
__version__ = '0.9'
import fcntl
import os
import re
import shelve
import SimpleHTTPServer
import socket
import SocketServer
import sys
import zipfile
from select import select
@ -47,6 +49,7 @@ USE_COLOR = 'NO_COLOR' not in environ
# error, info, debug
LOG_LEVELS_C = (RED, BLUE, BLACK)
LOG_LEVELS_T = 'EID'
SIMPLE_HTTP_SERVER_PORT = 8000
class ChromeDownloader(FancyURLopener):
@ -75,7 +78,8 @@ class BuildozerCommandException(BuildozerException):
class Buildozer(object):
standard_cmds = ('clean', 'update', 'debug', 'release', 'deploy', 'run')
standard_cmds = ('clean', 'update', 'debug', 'release',
'deploy', 'run', 'serve')
def __init__(self, filename='buildozer.spec', target=None):
super(Buildozer, self).__init__()
@ -86,7 +90,9 @@ class Buildozer(object):
self.build_id = None
self.config_profile = ''
self.config = SafeConfigParser(allow_no_value=True)
self.config.optionxform = lambda value: value
self.config.getlist = self._get_config_list
self.config.getlistvalues = self._get_config_list_values
self.config.getdefault = self._get_config_default
self.config.getbooldefault = self._get_config_bool
@ -361,7 +367,8 @@ class Buildozer(object):
target_available_packages = self.target.get_available_packages()
# remove all the requirements that the target can compile
requirements = [x for x in requirements if x not in
onlyname = lambda x: x.split('==')[0]
requirements = [x for x in requirements if onlyname(x) not in
target_available_packages]
# did we already installed the libs ?
@ -383,6 +390,10 @@ class Buildozer(object):
def _install_application_requirement(self, module):
self._ensure_virtualenv()
# resetup distribute, just in case
self.debug('Install distribute')
self.cmd('curl http://python-distribute.org/distribute_setup.py | venv/bin/python', get_stdout=True, cwd=self.buildozer_dir)
self.debug('Install requirement {} in virtualenv'.format(module))
self.cmd('pip-2.7 install --download-cache={} --target={} {}'.format(
self.global_cache_dir, self.applibs_dir, module),
@ -502,6 +513,19 @@ class Buildozer(object):
raise Exception('Unhandled extraction for type {0}'.format(archive))
def file_copytree(self, src, dest):
print 'copy {} to {}'.format(src, dest)
if os.path.isdir(src):
if not os.path.isdir(dest):
os.makedirs(dest)
files = os.listdir(src)
for f in files:
self.file_copytree(
os.path.join(src, f),
os.path.join(dest, f))
else:
copyfile(src, dest)
def clean_platform(self):
self.info('Clean the platform build directory')
if not exists(self.platform_dir):
@ -662,6 +686,21 @@ class Buildozer(object):
copyfile(join(dirname(__file__), 'sitecustomize.py'),
join(self.app_dir, 'sitecustomize.py'))
main_py = join(self.app_dir, 'service', 'main.py')
if not self.file_exists(main_py):
#self.error('Unable to patch main_py to add applibs directory.')
return
header = ('import sys, os; '
'sys.path = [os.path.join(os.getcwd(),'
'"..", "_applibs")] + sys.path\n')
with open(main_py, 'rb') as fd:
data = fd.read()
data = header + data
with open(main_py, 'wb') as fd:
fd.write(data)
self.info('Patched service/main.py to include applibs')
def namify(self, name):
'''Return a "valid" name from a name with lot of invalid chars
(allowed characters: a-z, A-Z, 0-9, -, _)
@ -704,6 +743,10 @@ class Buildozer(object):
def global_platform_dir(self):
return join(self.global_buildozer_dir, self.targetname, 'platform')
@property
def global_packages_dir(self):
return join(self.global_buildozer_dir, self.targetname, 'packages')
@property
def global_cache_dir(self):
return join(self.global_buildozer_dir, 'cache')
@ -760,12 +803,13 @@ class Buildozer(object):
print
print 'Target commands:'
print ' clean Clean the target environment'
print ' update Update the target dependencies'
print ' debug Build the application in debug mode'
print ' release Build the application in release mode'
print ' deploy Deploy the application on the device'
print ' run Run the application on the device'
print ' clean Clean the target environment'
print ' update Update the target dependencies'
print ' debug Build the application in debug mode'
print ' release Build the application in release mode'
print ' deploy Deploy the application on the device'
print ' run Run the application on the device'
print ' serve Serve the bin directory via SimpleHTTPServer'
for target, m in targets:
mt = m.get_target(self)
@ -828,7 +872,7 @@ class Buildozer(object):
# maybe it's a target?
targets = [x[0] for x in self.targets()]
if command not in targets:
print 'Unknow command/target', command
print 'Unknown command/target', command
exit(1)
self.set_target(command)
@ -864,6 +908,16 @@ class Buildozer(object):
'''
print 'Buildozer {0}'.format(__version__)
def cmd_serve(self, *args):
'''Serve the bin directory via SimpleHTTPServer
'''
os.chdir(self.bin_dir)
handler = SimpleHTTPServer.SimpleHTTPRequestHandler
httpd = SocketServer.TCPServer(("", SIMPLE_HTTP_SERVER_PORT), handler)
print("Serving via HTTP at port {}".format(SIMPLE_HTTP_SERVER_PORT))
print("Press Ctrl+c to quit serving.")
httpd.serve_forever()
#
# Private
#
@ -898,8 +952,11 @@ class Buildozer(object):
def _get_config_list_values(self, *args, **kwargs):
kwargs['with_values'] = True
return self._get_config_list(*args, **kwargs)
def _get_config_list(self, section, token, default=None):
def _get_config_list(self, section, token, default=None, with_values=False):
# monkey-patch method for ConfigParser
# get a key as a list of string, seperated from the comma
@ -907,7 +964,11 @@ class Buildozer(object):
l_section = '{}:{}'.format(section, token)
if self.config.has_section(l_section):
values = self.config.options(l_section)
return [x.strip() for x in values]
if with_values:
return ['{}={}'.format(key, self.config.get(l_section, key)) for
key in values]
else:
return [x.strip() for x in values]
values = self.config.getdefault(section, token, '')
if not values:
@ -966,7 +1027,7 @@ class BuildozerRemote(Buildozer):
remote_name = args[0]
remote_section = 'remote:{}'.format(remote_name)
if not self.config.has_section(remote_section):
self.error('Unknow remote "{}", must be configured first.'.format(
self.error('Unknown remote "{}", must be configured first.'.format(
remote_name))
return

View file

@ -67,7 +67,10 @@ fullscreen = 1
#android.sdk = 21
# (str) Android NDK version to use
#android.ndk = 8c
#android.ndk = 9
# (bool) Use --private data storage (True) or --dir public storage (False)
#android.private_storage = True
# (str) Android NDK directory (if empty, it will be automatically downloaded.)
#android.ndk_path =
@ -84,6 +87,10 @@ fullscreen = 1
# OUYA-ODK/libs/*.jar
#android.add_jars = foo.jar,bar.jar,path/to/more/*.jar
# (list) List of Java files to add to the android project (can be java or a
# directory containing the files)
#android.add_src =
# (str) python-for-android branch to use, if not master, useful to try
# not yet merged features.
#android.branch = master
@ -101,6 +108,17 @@ fullscreen = 1
# (list) Android additionnal libraries to copy into libs/armeabi
#android.add_libs_armeabi = libs/android/*.so
# (bool) Indicate whether the screen should stay on
# Don't forget to add the WAKE_LOCK permission if you set this to True
#android.wakelock = False
# (list) Android application meta-data to set (key=value format)
#android.meta_data =
# (list) Android library project to add (will be added in the
# project.properties automatically.)
#android.library_references =
#
# iOS specific
#

View file

@ -72,7 +72,7 @@ class Target(object):
for item in result:
command, args = item[0], item[1:]
if not hasattr(self, 'cmd_{0}'.format(command)):
self.buildozer.error('Unknow command {0}'.format(command))
self.buildozer.error('Unknown command {0}'.format(command))
exit(1)
func = getattr(self, 'cmd_{0}'.format(command))
@ -107,3 +107,6 @@ class Target(object):
def cmd_run(self, *args):
self.buildozer.prepare_for_build()
def cmd_serve(self, *args):
self.buildozer.cmd_serve()

View file

@ -11,7 +11,7 @@ Android target, based on python-for-android project
ANDROID_API = '14'
ANDROID_MINAPI = '8'
ANDROID_SDK_VERSION = '21'
ANDROID_NDK_VERSION = '8e'
ANDROID_NDK_VERSION = '9'
APACHE_ANT_VERSION = '1.8.4'
@ -22,7 +22,7 @@ from sys import platform, executable
from buildozer import BuildozerException
from buildozer.target import Target
from os import environ
from os.path import join, realpath, expanduser, basename
from os.path import join, realpath, expanduser, basename, relpath
from shutil import copyfile
from glob import glob
@ -69,7 +69,7 @@ class TargetAndroid(Target):
version = self.buildozer.config.getdefault(
'app', 'android.ndk', self.android_ndk_version)
return join(self.buildozer.global_platform_dir,
'android-ndk-{0}'.format(version))
'android-ndk-r{0}'.format(version))
@property
def apache_ant_dir(self):
@ -135,6 +135,11 @@ class TargetAndroid(Target):
permissions = self.buildozer.config.getlist(
'app', 'android.permissions', [])
for permission in permissions:
# no check on full named permission
# like com.google.android.providers.gsf.permission.READ_GSERVICES
if '.' in permission:
continue
permission = permission.upper()
if permission not in available_permissions:
errors.append(
'[app] "android.permission" contain an unknown'
@ -246,9 +251,10 @@ class TargetAndroid(Target):
self.buildozer.info('Android NDK is missing, downloading')
if platform in ('win32', 'cygwin'):
#FIXME find a way of checking 32/64 bits os (not sys.maxint)
# Checking of 32/64 bits at Windows from: http://stackoverflow.com/a/1405971/798575
import struct
archive = 'android-ndk-r{0}-windows-{1}.zip'
is_64 = False
is_64 = (8*struct.calcsize("P") == 64)
elif platform in ('darwin', ):
archive = 'android-ndk-r{0}-darwin-{1}.tar.bz2'
is_64 = (os.uname()[4] == 'x86_64')
@ -301,10 +307,10 @@ class TargetAndroid(Target):
cmd('git clean -dxf', cwd=pa_dir)
cmd('git pull origin master', cwd=pa_dir)
source = self.buildozer.config.getdefault('app', 'android.branch')
if source:
cmd('git checkout --track -b %s origin/%s' % (source, source),
cwd=pa_dir)
source = self.buildozer.config.getdefault('app', 'android.branch')
if source:
cmd('git checkout %s' % (source),
cwd=pa_dir)
self._install_apache_ant()
self._install_android_sdk()
@ -316,10 +322,11 @@ class TargetAndroid(Target):
self.check_configuration_tokens()
self.buildozer.environ.update({
'PACKAGES_PATH': self.buildozer.global_packages_dir,
'ANDROIDSDK': self.android_sdk_dir,
'ANDROIDNDK': self.android_ndk_dir,
'ANDROIDAPI': self.android_api,
'ANDROIDNDKVER': self.android_ndk_version})
'ANDROIDNDKVER': 'r{}'.format(self.android_ndk_version)})
def get_available_packages(self):
available_modules = self.buildozer.cmd(
@ -338,7 +345,8 @@ class TargetAndroid(Target):
# we need to extract the requirements that python-for-android knows
# about
available_modules = self.get_available_packages()
android_requirements = [x for x in app_requirements if x in
onlyname = lambda x: x.split('==')[0]
android_requirements = [x for x in app_requirements if onlyname(x) in
available_modules]
need_compile = 0
@ -353,8 +361,13 @@ class TargetAndroid(Target):
modules_str = ' '.join(android_requirements)
cmd = self.buildozer.cmd
self.buildozer.debug('Clean and build python-for-android')
cmd('git clean -dxf', cwd=self.pa_dir)
cmd('./distribute.sh -m "{0}"'.format(modules_str), 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
@ -390,17 +403,26 @@ class TargetAndroid(Target):
'Failed to find libs_armeabi files: {}'.format(
pattern))
# update the project.properties libraries references
self._update_libraries_references(dist_dir)
# add src files
self._add_java_src(dist_dir)
# build the app
build_cmd = (
'{python} build.py --name {name}'
' --version {version}'
' --package {package}'
' --private {appdir}'
' --{storage_type} {appdir}'
' --sdk {androidsdk}'
' --minsdk {androidminsdk}').format(
python=executable,
name=quote(config.get('app', 'title')),
version=version,
package=package,
storage_type='private' if config.getbooldefault(
'app', 'android.private_storage', True) else 'dir',
appdir=self.buildozer.app_dir,
androidminsdk=config.getdefault(
'app', 'android.minsdk', 8),
@ -411,7 +433,18 @@ class TargetAndroid(Target):
permissions = config.getlist('app',
'android.permissions', [])
for permission in permissions:
build_cmd += ' --permission {0}'.format(permission)
# force the latest component to be uppercase
permission = permission.split('.')
permission[-1] = permission[-1].upper()
permission = '.'.join(permission)
build_cmd += ' --permission {}'.format(permission)
# meta-data
meta_datas = config.getlistvalues('app', 'android.meta_data', [])
for meta in meta_datas:
key, value = meta.split('=', 1)
meta = '{}={}'.format(key.strip(), value.strip())
build_cmd += ' --meta-data "{}"'.format(meta)
# add extra Java jar files
add_jars = config.getlist('app', 'android.add_jars', [])
@ -457,6 +490,11 @@ class TargetAndroid(Target):
if not fullscreen:
build_cmd += ' --window'
# wakelock ?
wakelock = config.getbooldefault('app', 'android.wakelock', False)
if wakelock:
build_cmd += ' --wakelock'
# intent filters
intent_filters = config.getdefault('app',
'android.manifest.intent_filters', '')
@ -489,6 +527,58 @@ class TargetAndroid(Target):
self.buildozer.state['android:latestapk'] = apk
self.buildozer.state['android:latestmode'] = self.build_mode
def _update_libraries_references(self, dist_dir):
# ensure the project.properties exist
project_fn = join(dist_dir, 'project.properties')
if not self.buildozer.file_exists(project_fn):
content = ['target=android-{}\n'.format(self.android_api)]
else:
with open(project_fn) as fd:
content = fd.readlines()
# extract library reference
references = []
for line in content[:]:
if not line.startswith('android.library.reference.'):
continue
content.remove(line)
# convert our references to relative path
app_references = self.buildozer.config.getlist(
'app', 'android.library_references', [])
source_dir = realpath(self.buildozer.config.getdefault('app', 'source.dir', '.'))
for cref in app_references:
# get the full path of the current reference
ref = realpath(join(source_dir, cref))
if not self.buildozer.file_exists(ref):
self.buildozer.error('Invalid library reference (path not found): {}'.format(cref))
exit(1)
# get a relative path from the project file
ref = relpath(ref, dist_dir)
# ensure the reference exists
references.append(ref)
# recreate the project.properties
with open(project_fn, 'wb') as fd:
for line in content:
fd.write(line)
for index, ref in enumerate(references):
fd.write('android.library.reference.{}={}\n'.format(
index + 1, ref))
self.buildozer.debug('project.properties updated')
def _add_java_src(self, dist_dir):
print '_add_java_src()'
java_src = self.buildozer.config.getlist('app', 'android.add_src', [])
src_dir = join(dist_dir, 'src')
for pattern in java_src:
for fn in glob(expanduser(pattern.strip())):
print 'match file', fn
last_component = basename(fn)
self.buildozer.file_copytree(fn, join(src_dir, last_component))
@property
def serials(self):
if hasattr(self, '_serials'):