From 3dc42cb6273fedd6701d5a5721b16c6d51d89998 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Wed, 19 Dec 2012 03:55:11 +0100 Subject: [PATCH] wip --- README.rst | 11 +++- buildozer/__init__.py | 121 ++++++++++++++++++++++++++++++----- buildozer/targets/android.py | 60 ++++++++--------- 3 files changed, 140 insertions(+), 52 deletions(-) diff --git a/README.rst b/README.rst index 08d6285..4338ff0 100644 --- a/README.rst +++ b/README.rst @@ -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 diff --git a/buildozer/__init__.py b/buildozer/__init__.py index f8b1eb0..8f7a44b 100644 --- a/buildozer/__init__.py +++ b/buildozer/__init__.py @@ -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 diff --git a/buildozer/targets/android.py b/buildozer/targets/android.py index 470dfe9..a7b4e93 100644 --- a/buildozer/targets/android.py +++ b/buildozer/targets/android.py @@ -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):