This commit is contained in:
Mathieu Virbel 2012-12-19 03:55:11 +01:00
parent da7c95b6ee
commit 3dc42cb627
3 changed files with 140 additions and 52 deletions

View file

@ -28,16 +28,21 @@ buildozer.spec
# Title of your application
title = My Application
# Source code variables
# Source code where the main.py live
source.dir = .
source.include_ext = py,png,jpg
# Source files to include (let empty to include all the files)
source.include_exts = py,png,jpg
# Source files to exclude (let empty to not excluding anything)
#source.exclude_exts = spec
# Application versionning
version.regex = __version__ = '(.*)'
version.filename = %(source.dir)s/main.py
# Application requirements
requirements = twisted kivy
requirements = twisted,kivy
# Android specific
android.permissions = INTERNET

View file

@ -11,30 +11,50 @@ Layout directory for buildozer:
'''
import shelve
import zipfile
from sys import stdout, exit
from urllib import urlretrieve
from re import search
from ConfigParser import SafeConfigParser
from os.path import join, exists, dirname, realpath
from os.path import join, exists, dirname, realpath, splitext
from subprocess import Popen, PIPE
from os import environ, mkdir, unlink, rename
from os import environ, mkdir, unlink, rename, walk, sep
from copy import copy
from shutil import copyfile
class ConfigDict(dict):
def get(self, key, value, default):
print 'hello'
class Buildozer(object):
def __init__(self, filename, target):
super(Buildozer, self).__init__()
self.environ = copy(environ)
self.environ = {}
self.targetname = target
self.specfilename = filename
self.state = None
self.config = SafeConfigParser()
self.config = SafeConfigParser({}, ConfigDict, allow_no_value=True)
self.config.getlist = self._get_config_list
self.config.getdefault = self._get_config_default
self.config.read(filename)
# resolve target
m = __import__('buildozer.targets.%s' % target, fromlist=['buildozer'])
self.target = m.get_target(self)
def _get_config_list(self, section, token, default=None):
values = self.config.getdefault(section, token, default).split(',')
return [x.strip() for x in values]
def _get_config_default(self, section, token, default=None):
if not self.config.has_section(section):
return default
if not self.config.has_option(section, token):
return default
return self.config.get(section, token)
def log(self, msg):
print '-', msg
@ -54,7 +74,11 @@ class Buildozer(object):
raise Exception(msg + 'not found')
def cmd(self, command, **kwargs):
kwargs.setdefault('env', self.environ)
#print ' '.join(['{0}={1}'.format(*args) for args in
# self.environ.iteritems()])
env = copy(environ)
env.update(self.environ)
kwargs.setdefault('env', env)
self.log('run %r' % command)
c = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, **kwargs)
ret = c.communicate()
@ -70,29 +94,35 @@ class Buildozer(object):
def run(self):
self.log('Build started')
self.log('check configuration tokens')
self.log('Check configuration tokens')
self.do_config_requirements()
self.log('check requirements for %s' % self.targetname)
self.log('Check requirements for %s' % self.targetname)
self.target.check_requirements()
self.log('ensure build layout')
self.log('Ensure build layout')
self.ensure_build_layout()
self.log('install platform')
self.log('Install platform')
self.target.install_platform()
self.log('compile platform')
self.log('Compile platform')
self.target.compile_platform()
self.log('Prebuild the application')
self.prebuild_application()
self.log('Package the application')
self.target.build_package()
def do_config_requirements(self):
pass
def ensure_build_layout(self):
specdir = dirname(self.specfilename)
self.mkdir(join(specdir, self.targetname))
self.mkdir(join(specdir, self.targetname, 'platform'))
self.mkdir(join(specdir, self.targetname, 'app'))
self.mkdir(join(specdir, '.buildozer', self.targetname))
self.mkdir(join(specdir, '.buildozer', self.targetname, 'platform'))
self.mkdir(join(specdir, '.buildozer', self.targetname, 'app'))
self.state = shelve.open(join(self.platform_dir, 'state.db'))
def mkdir(self, dn):
@ -109,6 +139,28 @@ class Buildozer(object):
target = join(cwd, target)
rename(source, target)
def file_extract(self, archive, cwd=None):
if archive.endswith('.tgz') or archive.endswith('.tar.gz'):
# XXX tarfile doesn't work for NDK-r8c :(
#tf = tarfile.open(archive, 'r:*')
#tf.extractall(path=cwd)
#tf.close()
self.cmd('tar xzf {0}'.format(archive), cwd=cwd)
return
if archive.endswith('.tbz2') or archive.endswith('.tar.bz2'):
# XXX same as before
self.cmd('tar xjf {0}'.format(archive), cwd=cwd)
return
if archive.endswith('.zip'):
zf = zipfile.ZipFile(archive)
zf.extractall(path=cwd)
zf.close()
return
raise Exception('Unhandled extraction for type {0}'.format(archive))
def download(self, url, filename, cwd=None):
def report_hook(index, blksize, size):
if size <= 0:
@ -161,13 +213,52 @@ class Buildozer(object):
raise Exception('Missing version or version.regex + version.filename')
def prebuild_application(self):
source_dir = realpath(self.config.getdefault('app', 'source.dir', '.'))
include_exts = self.config.getlist('app', 'source.include_exts', '')
exclude_exts = self.config.getlist('app', 'source.exclude_exts', '')
app_dir = self.app_dir
for root, dirs, files in walk(source_dir):
# avoid hidden directory
if True in [x.startswith('.') for x in root.split(sep)]:
continue
for fn in files:
# avoid hidden files
if fn.startswith('.'):
continue
# filter based on the extension
# TODO more filters
basename, ext = splitext(fn)
if ext:
ext = ext[1:]
if include_exts and ext not in include_exts:
continue
if exclude_exts and ext in exclude_exts:
continue
sfn = join(root, fn)
rfn = realpath(join(app_dir, root[len(source_dir):], fn))
# ensure the directory exists
dfn = dirname(rfn)
self.mkdir(dfn)
# copy!
self.log('Copy {0}'.format(sfn))
copyfile(sfn, rfn)
@property
def platform_dir(self):
return join(dirname(self.specfilename), self.targetname, 'platform')
return join(dirname(self.specfilename), '.buildozer',
self.targetname, 'platform')
@property
def app_dir(self):
return join(dirname(self.specfilename), self.targetname, 'app')
return join(dirname(self.specfilename), '.buildozer',
self.targetname, 'app')
def run():
from optparse import OptionParser

View file

@ -1,10 +1,16 @@
ANDROID_API = '16'
#
# Android target
# Thanks for Renpy (again) for its install_sdk.py and plat.py in the PGS4A
# project!
#
ANDROID_API = '14'
ANDROID_SDK_VERSION = '21'
ANDROID_NDK_VERSION = '8c'
APACHE_ANT_VERSION = '1.8.4'
import tarfile
import zipfile
import traceback
from sys import platform
from buildozer.target import Target
@ -72,13 +78,10 @@ class TargetAndroid(Target):
archive = 'apache-ant-{0}-bin.tar.gz'.format(APACHE_ANT_VERSION)
unpacked = 'apache-ant-{0}'.format(APACHE_ANT_VERSION)
url = 'http://archive.apache.org/dist/ant/binaries/'
archive = self.buildozer.download(url, archive,
self.buildozer.download(url, archive,
cwd=self.buildozer.platform_dir)
tf = tarfile.open(archive, 'r:*')
tf.extractall(path=self.buildozer.platform_dir)
tf.close()
self.buildozer.file_extract(archive, cwd=self.buildozer.platform_dir)
self.buildozer.file_rename(unpacked, 'apache-ant',
cwd=self.buildozer.platform_dir)
self.buildozer.log('Apache ANT installation done.')
@ -105,19 +108,11 @@ class TargetAndroid(Target):
archive = archive.format(ANDROID_SDK_VERSION)
url = 'http://dl.google.com/android/'
archive = self.buildozer.download(url, archive,
self.buildozer.download(url, archive,
cwd=self.buildozer.platform_dir)
self.buildozer.log('Unpacking Android SDK')
if archive.endswith('.tgz'):
tf = tarfile.open(archive, 'r:*')
tf.extractall(path=self.buildozer.platform_dir)
tf.close()
else:
zf = zipfile.ZipFile(archive)
zf.extractall(path=self.buildozer.platform_dir)
zf.close()
self.buildozer.file_extract(archive, cwd=self.buildozer.platform_dir)
self.buildozer.file_rename(unpacked, 'android-sdk',
cwd=self.buildozer.platform_dir)
self.buildozer.log('Android SDK installation done.')
@ -143,19 +138,11 @@ class TargetAndroid(Target):
archive = archive.format(ANDROID_NDK_VERSION)
unpacked = unpacked.format(ANDROID_NDK_VERSION)
url = 'http://dl.google.com/android/ndk/'
archive = self.buildozer.download(url, archive,
self.buildozer.download(url, archive,
cwd=self.buildozer.platform_dir)
self.buildozer.log('Unpacking Android NDK')
if archive.endswith('.tar.bz2'):
tf = tarfile.open(archive, 'r:*')
tf.extractall(path=self.buildozer.platform_dir)
tf.close()
else:
zf = zipfile.ZipFile(archive)
zf.extractall(path=self.buildozer.platform_dir)
zf.close()
self.buildozer.file_extract(archive, cwd=self.buildozer.platform_dir)
self.buildozer.file_rename(unpacked, 'android-ndk',
cwd=self.buildozer.platform_dir)
self.buildozer.log('Android NDK installation done.')
@ -172,11 +159,9 @@ class TargetAndroid(Target):
if not packages:
self.buildozer.log('Android packages already installed.')
return
ret = self.buildozer.cmd('{0} update sdk -u -a -t {1}'.format(
self.buildozer.cmd('{0} update sdk -u -a -t {1}'.format(
self.android_cmd, ','.join(packages)),
cwd=self.buildozer.platform_dir)
print ret[0]
print ret[1]
self.buildozer.log('Android packages installation done.')
def install_platform(self):
@ -192,7 +177,7 @@ class TargetAndroid(Target):
cmd('git pull origin master', cwd=pa_dir)
'''
self.ant_dir = ant_dir = self._install_apache_ant()
self._install_apache_ant()
self.sdk_dir = sdk_dir = self._install_android_sdk()
self.ndk_dir = ndk_dir = self._install_android_ndk()
self._install_android_packages()
@ -206,8 +191,8 @@ class TargetAndroid(Target):
# for android, the compilation depends really on the app requirements.
# compile the distribution only if the requirements changed.
last_requirements = self.buildozer.state.get('android.requirements', '')
app_requirements = self.buildozer.config.get('app',
'requirements', '').split()
app_requirements = self.buildozer.config.getlist('app',
'requirements', '')
# we need to extract the requirements that python-for-android knows
# about
@ -244,7 +229,14 @@ class TargetAndroid(Target):
cmd('./distribute.sh -m "{0}"'.format(modules_str), cwd=self.pa_dir)
self.buildozer.log('Distribution compiled.')
# ensure we will not compile again
self.buildozer.state['android.requirements'] = android_requirements
self.buildozer.state.sync()
def build_package(self):
dist_dir = join(self.pa_dir, 'dist', 'default')
#cmd('./build.py --name {0} --private {1} '
# '--version {2}
def get_target(buildozer):