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 of your application
title = My Application title = My Application
# Source code variables # Source code where the main.py live
source.dir = . 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 # Application versionning
version.regex = __version__ = '(.*)' version.regex = __version__ = '(.*)'
version.filename = %(source.dir)s/main.py version.filename = %(source.dir)s/main.py
# Application requirements # Application requirements
requirements = twisted kivy requirements = twisted,kivy
# Android specific # Android specific
android.permissions = INTERNET android.permissions = INTERNET

View file

@ -11,30 +11,50 @@ Layout directory for buildozer:
''' '''
import shelve import shelve
import zipfile
from sys import stdout, exit from sys import stdout, exit
from urllib import urlretrieve from urllib import urlretrieve
from re import search from re import search
from ConfigParser import SafeConfigParser 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 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 copy import copy
from shutil import copyfile
class ConfigDict(dict):
def get(self, key, value, default):
print 'hello'
class Buildozer(object): class Buildozer(object):
def __init__(self, filename, target): def __init__(self, filename, target):
super(Buildozer, self).__init__() super(Buildozer, self).__init__()
self.environ = copy(environ) self.environ = {}
self.targetname = target self.targetname = target
self.specfilename = filename self.specfilename = filename
self.state = None 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) self.config.read(filename)
# resolve target # resolve target
m = __import__('buildozer.targets.%s' % target, fromlist=['buildozer']) m = __import__('buildozer.targets.%s' % target, fromlist=['buildozer'])
self.target = m.get_target(self) 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): def log(self, msg):
print '-', msg print '-', msg
@ -54,7 +74,11 @@ class Buildozer(object):
raise Exception(msg + 'not found') raise Exception(msg + 'not found')
def cmd(self, command, **kwargs): 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) self.log('run %r' % command)
c = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, **kwargs) c = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, **kwargs)
ret = c.communicate() ret = c.communicate()
@ -70,29 +94,35 @@ class Buildozer(object):
def run(self): def run(self):
self.log('Build started') self.log('Build started')
self.log('check configuration tokens') self.log('Check configuration tokens')
self.do_config_requirements() 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.target.check_requirements()
self.log('ensure build layout') self.log('Ensure build layout')
self.ensure_build_layout() self.ensure_build_layout()
self.log('install platform') self.log('Install platform')
self.target.install_platform() self.target.install_platform()
self.log('compile platform') self.log('Compile platform')
self.target.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): def do_config_requirements(self):
pass pass
def ensure_build_layout(self): def ensure_build_layout(self):
specdir = dirname(self.specfilename) specdir = dirname(self.specfilename)
self.mkdir(join(specdir, self.targetname)) self.mkdir(join(specdir, '.buildozer', self.targetname))
self.mkdir(join(specdir, self.targetname, 'platform')) self.mkdir(join(specdir, '.buildozer', self.targetname, 'platform'))
self.mkdir(join(specdir, self.targetname, 'app')) self.mkdir(join(specdir, '.buildozer', self.targetname, 'app'))
self.state = shelve.open(join(self.platform_dir, 'state.db')) self.state = shelve.open(join(self.platform_dir, 'state.db'))
def mkdir(self, dn): def mkdir(self, dn):
@ -109,6 +139,28 @@ class Buildozer(object):
target = join(cwd, target) target = join(cwd, target)
rename(source, 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 download(self, url, filename, cwd=None):
def report_hook(index, blksize, size): def report_hook(index, blksize, size):
if size <= 0: if size <= 0:
@ -161,13 +213,52 @@ class Buildozer(object):
raise Exception('Missing version or version.regex + version.filename') 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 @property
def platform_dir(self): def platform_dir(self):
return join(dirname(self.specfilename), self.targetname, 'platform') return join(dirname(self.specfilename), '.buildozer',
self.targetname, 'platform')
@property @property
def app_dir(self): def app_dir(self):
return join(dirname(self.specfilename), self.targetname, 'app') return join(dirname(self.specfilename), '.buildozer',
self.targetname, 'app')
def run(): def run():
from optparse import OptionParser 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_SDK_VERSION = '21'
ANDROID_NDK_VERSION = '8c' ANDROID_NDK_VERSION = '8c'
APACHE_ANT_VERSION = '1.8.4' APACHE_ANT_VERSION = '1.8.4'
import tarfile
import zipfile
import traceback import traceback
from sys import platform from sys import platform
from buildozer.target import Target from buildozer.target import Target
@ -72,13 +78,10 @@ class TargetAndroid(Target):
archive = 'apache-ant-{0}-bin.tar.gz'.format(APACHE_ANT_VERSION) archive = 'apache-ant-{0}-bin.tar.gz'.format(APACHE_ANT_VERSION)
unpacked = 'apache-ant-{0}'.format(APACHE_ANT_VERSION) unpacked = 'apache-ant-{0}'.format(APACHE_ANT_VERSION)
url = 'http://archive.apache.org/dist/ant/binaries/' url = 'http://archive.apache.org/dist/ant/binaries/'
archive = self.buildozer.download(url, archive, self.buildozer.download(url, archive,
cwd=self.buildozer.platform_dir) cwd=self.buildozer.platform_dir)
tf = tarfile.open(archive, 'r:*') self.buildozer.file_extract(archive, cwd=self.buildozer.platform_dir)
tf.extractall(path=self.buildozer.platform_dir)
tf.close()
self.buildozer.file_rename(unpacked, 'apache-ant', self.buildozer.file_rename(unpacked, 'apache-ant',
cwd=self.buildozer.platform_dir) cwd=self.buildozer.platform_dir)
self.buildozer.log('Apache ANT installation done.') self.buildozer.log('Apache ANT installation done.')
@ -105,19 +108,11 @@ class TargetAndroid(Target):
archive = archive.format(ANDROID_SDK_VERSION) archive = archive.format(ANDROID_SDK_VERSION)
url = 'http://dl.google.com/android/' url = 'http://dl.google.com/android/'
archive = self.buildozer.download(url, archive, self.buildozer.download(url, archive,
cwd=self.buildozer.platform_dir) cwd=self.buildozer.platform_dir)
self.buildozer.log('Unpacking Android SDK') self.buildozer.log('Unpacking Android SDK')
if archive.endswith('.tgz'): self.buildozer.file_extract(archive, cwd=self.buildozer.platform_dir)
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_rename(unpacked, 'android-sdk', self.buildozer.file_rename(unpacked, 'android-sdk',
cwd=self.buildozer.platform_dir) cwd=self.buildozer.platform_dir)
self.buildozer.log('Android SDK installation done.') self.buildozer.log('Android SDK installation done.')
@ -143,19 +138,11 @@ class TargetAndroid(Target):
archive = archive.format(ANDROID_NDK_VERSION) archive = archive.format(ANDROID_NDK_VERSION)
unpacked = unpacked.format(ANDROID_NDK_VERSION) unpacked = unpacked.format(ANDROID_NDK_VERSION)
url = 'http://dl.google.com/android/ndk/' url = 'http://dl.google.com/android/ndk/'
archive = self.buildozer.download(url, archive, self.buildozer.download(url, archive,
cwd=self.buildozer.platform_dir) cwd=self.buildozer.platform_dir)
self.buildozer.log('Unpacking Android NDK') self.buildozer.log('Unpacking Android NDK')
if archive.endswith('.tar.bz2'): self.buildozer.file_extract(archive, cwd=self.buildozer.platform_dir)
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_rename(unpacked, 'android-ndk', self.buildozer.file_rename(unpacked, 'android-ndk',
cwd=self.buildozer.platform_dir) cwd=self.buildozer.platform_dir)
self.buildozer.log('Android NDK installation done.') self.buildozer.log('Android NDK installation done.')
@ -172,11 +159,9 @@ class TargetAndroid(Target):
if not packages: if not packages:
self.buildozer.log('Android packages already installed.') self.buildozer.log('Android packages already installed.')
return 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)), self.android_cmd, ','.join(packages)),
cwd=self.buildozer.platform_dir) cwd=self.buildozer.platform_dir)
print ret[0]
print ret[1]
self.buildozer.log('Android packages installation done.') self.buildozer.log('Android packages installation done.')
def install_platform(self): def install_platform(self):
@ -192,7 +177,7 @@ class TargetAndroid(Target):
cmd('git pull origin master', cwd=pa_dir) 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.sdk_dir = sdk_dir = self._install_android_sdk()
self.ndk_dir = ndk_dir = self._install_android_ndk() self.ndk_dir = ndk_dir = self._install_android_ndk()
self._install_android_packages() self._install_android_packages()
@ -206,8 +191,8 @@ class TargetAndroid(Target):
# for android, the compilation depends really on the app requirements. # for android, the compilation depends really on the app requirements.
# compile the distribution only if the requirements changed. # compile the distribution only if the requirements changed.
last_requirements = self.buildozer.state.get('android.requirements', '') last_requirements = self.buildozer.state.get('android.requirements', '')
app_requirements = self.buildozer.config.get('app', app_requirements = self.buildozer.config.getlist('app',
'requirements', '').split() 'requirements', '')
# we need to extract the requirements that python-for-android knows # we need to extract the requirements that python-for-android knows
# about # about
@ -244,7 +229,14 @@ class TargetAndroid(Target):
cmd('./distribute.sh -m "{0}"'.format(modules_str), cwd=self.pa_dir) cmd('./distribute.sh -m "{0}"'.format(modules_str), cwd=self.pa_dir)
self.buildozer.log('Distribution compiled.') 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): def get_target(buildozer):