wip
This commit is contained in:
parent
da7c95b6ee
commit
3dc42cb627
3 changed files with 140 additions and 52 deletions
11
README.rst
11
README.rst
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in a new issue