Merge branch 'master' of https://github.com/kivy/buildozer
Conflicts: .gitignore
This commit is contained in:
commit
3a6ba988bd
12 changed files with 1395 additions and 0 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,6 +1,7 @@
|
|||
*.py[co]
|
||||
|
||||
# Packages
|
||||
.*.swp
|
||||
*.egg
|
||||
*.egg-info
|
||||
dist
|
||||
|
|
7
COPYING
Normal file
7
COPYING
Normal file
|
@ -0,0 +1,7 @@
|
|||
Copyright (c) 2012 Mathieu Virbel <mat@kivy.org>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
2
MANIFEST.in
Normal file
2
MANIFEST.in
Normal file
|
@ -0,0 +1,2 @@
|
|||
include *COPYING
|
||||
recursive-include buildozer *.spec
|
84
README.rst
Normal file
84
README.rst
Normal file
|
@ -0,0 +1,84 @@
|
|||
Buildozer
|
||||
=========
|
||||
|
||||
THIS IS A WORK IN PROGRESS, DO NOT USE.
|
||||
|
||||
Buildozer is a tool for creating application packages easily.
|
||||
|
||||
The goal is to have one "buildozer.spec" file in your app directory: it
|
||||
describe your application requirements, titles, etc. Buildozer will use that
|
||||
spec for create package for Android, iOS, Windows, OSX and Linux.
|
||||
|
||||
Usage example
|
||||
-------------
|
||||
|
||||
#. Install buildozer::
|
||||
|
||||
# latest dev
|
||||
git clone git://github.com/kivy/buildozer
|
||||
cd buildozer
|
||||
sudo python2.7 setup.py install
|
||||
|
||||
# via pip (latest stable)
|
||||
sudo pip install buildozer
|
||||
|
||||
# via easy_install
|
||||
sudo easy_install buildozer
|
||||
|
||||
#. Go into your application directory and do::
|
||||
|
||||
buildozer init
|
||||
# edit the buildozer.spec, then
|
||||
buildozer android debug deploy run
|
||||
|
||||
Example of commands::
|
||||
|
||||
# buildozer commands
|
||||
buildozer clean
|
||||
|
||||
# buildozer target command
|
||||
buildozer android update
|
||||
buildozer android deploy
|
||||
buildozer android debug
|
||||
buildozer android release
|
||||
|
||||
# or all in one (compile in debug, deploy on device)
|
||||
buildozer android debug deploy
|
||||
|
||||
# set the default command if nothing set
|
||||
buildozer setdefault android debug deploy run
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
::
|
||||
|
||||
Usage: buildozer [--verbose] [target] [command1] [command2]
|
||||
|
||||
Available targets:
|
||||
android Android target, based on python-for-android project
|
||||
ios iOS target, based on kivy-ios project. (not working yet.)
|
||||
|
||||
Global commands (without target):
|
||||
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
|
||||
version Show the Buildozer version
|
||||
|
||||
Target commands:
|
||||
clean Clean the target environment
|
||||
update Update the target dependencies
|
||||
debug Build the application in debug mode
|
||||
release Build the application in release mode
|
||||
deploy Deploy the application on the device
|
||||
run Run the application on the device
|
||||
|
||||
|
||||
|
||||
buildozer.spec
|
||||
--------------
|
||||
|
||||
See `buildozer/default.spec <https://raw.github.com/kivy/buildozer/master/buildozer/default.spec>`_ for an up-to-date spec file.
|
||||
|
615
buildozer/__init__.py
Normal file
615
buildozer/__init__.py
Normal file
|
@ -0,0 +1,615 @@
|
|||
'''
|
||||
|
||||
Layout directory for buildozer:
|
||||
|
||||
build/
|
||||
<targetname>/
|
||||
platform/ - all the platform files necessary
|
||||
app/ - compiled application
|
||||
|
||||
|
||||
'''
|
||||
|
||||
__version__ = '0.3-dev'
|
||||
|
||||
import shelve
|
||||
import zipfile
|
||||
import sys
|
||||
import fcntl
|
||||
import os
|
||||
import re
|
||||
from select import select
|
||||
from sys import stdout, stderr, exit
|
||||
from urllib import urlretrieve
|
||||
from re import search
|
||||
from ConfigParser import SafeConfigParser
|
||||
from os.path import join, exists, dirname, realpath, splitext, expanduser
|
||||
from subprocess import Popen, PIPE
|
||||
from os import environ, unlink, rename, walk, sep, listdir, makedirs
|
||||
from copy import copy
|
||||
from shutil import copyfile, rmtree
|
||||
|
||||
RESET_SEQ = "\033[0m"
|
||||
COLOR_SEQ = "\033[1;{0}m"
|
||||
BOLD_SEQ = "\033[1m"
|
||||
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
|
||||
USE_COLOR = 'NO_COLOR' not in environ
|
||||
# error, info, debug
|
||||
LOG_LEVELS_C = (RED, BLUE, BLACK)
|
||||
LOG_LEVELS_T = 'EID'
|
||||
|
||||
class Buildozer(object):
|
||||
|
||||
standard_cmds = ('clean', 'update', 'debug', 'release', 'deploy', 'run')
|
||||
|
||||
def __init__(self, filename='buildozer.spec', target=None):
|
||||
super(Buildozer, self).__init__()
|
||||
self.log_level = 1
|
||||
self.environ = {}
|
||||
self.specfilename = filename
|
||||
self.state = None
|
||||
self.config = SafeConfigParser()
|
||||
self.config.getlist = self._get_config_list
|
||||
self.config.getdefault = self._get_config_default
|
||||
self.config.read(filename)
|
||||
|
||||
try:
|
||||
self.log_level = int(self.config.getdefault(
|
||||
'buildozer', 'log_level', '1'))
|
||||
except:
|
||||
pass
|
||||
|
||||
self.targetname = None
|
||||
self.target = None
|
||||
if target:
|
||||
self.set_target(target)
|
||||
|
||||
def set_target(self, target):
|
||||
'''Set the target to use (one of buildozer.targets, such as "android")
|
||||
'''
|
||||
self.targetname = target
|
||||
m = __import__('buildozer.targets.{0}'.format(target),
|
||||
fromlist=['buildozer'])
|
||||
self.target = m.get_target(self)
|
||||
self.check_build_layout()
|
||||
self.check_configuration_tokens()
|
||||
self.target.check_configuration_tokens()
|
||||
|
||||
def prepare_for_build(self):
|
||||
'''Prepare the build.
|
||||
'''
|
||||
assert(self.target is not None)
|
||||
if hasattr(self.target, '_build_prepared'):
|
||||
return
|
||||
|
||||
self.info('Preparing build')
|
||||
|
||||
self.info('Check requirements for {0}'.format(self.targetname))
|
||||
self.target.check_requirements()
|
||||
|
||||
self.info('Install platform')
|
||||
self.target.install_platform()
|
||||
|
||||
self.info('Compile platform')
|
||||
self.target.compile_platform()
|
||||
|
||||
# flag to prevent multiple build
|
||||
self.target._build_prepared = True
|
||||
|
||||
def build(self):
|
||||
'''Do the build.
|
||||
|
||||
The target can set build_mode to 'release' or 'debug' before calling
|
||||
this method.
|
||||
|
||||
(:meth:`prepare_for_build` must have been call before.)
|
||||
'''
|
||||
assert(self.target is not None)
|
||||
assert(hasattr(self.target, '_build_prepared'))
|
||||
|
||||
if hasattr(self.target, '_build_done'):
|
||||
return
|
||||
|
||||
self.info('Build the application')
|
||||
self.build_application()
|
||||
|
||||
self.info('Package the application')
|
||||
self.target.build_package()
|
||||
|
||||
# flag to prevent multiple build
|
||||
self.target._build_done = True
|
||||
|
||||
#
|
||||
# Log functions
|
||||
#
|
||||
|
||||
def log(self, level, msg):
|
||||
if level > self.log_level:
|
||||
return
|
||||
if USE_COLOR:
|
||||
color = COLOR_SEQ.format(30 + LOG_LEVELS_C[level])
|
||||
print ''.join((RESET_SEQ, color, '# ', msg, RESET_SEQ))
|
||||
else:
|
||||
print LOG_LEVELS_T[level], msg
|
||||
|
||||
def debug(self, msg):
|
||||
self.log(2, msg)
|
||||
|
||||
def info(self, msg):
|
||||
self.log(1, msg)
|
||||
|
||||
def error(self, msg):
|
||||
self.log(0, msg)
|
||||
|
||||
#
|
||||
# Internal check methods
|
||||
#
|
||||
|
||||
def checkbin(self, msg, fn):
|
||||
self.debug('Search for {0}'.format(msg))
|
||||
if exists(fn):
|
||||
return realpath(fn)
|
||||
for dn in environ['PATH'].split(':'):
|
||||
rfn = realpath(join(dn, fn))
|
||||
if exists(rfn):
|
||||
self.debug(' -> found at {0}'.format(rfn))
|
||||
return rfn
|
||||
raise Exception(msg + 'not found')
|
||||
|
||||
def cmd(self, command, **kwargs):
|
||||
#print ' '.join(['{0}={1}'.format(*args) for args in
|
||||
# self.environ.iteritems()])
|
||||
|
||||
# prepare the environ, based on the system + our own env
|
||||
env = copy(environ)
|
||||
env.update(self.environ)
|
||||
|
||||
# prepare the process
|
||||
kwargs.setdefault('env', env)
|
||||
kwargs.setdefault('stdout', PIPE)
|
||||
kwargs.setdefault('stderr', PIPE)
|
||||
kwargs.setdefault('close_fds', True)
|
||||
kwargs.setdefault('shell', True)
|
||||
kwargs.setdefault('show_output', self.log_level > 1)
|
||||
|
||||
show_output = kwargs.pop('show_output')
|
||||
get_stdout = kwargs.pop('get_stdout', False)
|
||||
get_stderr = kwargs.pop('get_stderr', False)
|
||||
|
||||
self.debug('Run {0!r}'.format(command))
|
||||
|
||||
# open the process
|
||||
process = Popen(command, **kwargs)
|
||||
|
||||
# prepare fds
|
||||
fd_stdout = process.stdout.fileno()
|
||||
fd_stderr = process.stderr.fileno()
|
||||
fcntl.fcntl(
|
||||
fd_stdout, fcntl.F_SETFL,
|
||||
fcntl.fcntl(fd_stdout, fcntl.F_GETFL) | os.O_NONBLOCK)
|
||||
fcntl.fcntl(
|
||||
fd_stderr, fcntl.F_SETFL,
|
||||
fcntl.fcntl(fd_stderr, fcntl.F_GETFL) | os.O_NONBLOCK)
|
||||
|
||||
ret_stdout = [] if get_stdout else None
|
||||
ret_stderr = [] if get_stderr else None
|
||||
while True:
|
||||
readx = select([fd_stdout, fd_stderr], [], [])[0]
|
||||
if fd_stdout in readx:
|
||||
chunk = process.stdout.read()
|
||||
if chunk == '':
|
||||
break
|
||||
if get_stdout:
|
||||
ret_stdout.append(chunk)
|
||||
if show_output:
|
||||
stdout.write(chunk)
|
||||
if fd_stderr in readx:
|
||||
chunk = process.stderr.read()
|
||||
if chunk == '':
|
||||
break
|
||||
if get_stderr:
|
||||
ret_stderr.append(chunk)
|
||||
if show_output:
|
||||
stderr.write(chunk)
|
||||
|
||||
stdout.flush()
|
||||
stderr.flush()
|
||||
|
||||
process.communicate()
|
||||
if process.returncode != 0:
|
||||
self.error('Command failed: {0}'.format(command))
|
||||
if ret_stdout:
|
||||
ret_stdout = ''.join(ret_stdout)
|
||||
if ret_stderr:
|
||||
ret_stderr = ''.join(ret_stderr)
|
||||
return (ret_stdout, ret_stderr)
|
||||
|
||||
def check_configuration_tokens(self):
|
||||
'''Ensure the spec file is 'correct'.
|
||||
'''
|
||||
self.info('Check configuration tokens')
|
||||
get = self.config.getdefault
|
||||
errors = []
|
||||
adderror = errors.append
|
||||
if not get('app', 'title', ''):
|
||||
adderror('[app] "title" is missing')
|
||||
if not get('app', 'package.name', ''):
|
||||
adderror('[app] "package.name" is missing')
|
||||
if not get('app', 'source.dir', ''):
|
||||
adderror('[app] "source.dir" is missing')
|
||||
|
||||
version = get('app', 'version', '')
|
||||
version_regex = get('app', 'version.regex', '')
|
||||
if not version and not version_regex:
|
||||
adderror('[app] One of "version" or "version.regex" must be set')
|
||||
if version and version_regex:
|
||||
adderror('[app] Conflict between "version" and "version.regex"'
|
||||
', only one can be used.')
|
||||
if version_regex and not get('app', 'version.filename', ''):
|
||||
adderror('[app] "version.filename" is missing'
|
||||
', required by "version.regex"')
|
||||
|
||||
if errors:
|
||||
self.error('{0} error(s) found in the buildozer.spec'.format(
|
||||
len(errors)))
|
||||
for error in errors:
|
||||
print error
|
||||
exit(1)
|
||||
|
||||
|
||||
def check_build_layout(self):
|
||||
'''Ensure the build (local and global) directory layout and files are
|
||||
ready.
|
||||
'''
|
||||
self.info('Ensure build layout')
|
||||
|
||||
if not exists(self.specfilename):
|
||||
print 'No {0} found in the current directory. Abandon.'.format(
|
||||
self.specfilename)
|
||||
exit(1)
|
||||
|
||||
# create global dir
|
||||
self.mkdir(self.global_buildozer_dir)
|
||||
|
||||
# create local dir
|
||||
specdir = dirname(self.specfilename)
|
||||
self.mkdir(join(specdir, '.buildozer'))
|
||||
self.mkdir(join(specdir, 'bin'))
|
||||
self.state = shelve.open(join(self.buildozer_dir, 'state.db'))
|
||||
|
||||
if self.targetname:
|
||||
target = self.targetname
|
||||
self.mkdir(join(self.global_platform_dir, target, 'platform'))
|
||||
self.mkdir(join(specdir, '.buildozer', target, 'platform'))
|
||||
self.mkdir(join(specdir, '.buildozer', target, 'app'))
|
||||
|
||||
def mkdir(self, dn):
|
||||
if exists(dn):
|
||||
return
|
||||
self.debug('Create directory {0}'.format(dn))
|
||||
makedirs(dn)
|
||||
|
||||
def file_exists(self, *args):
|
||||
return exists(join(*args))
|
||||
|
||||
def file_rename(self, source, target, cwd=None):
|
||||
if cwd:
|
||||
source = join(cwd, source)
|
||||
target = join(cwd, target)
|
||||
self.debug('Rename {0} to {1}'.format(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 clean_platform(self):
|
||||
self.info('Clean the platform build directory')
|
||||
if not exists(self.platform_dir):
|
||||
return
|
||||
rmtree(self.platform_dir)
|
||||
|
||||
def download(self, url, filename, cwd=None):
|
||||
def report_hook(index, blksize, size):
|
||||
if size <= 0:
|
||||
progression = '{0} bytes'.format(index * blksize)
|
||||
else:
|
||||
progression = '{0:.2f}%'.format(
|
||||
index * blksize * 100. / float(size))
|
||||
print '- Download', progression, '\r',
|
||||
stdout.flush()
|
||||
|
||||
url = url + filename
|
||||
if cwd:
|
||||
filename = join(cwd, filename)
|
||||
if self.file_exists(filename):
|
||||
unlink(filename)
|
||||
|
||||
self.debug('Downloading {0}'.format(url))
|
||||
urlretrieve(url, filename, report_hook)
|
||||
return filename
|
||||
|
||||
def get_version(self):
|
||||
c = self.config
|
||||
has_version = c.has_option('app', 'version')
|
||||
has_regex = c.has_option('app', 'version.regex')
|
||||
has_filename = c.has_option('app', 'version.filename')
|
||||
|
||||
# version number specified
|
||||
if has_version:
|
||||
if has_regex or has_filename:
|
||||
raise Exception(
|
||||
'version.regex and version.filename conflict with version')
|
||||
return c.get('app', 'version')
|
||||
|
||||
# search by regex
|
||||
if has_regex or has_filename:
|
||||
if has_regex and not has_filename:
|
||||
raise Exception('version.filename is missing')
|
||||
if has_filename and not has_regex:
|
||||
raise Exception('version.regex is missing')
|
||||
|
||||
fn = c.get('app', 'version.filename')
|
||||
with open(fn) as fd:
|
||||
data = fd.read()
|
||||
regex = c.get('app', 'version.regex')
|
||||
match = search(regex, data)
|
||||
if not match:
|
||||
raise Exception(
|
||||
'Unable to found capture version in {0}'.format(fn))
|
||||
version = match.groups()[0]
|
||||
self.debug('Captured version: {0}'.format(version))
|
||||
return version
|
||||
|
||||
raise Exception('Missing version or version.regex + version.filename')
|
||||
|
||||
def build_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.debug('Copy {0}'.format(sfn))
|
||||
copyfile(sfn, rfn)
|
||||
|
||||
def namify(self, name):
|
||||
'''Return a "valid" name from a name with lot of invalid chars
|
||||
(allowed characters: a-z, A-Z, 0-9, -, _)
|
||||
'''
|
||||
return re.sub('[^a-zA-Z0-9_\-]', '_', name)
|
||||
|
||||
@property
|
||||
def buildozer_dir(self):
|
||||
return realpath(join(
|
||||
dirname(self.specfilename), '.buildozer'))
|
||||
|
||||
@property
|
||||
def platform_dir(self):
|
||||
return join(self.buildozer_dir, self.targetname, 'platform')
|
||||
|
||||
@property
|
||||
def app_dir(self):
|
||||
return join(self.buildozer_dir, self.targetname, 'app')
|
||||
|
||||
@property
|
||||
def bin_dir(self):
|
||||
return realpath(join(
|
||||
dirname(self.specfilename), 'bin'))
|
||||
|
||||
@property
|
||||
def global_buildozer_dir(self):
|
||||
return join(expanduser('~'), '.buildozer')
|
||||
|
||||
@property
|
||||
def global_platform_dir(self):
|
||||
return join(self.global_buildozer_dir, self.targetname, 'platform')
|
||||
|
||||
|
||||
|
||||
#
|
||||
# command line invocation
|
||||
#
|
||||
|
||||
def targets(self):
|
||||
for fn in listdir(join(dirname(__file__), 'targets')):
|
||||
if fn.startswith('.') or fn.startswith('__'):
|
||||
continue
|
||||
if not fn.endswith('.py'):
|
||||
continue
|
||||
target = fn[:-3]
|
||||
try:
|
||||
m = __import__('buildozer.targets.{0}'.format(target),
|
||||
fromlist=['buildozer'])
|
||||
yield target, m
|
||||
except:
|
||||
pass
|
||||
|
||||
def usage(self):
|
||||
print 'Usage: buildozer [--verbose] [target] [command1] [command2]'
|
||||
print
|
||||
print 'Available targets:'
|
||||
targets = list(self.targets())
|
||||
for target, m in targets:
|
||||
doc = m.__doc__.strip().splitlines()[0].strip()
|
||||
print ' {0:<18} {1}'.format(target, doc)
|
||||
|
||||
print
|
||||
print 'Global commands (without target):'
|
||||
cmds = [x for x in dir(self) if x.startswith('cmd_')]
|
||||
for cmd in cmds:
|
||||
name = cmd[4:]
|
||||
meth = getattr(self, cmd)
|
||||
|
||||
doc = [x for x in
|
||||
meth.__doc__.strip().splitlines()][0].strip()
|
||||
print ' {0:<18} {1}'.format(name, doc)
|
||||
|
||||
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'
|
||||
|
||||
for target, m in targets:
|
||||
mt = m.get_target(self)
|
||||
commands = mt.get_custom_commands()
|
||||
if not commands:
|
||||
continue
|
||||
print
|
||||
print 'Target "{0}" commands:'.format(target)
|
||||
for command, doc in commands:
|
||||
doc = doc.strip().splitlines()[0].strip()
|
||||
print ' {0:<18} {1}'.format(command, doc)
|
||||
|
||||
print
|
||||
|
||||
|
||||
def run_default(self):
|
||||
self.check_build_layout()
|
||||
if 'buildozer:defaultcommand' not in self.state:
|
||||
print 'No default command set.'
|
||||
print 'Use "buildozer setdefault <command args...>"'
|
||||
exit(1)
|
||||
cmd = self.state['buildozer:defaultcommand']
|
||||
self.run_command(cmd)
|
||||
|
||||
def run_command(self, args):
|
||||
while args:
|
||||
if not args[0].startswith('-'):
|
||||
break
|
||||
arg = args.pop(0)
|
||||
|
||||
if arg in ('-v', '--verbose'):
|
||||
self.log_level = 2
|
||||
|
||||
if arg in ('-h', '--help'):
|
||||
self.usage()
|
||||
exit(0)
|
||||
|
||||
if args == '--version':
|
||||
print 'Buildozer {0}'.format(__version__)
|
||||
exit(0)
|
||||
|
||||
if not args:
|
||||
self.run_default()
|
||||
return
|
||||
|
||||
command, args = args[0], args[1:]
|
||||
cmd = 'cmd_{0}'.format(command)
|
||||
|
||||
# internal commands ?
|
||||
if hasattr(self, cmd):
|
||||
getattr(self, cmd)(*args)
|
||||
return
|
||||
|
||||
# maybe it's a target?
|
||||
targets = [x[0] for x in self.targets()]
|
||||
if command not in targets:
|
||||
print 'Unknow command/target', command
|
||||
exit(1)
|
||||
|
||||
self.set_target(command)
|
||||
self.target.run_commands(args)
|
||||
|
||||
def cmd_init(self, *args):
|
||||
'''Create a initial buildozer.spec in the current directory
|
||||
'''
|
||||
if exists('buildozer.spec'):
|
||||
print 'ERROR: You already have a buildozer.spec file.'
|
||||
exit(1)
|
||||
copyfile(join(dirname(__file__), 'default.spec'), 'buildozer.spec')
|
||||
print 'File buildozer.spec created, ready to customize!'
|
||||
|
||||
def cmd_clean(self, *args):
|
||||
'''Clean the whole Buildozer environment.
|
||||
'''
|
||||
pass
|
||||
|
||||
def cmd_help(self, *args):
|
||||
'''Show the Buildozer help.
|
||||
'''
|
||||
self.usage()
|
||||
|
||||
def cmd_setdefault(self, *args):
|
||||
'''Set the default command to do when to arguments are given
|
||||
'''
|
||||
self.check_build_layout()
|
||||
self.state['buildozer:defaultcommand'] = args
|
||||
|
||||
def cmd_version(self, *args):
|
||||
'''Show the Buildozer version
|
||||
'''
|
||||
print 'Buildozer {0}'.format(__version__)
|
||||
|
||||
#
|
||||
# Private
|
||||
#
|
||||
|
||||
def _get_config_list(self, section, token, default=None):
|
||||
# monkey-patch method for ConfigParser
|
||||
# get a key as a list of string, seperated from the comma
|
||||
values = self.config.getdefault(section, token, default).split(',')
|
||||
return [x.strip() for x in values]
|
||||
|
||||
def _get_config_default(self, section, token, default=None):
|
||||
# monkey-patch method for ConfigParser
|
||||
# get a key in a section, or the default
|
||||
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 run():
|
||||
Buildozer().run_command(sys.argv[1:])
|
||||
|
62
buildozer/default.spec
Normal file
62
buildozer/default.spec
Normal file
|
@ -0,0 +1,62 @@
|
|||
[app]
|
||||
|
||||
# (str) Title of your application
|
||||
title = My Application
|
||||
|
||||
# (str) Package name
|
||||
package.name = myapp
|
||||
|
||||
# (str) Package domain (needed for android/ios packaging)
|
||||
package.domain = org.test
|
||||
|
||||
# (str) Source code where the main.py live
|
||||
source.dir = .
|
||||
|
||||
# (list) Source files to include (let empty to include all the files)
|
||||
source.include_exts = py,png,jpg
|
||||
|
||||
# (list) Source files to exclude (let empty to not excluding anything)
|
||||
#source.exclude_exts = spec
|
||||
|
||||
# (str) Application versionning (method 1)
|
||||
version.regex = __version__ = '(.*)'
|
||||
version.filename = %(source.dir)s/main.py
|
||||
|
||||
# (str) Application versionning (method 2)
|
||||
# version = 1.2.0
|
||||
|
||||
# (list) Application requirements
|
||||
requirements = twisted,kivy
|
||||
|
||||
#
|
||||
# Android specific
|
||||
#
|
||||
|
||||
# (list) Permissions
|
||||
#android.permissions = INTERNET
|
||||
|
||||
# (int) Android API to use
|
||||
#android.api = 14
|
||||
|
||||
# (int) Minimum API required (8 = Android 2.2 devices)
|
||||
#android.minapi = 8
|
||||
|
||||
# (int) Android SDK version to use
|
||||
#android.sdk = 21
|
||||
|
||||
# (str) Android NDK version to use
|
||||
#android.ndk = 8c
|
||||
|
||||
# (str) Android NDK directory (if empty, it will be automatically downloaded.)
|
||||
#android.ndk_path =
|
||||
|
||||
# (str) Android SDK directory (if empty, it will be automatically downloaded.)
|
||||
#android.sdk_path =
|
||||
|
||||
# (str) Android entry point, default is ok for Kivy-based app
|
||||
#android.entrypoint = org.renpy.android.PythonActivity
|
||||
|
||||
[buildozer]
|
||||
|
||||
# (int) Log level (0 = error only, 1 = info, 2 = debug (with command output))
|
||||
log_level = 1
|
92
buildozer/target.py
Normal file
92
buildozer/target.py
Normal file
|
@ -0,0 +1,92 @@
|
|||
from sys import exit
|
||||
|
||||
class Target(object):
|
||||
|
||||
def __init__(self, buildozer):
|
||||
super(Target, self).__init__()
|
||||
self.buildozer = buildozer
|
||||
self.build_mode = 'debug'
|
||||
self.platform_update = False
|
||||
|
||||
def check_requirements(self):
|
||||
pass
|
||||
|
||||
def check_configuration_tokens(self, errors=None):
|
||||
if errors:
|
||||
self.buildozer.info('Check target configuration tokens')
|
||||
self.buildozer.error(
|
||||
'{0} error(s) found in the buildozer.spec'.format(
|
||||
len(errors)))
|
||||
for error in errors:
|
||||
print error
|
||||
exit(1)
|
||||
|
||||
def compile_platform(self):
|
||||
pass
|
||||
|
||||
def install_platform(self):
|
||||
pass
|
||||
|
||||
def get_custom_commands(self):
|
||||
result = []
|
||||
for x in dir(self):
|
||||
if not x.startswith('cmd_'):
|
||||
continue
|
||||
if x[4:] in self.buildozer.standard_cmds:
|
||||
continue
|
||||
result.append((x[4:], getattr(self, x).__doc__))
|
||||
return result
|
||||
|
||||
def run_commands(self, args):
|
||||
if not args:
|
||||
self.buildozer.error('Missing target command')
|
||||
self.buildozer.usage()
|
||||
exit(1)
|
||||
|
||||
result = []
|
||||
last_command = []
|
||||
for arg in args:
|
||||
if not arg.startswith('--'):
|
||||
if last_command:
|
||||
result.append(last_command)
|
||||
last_command = []
|
||||
last_command.append(arg)
|
||||
else:
|
||||
if not last_command:
|
||||
self.buildozer.error('Argument passed without a command')
|
||||
self.buildozer.usage()
|
||||
exit(1)
|
||||
last_command.append(arg)
|
||||
if last_command:
|
||||
result.append(last_command)
|
||||
|
||||
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))
|
||||
exit(1)
|
||||
getattr(self, 'cmd_{0}'.format(command))(args)
|
||||
|
||||
def cmd_clean(self, *args):
|
||||
self.buildozer.clean_platform()
|
||||
|
||||
def cmd_update(self, *args):
|
||||
self.platform_update = True
|
||||
self.buildozer.prepare_for_build()
|
||||
|
||||
def cmd_debug(self, *args):
|
||||
self.buildozer.prepare_for_build()
|
||||
self.build_mode = 'debug'
|
||||
self.buildozer.build()
|
||||
|
||||
def cmd_release(self, *args):
|
||||
self.buildozer.prepare_for_build()
|
||||
self.build_mode = 'release'
|
||||
self.buildozer.build()
|
||||
|
||||
def cmd_deploy(self, *args):
|
||||
self.buildozer.prepare_for_build()
|
||||
|
||||
def cmd_run(self, *args):
|
||||
self.buildozer.prepare_for_build()
|
||||
|
0
buildozer/targets/__init__.py
Normal file
0
buildozer/targets/__init__.py
Normal file
453
buildozer/targets/android.py
Normal file
453
buildozer/targets/android.py
Normal file
|
@ -0,0 +1,453 @@
|
|||
'''
|
||||
Android target, based on python-for-android project
|
||||
'''
|
||||
#
|
||||
# Android target
|
||||
# Thanks for Renpy (again) for its install_sdk.py and plat.py in the PGS4A
|
||||
# project!
|
||||
#
|
||||
|
||||
|
||||
ANDROID_API = '14'
|
||||
ANDROID_MINAPI = '8'
|
||||
ANDROID_SDK_VERSION = '21'
|
||||
ANDROID_NDK_VERSION = '8c'
|
||||
APACHE_ANT_VERSION = '1.8.4'
|
||||
|
||||
|
||||
import traceback
|
||||
from pipes import quote
|
||||
from sys import platform, executable
|
||||
from buildozer.target import Target
|
||||
from os.path import join, realpath
|
||||
from shutil import copyfile
|
||||
|
||||
|
||||
class TargetAndroid(Target):
|
||||
|
||||
@property
|
||||
def android_sdk_version(self):
|
||||
return self.buildozer.config.getdefault(
|
||||
'app', 'android.sdk', ANDROID_SDK_VERSION)
|
||||
|
||||
@property
|
||||
def android_ndk_version(self):
|
||||
return self.buildozer.config.getdefault(
|
||||
'app', 'android.ndk', ANDROID_NDK_VERSION)
|
||||
|
||||
@property
|
||||
def android_api(self):
|
||||
return self.buildozer.config.getdefault(
|
||||
'app', 'android.api', ANDROID_API)
|
||||
|
||||
@property
|
||||
def android_minapi(self):
|
||||
return self.buildozer.config.getdefault(
|
||||
'app', 'android.minapi', ANDROID_MINAPI)
|
||||
|
||||
@property
|
||||
def android_sdk_dir(self):
|
||||
directory = self.buildozer.config.getdefault(
|
||||
'app', 'android.sdk_path', '')
|
||||
if directory:
|
||||
return realpath(directory)
|
||||
version = self.buildozer.config.getdefault(
|
||||
'app', 'android.sdk', self.android_sdk_version)
|
||||
return join(self.buildozer.global_platform_dir,
|
||||
'android-sdk-{0}'.format(version))
|
||||
|
||||
@property
|
||||
def android_ndk_dir(self):
|
||||
directory = self.buildozer.config.getdefault(
|
||||
'app', 'android.ndk_path', '')
|
||||
if directory:
|
||||
return realpath(directory)
|
||||
version = self.buildozer.config.getdefault(
|
||||
'app', 'android.ndk', self.android_ndk_version)
|
||||
return join(self.buildozer.global_platform_dir,
|
||||
'android-sdk-{0}'.format(version))
|
||||
|
||||
@property
|
||||
def apache_ant_dir(self):
|
||||
directory = self.buildozer.config.getdefault(
|
||||
'app', 'android.ant_path', '')
|
||||
if directory:
|
||||
return realpath(directory)
|
||||
version = self.buildozer.config.getdefault(
|
||||
'app', 'android.ant', APACHE_ANT_VERSION)
|
||||
return join(self.buildozer.global_platform_dir,
|
||||
'apache-ant-{0}'.format(version))
|
||||
|
||||
def check_requirements(self):
|
||||
if platform in ('win32', 'cygwin'):
|
||||
try:
|
||||
self._set_win32_java_home()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
self.android_cmd = join(self.android_sdk_dir, 'tools', 'android.bat')
|
||||
self.ant_cmd = join(self.apache_ant_dir, 'bin', 'ant.bat')
|
||||
self.adb_cmd = join(self.android_sdk_dir, 'platform-tools', 'adb.exe')
|
||||
self.javac_cmd = self._locate_java('javac.exe')
|
||||
self.keytool_cmd = self._locate_java('keytool.exe')
|
||||
elif platform in ('darwin', ):
|
||||
self.android_cmd = join(self.android_sdk_dir, 'tools', 'android')
|
||||
self.ant_cmd = join(self.apache_ant_dir, 'bin', 'ant')
|
||||
self.adb_cmd = join(self.android_sdk_dir, 'platform-tools', 'adb')
|
||||
self.javac_cmd = self._locate_java('javac')
|
||||
self.keytool_cmd = self._locate_java('keytool')
|
||||
else:
|
||||
self.android_cmd = join(self.android_sdk_dir, 'tools', 'android')
|
||||
self.ant_cmd = join(self.apache_ant_dir, 'bin', 'ant')
|
||||
self.adb_cmd = join(self.android_sdk_dir, 'platform-tools', 'adb')
|
||||
self.javac_cmd = self._locate_java('javac')
|
||||
self.keytool_cmd = self._locate_java('keytool')
|
||||
|
||||
checkbin = self.buildozer.checkbin
|
||||
checkbin('Git git', 'git')
|
||||
checkbin('Cython cython', 'cython')
|
||||
checkbin('Java compiler', self.javac_cmd)
|
||||
checkbin('Java keytool', self.keytool_cmd)
|
||||
|
||||
def check_configuration_tokens(self):
|
||||
errors = []
|
||||
|
||||
# check the permission
|
||||
available_permissions = self._get_available_permissions()
|
||||
if available_permissions:
|
||||
permissions = self.buildozer.config.getlist(
|
||||
'app', 'android.permissions', [])
|
||||
for permission in permissions:
|
||||
if permission not in available_permissions:
|
||||
errors.append(
|
||||
'[app] "android.permission" contain an unknown'
|
||||
' permission {0}'.format(permission))
|
||||
|
||||
super(TargetAndroid, self).check_configuration_tokens(errors)
|
||||
|
||||
def _get_available_permissions(self):
|
||||
key = 'android:available_permissions'
|
||||
key_sdk = 'android:available_permissions_sdk'
|
||||
|
||||
refresh_permissions = False
|
||||
sdk = self.buildozer.state.get(key_sdk, None)
|
||||
if not sdk or sdk != self.android_sdk_version:
|
||||
refresh_permissions = True
|
||||
if key not in self.buildozer.state:
|
||||
refresh_permissions = True
|
||||
if not refresh_permissions:
|
||||
return self.buildozer.state[key]
|
||||
|
||||
try:
|
||||
self.buildozer.debug('Read available permissions from api-versions.xml')
|
||||
import xml.etree.ElementTree as ET
|
||||
fn = join(self.android_sdk_dir, 'platform-tools',
|
||||
'api', 'api-versions.xml')
|
||||
with open(fn) as fd:
|
||||
doc = ET.fromstring(fd.read())
|
||||
fields = doc.findall('.//class[@name="android/Manifest$permission"]/field[@name]')
|
||||
available_permissions = [x.attrib['name'] for x in fields]
|
||||
|
||||
self.buildozer.state[key] = available_permissions
|
||||
self.buildozer.state[key_sdk] = self.android_sdk_version
|
||||
return available_permissions
|
||||
except:
|
||||
return None
|
||||
|
||||
def _set_win32_java_home(self):
|
||||
if 'JAVA_HOME' in self.buildozer.environ:
|
||||
return
|
||||
import _winreg
|
||||
with _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\JavaSoft\Java Development Kit") as jdk: #@UndefinedVariable
|
||||
current_version, _type = _winreg.QueryValueEx(jdk, "CurrentVersion") #@UndefinedVariable
|
||||
with _winreg.OpenKey(jdk, current_version) as cv: #@UndefinedVariable
|
||||
java_home, _type = _winreg.QueryValueEx(cv, "JavaHome") #@UndefinedVariable
|
||||
self.buildozer.environ['JAVA_HOME'] = java_home
|
||||
|
||||
def _locate_java(self, s):
|
||||
'''If JAVA_HOME is in the environ, return $JAVA_HOME/bin/s. Otherwise,
|
||||
return s.
|
||||
'''
|
||||
if 'JAVA_HOME' in self.buildozer.environ:
|
||||
return join(self.buildozer.environ['JAVA_HOME'], 'bin', s)
|
||||
else:
|
||||
return s
|
||||
|
||||
def _install_apache_ant(self):
|
||||
ant_dir = self.apache_ant_dir
|
||||
if self.buildozer.file_exists(ant_dir):
|
||||
self.buildozer.info('Apache ANT found at {0}'.format(ant_dir))
|
||||
return ant_dir
|
||||
|
||||
self.buildozer.info('Android ANT is missing, downloading')
|
||||
archive = 'apache-ant-{0}-bin.tar.gz'.format(APACHE_ANT_VERSION)
|
||||
url = 'http://archive.apache.org/dist/ant/binaries/'
|
||||
self.buildozer.download(url, archive,
|
||||
cwd=self.buildozer.global_platform_dir)
|
||||
self.buildozer.file_extract(archive,
|
||||
cwd=self.buildozer.global_platform_dir)
|
||||
self.buildozer.info('Apache ANT installation done.')
|
||||
return ant_dir
|
||||
|
||||
def _install_android_sdk(self):
|
||||
sdk_dir = self.android_sdk_dir
|
||||
if self.buildozer.file_exists(sdk_dir):
|
||||
self.buildozer.info('Android SDK found at {0}'.format(sdk_dir))
|
||||
return sdk_dir
|
||||
|
||||
self.buildozer.info('Android SDK is missing, downloading')
|
||||
if platform in ('win32', 'cygwin'):
|
||||
archive = 'android-sdk_r{0}-windows.zip'
|
||||
unpacked = 'android-sdk-windows'
|
||||
elif platform in ('darwin', ):
|
||||
archive = 'android-sdk_r{0}-macosx.zip'
|
||||
unpacked = 'android-sdk-macosx'
|
||||
elif platform in ('linux2', 'linux3'):
|
||||
archive = 'android-sdk_r{0}-linux.tgz'
|
||||
unpacked = 'android-sdk-linux'
|
||||
else:
|
||||
raise SystemError('Unsupported platform: {0}'.format(platform))
|
||||
|
||||
archive = archive.format(self.android_sdk_version)
|
||||
url = 'http://dl.google.com/android/'
|
||||
self.buildozer.download(url, archive,
|
||||
cwd=self.buildozer.global_platform_dir)
|
||||
|
||||
self.buildozer.info('Unpacking Android SDK')
|
||||
self.buildozer.file_extract(archive,
|
||||
cwd=self.buildozer.global_platform_dir)
|
||||
self.buildozer.file_rename(unpacked, sdk_dir,
|
||||
cwd=self.buildozer.global_platform_dir)
|
||||
self.buildozer.info('Android SDK installation done.')
|
||||
return sdk_dir
|
||||
|
||||
def _install_android_ndk(self):
|
||||
ndk_dir = self.android_ndk_dir
|
||||
if self.buildozer.file_exists(ndk_dir):
|
||||
self.buildozer.info('Android NDK found at {0}'.format(ndk_dir))
|
||||
return ndk_dir
|
||||
|
||||
self.buildozer.info('Android NDK is missing, downloading')
|
||||
if platform in ('win32', 'cygwin'):
|
||||
archive = 'android-ndk-r{0}-windows.zip'
|
||||
elif platform in ('darwin', ):
|
||||
archive = 'android-ndk-r{0}-darwin.tar.bz2'
|
||||
elif platform in ('linux2', 'linux3'):
|
||||
archive = 'android-ndk-r{0}-linux-x86.tar.bz2'
|
||||
else:
|
||||
raise SystemError('Unsupported platform: {0}'.format(platform))
|
||||
|
||||
unpacked = 'android-ndk-r{0}'
|
||||
archive = archive.format(self.android_ndk_version)
|
||||
unpacked = unpacked.format(self.android_ndk_version)
|
||||
url = 'http://dl.google.com/android/ndk/'
|
||||
self.buildozer.download(url, archive,
|
||||
cwd=self.buildozer.global_platform_dir)
|
||||
|
||||
self.buildozer.info('Unpacking Android NDK')
|
||||
self.buildozer.file_extract(archive,
|
||||
cwd=self.buildozer.global_platform_dir)
|
||||
self.buildozer.file_rename(unpacked, ndk_dir,
|
||||
cwd=self.buildozer.global_platform_dir)
|
||||
self.buildozer.info('Android NDK installation done.')
|
||||
return ndk_dir
|
||||
|
||||
def _install_android_packages(self):
|
||||
packages = []
|
||||
android_platform = join(self.android_sdk_dir, 'platforms',
|
||||
'android-{0}'.format(self.android_api))
|
||||
if not self.buildozer.file_exists(android_platform):
|
||||
packages.append('android-{0}'.format(self.android_api))
|
||||
if not self.buildozer.file_exists(self.android_sdk_dir, 'platform-tools'):
|
||||
packages.append('platform-tools')
|
||||
if not packages:
|
||||
self.buildozer.info('Android packages already installed.')
|
||||
return
|
||||
self.buildozer.cmd('{0} update sdk -u -a -t {1}'.format(
|
||||
self.android_cmd, ','.join(packages)),
|
||||
cwd=self.buildozer.global_platform_dir)
|
||||
self.buildozer.info('Android packages installation done.')
|
||||
|
||||
def install_platform(self):
|
||||
cmd = self.buildozer.cmd
|
||||
self.pa_dir = pa_dir = join(self.buildozer.platform_dir, 'python-for-android')
|
||||
if not self.buildozer.file_exists(pa_dir):
|
||||
cmd('git clone git://github.com/kivy/python-for-android',
|
||||
cwd=self.buildozer.platform_dir)
|
||||
elif self.platform_update:
|
||||
cmd('git clean -dxf', cwd=pa_dir)
|
||||
cmd('git pull origin master', cwd=pa_dir)
|
||||
|
||||
self._install_apache_ant()
|
||||
self._install_android_sdk()
|
||||
self._install_android_ndk()
|
||||
self._install_android_packages()
|
||||
|
||||
# ultimate configuration check.
|
||||
# some of our configuration cannot be check without platform.
|
||||
self.check_configuration_tokens()
|
||||
|
||||
self.buildozer.environ.update({
|
||||
'ANDROIDSDK': self.android_sdk_dir,
|
||||
'ANDROIDNDK': self.android_ndk_dir,
|
||||
'ANDROIDAPI': ANDROID_API,
|
||||
'ANDROIDNDKVER': self.android_ndk_version})
|
||||
|
||||
def compile_platform(self):
|
||||
# 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.getlist('app',
|
||||
'requirements', '')
|
||||
|
||||
# we need to extract the requirements that python-for-android knows
|
||||
# about
|
||||
available_modules = self.buildozer.cmd(
|
||||
'./distribute.sh -l', cwd=self.pa_dir, get_stdout=True)[0]
|
||||
if not available_modules.startswith('Available modules:'):
|
||||
self.buildozer.error('Python-for-android invalid output for -l')
|
||||
available_modules = available_modules[19:].splitlines()[0].split()
|
||||
|
||||
android_requirements = [x for x in app_requirements if x in
|
||||
available_modules]
|
||||
missing_requirements = [x for x in app_requirements if x not in
|
||||
available_modules]
|
||||
|
||||
if missing_requirements:
|
||||
self.buildozer.error(
|
||||
'Cannot package the app cause of the missing'
|
||||
' requirements in python-for-android: {0}'.format(
|
||||
missing_requirements))
|
||||
exit(1)
|
||||
|
||||
need_compile = 0
|
||||
if last_requirements != android_requirements:
|
||||
need_compile = 1
|
||||
if not self.buildozer.file_exists(self.pa_dir, 'dist', 'default'):
|
||||
need_compile = 1
|
||||
|
||||
if not need_compile:
|
||||
self.buildozer.info('Distribution already compiled, pass.')
|
||||
return
|
||||
|
||||
modules_str = ' '.join(android_requirements)
|
||||
cmd = self.buildozer.cmd
|
||||
cmd('git clean -dxf', cwd=self.pa_dir)
|
||||
cmd('./distribute.sh -m "{0}"'.format(modules_str), cwd=self.pa_dir)
|
||||
self.buildozer.info('Distribution compiled.')
|
||||
|
||||
# ensure we will not compile again
|
||||
self.buildozer.state['android.requirements'] = android_requirements
|
||||
self.buildozer.state.sync()
|
||||
|
||||
def _get_package(self):
|
||||
config = self.buildozer.config
|
||||
package_domain = config.getdefault('app', 'package.domain', '')
|
||||
package = config.get('app', 'package.name')
|
||||
if package_domain:
|
||||
package = package_domain + '.' + package
|
||||
return package
|
||||
|
||||
def build_package(self):
|
||||
dist_dir = join(self.pa_dir, 'dist', 'default')
|
||||
config = self.buildozer.config
|
||||
package = self._get_package()
|
||||
version = self.buildozer.get_version()
|
||||
|
||||
build_cmd = (
|
||||
'{python} build.py --name {name}'
|
||||
' --version {version}'
|
||||
' --package {package}'
|
||||
' --private {appdir}'
|
||||
' --sdk {androidsdk}'
|
||||
' --minsdk {androidminsdk}').format(
|
||||
python=executable,
|
||||
name=quote(config.get('app', 'title')),
|
||||
version=version,
|
||||
package=package,
|
||||
appdir=self.buildozer.app_dir,
|
||||
androidminsdk=config.getdefault(
|
||||
'app', 'android.minsdk', 8),
|
||||
androidsdk=config.getdefault(
|
||||
'app', 'android.sdk', ANDROID_API))
|
||||
|
||||
# add permissions
|
||||
permissions = config.getlist('app',
|
||||
'android.permissions', [])
|
||||
for permission in permissions:
|
||||
build_cmd += ' --permission {0}'.format(permission)
|
||||
|
||||
# build only in debug right now.
|
||||
if self.build_mode == 'debug':
|
||||
build_cmd += ' debug'
|
||||
mode = 'debug'
|
||||
else:
|
||||
build_cmd += ' release'
|
||||
mode = 'release-unsigned'
|
||||
self.buildozer.cmd(build_cmd, cwd=dist_dir)
|
||||
|
||||
# XXX found how the apk name is really built from the title
|
||||
bl = '\'" ,'
|
||||
apktitle = ''.join([x for x in config.get('app', 'title') if x not in
|
||||
bl])
|
||||
apk = '{title}-{version}-{mode}.apk'.format(
|
||||
title=apktitle, version=version, mode=mode)
|
||||
|
||||
# copy to our place
|
||||
copyfile(join(dist_dir, 'bin', apk),
|
||||
join(self.buildozer.bin_dir, apk))
|
||||
|
||||
self.buildozer.info('Android packaging done!')
|
||||
self.buildozer.info('APK {0} available in the bin directory'.format(apk))
|
||||
self.buildozer.state['android:latestapk'] = apk
|
||||
self.buildozer.state['android:latestmode'] = self.build_mode
|
||||
|
||||
def cmd_deploy(self, *args):
|
||||
super(TargetAndroid, self).cmd_deploy(*args)
|
||||
state = self.buildozer.state
|
||||
if 'android:latestapk' not in state:
|
||||
self.buildozer.error(
|
||||
'No APK built yet. Run "debug" first.')
|
||||
|
||||
if state.get('android:latestmode', '') != 'debug':
|
||||
self.buildozer.error(
|
||||
'Only debug APK are supported for deploy')
|
||||
|
||||
# search the APK in the bin dir
|
||||
apk = state['android:latestapk']
|
||||
full_apk = join(self.buildozer.bin_dir, apk)
|
||||
if not self.buildozer.file_exists(full_apk):
|
||||
self.buildozer.error(
|
||||
'Unable to found the latest APK. Please run "debug" again.')
|
||||
|
||||
# push on the device
|
||||
self.buildozer.cmd('{0} install -r {1}'.format(
|
||||
self.adb_cmd, full_apk), cwd=self.buildozer.global_platform_dir)
|
||||
|
||||
self.buildozer.info('Application pushed on the device.')
|
||||
|
||||
def cmd_run(self, *args):
|
||||
super(TargetAndroid, self).cmd_run(*args)
|
||||
|
||||
entrypoint = self.buildozer.config.getdefault(
|
||||
'app', 'android.entrypoint', 'org.renpy.android.PythonActivity')
|
||||
package = self._get_package()
|
||||
|
||||
self.buildozer.cmd(
|
||||
'{adb} shell am start -n {package}/{entry} -a {entry}'.format(
|
||||
adb=self.adb_cmd, package=package, entry=entrypoint),
|
||||
cwd=self.buildozer.global_platform_dir)
|
||||
|
||||
self.buildozer.info('Application started on the device.')
|
||||
|
||||
def cmd_logcat(self, *args):
|
||||
'''Show the log from the device
|
||||
'''
|
||||
self.check_requirements()
|
||||
self.buildozer.cmd('{adb} logcat'.format(adb=self.adb_cmd),
|
||||
cwd=self.buildozer.global_platform_dir,
|
||||
show_output=True)
|
||||
|
||||
|
||||
|
||||
def get_target(buildozer):
|
||||
return TargetAndroid(buildozer)
|
56
buildozer/targets/ios.py
Normal file
56
buildozer/targets/ios.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
'''
|
||||
iOS target, based on kivy-ios project. (not working yet.)
|
||||
'''
|
||||
|
||||
from buildozer.target import Target
|
||||
from os.path import join
|
||||
|
||||
class TargetIos(Target):
|
||||
|
||||
def check_requirements(self):
|
||||
checkbin = self.buildozer.checkbin
|
||||
cmd = self.buildozer.cmd
|
||||
|
||||
checkbin('Xcode xcodebuild', 'xcodebuild')
|
||||
checkbin('Xcode xcode-select', 'xcode-select')
|
||||
checkbin('Git git', 'git')
|
||||
|
||||
self.buildozer.debug('Check availability of a iPhone SDK')
|
||||
sdk = cmd('xcodebuild -showsdks | fgrep "iphoneos" | tail -n 1 | awk \'{print $2}\'')[0]
|
||||
if not sdk:
|
||||
raise Exception(
|
||||
'No iPhone SDK found. Please install at least one iOS SDK.')
|
||||
else:
|
||||
print ' -> found %r' % sdk
|
||||
|
||||
self.buildozer.debug('Check Xcode path')
|
||||
xcode = cmd('xcode-select -print-path')[0]
|
||||
if not xcode:
|
||||
raise Exception('Unable to get xcode path')
|
||||
self.buildozer.debug(' -> found {0}'.format(xcode))
|
||||
|
||||
def install_platform(self):
|
||||
cmd = self.buildozer.cmd
|
||||
self.ios_dir = ios_dir = join(self.buildozer.platform_dir, 'kivy-ios')
|
||||
if not self.buildozer.file_exists(ios_dir):
|
||||
cmd('git clone git://github.com/kivy/kivy-ios',
|
||||
cwd=self.buildozer.platform_dir)
|
||||
elif self.platform_update:
|
||||
cmd('git clean -dxf', cwd=ios_dir)
|
||||
cmd('git pull origin master', cwd=ios_dir)
|
||||
|
||||
def compile_platform(self):
|
||||
self.buildozer.cmd('tools/build-all.sh', cwd=self.ios_dir)
|
||||
|
||||
def build_package(self):
|
||||
# create the project
|
||||
app_name = self.buildozer.namify(self.config.get('app', 'title'))
|
||||
|
||||
self.app_project_dir = join(self.ios_dir, 'app-{0}'.format(app_name))
|
||||
self.buildozer.cmd('tools/create-xcode-project.sh {0} {1}'.format(
|
||||
app_name, self.buildozer.app_dir),
|
||||
cwd=self.ios_dir)
|
||||
|
||||
|
||||
def get_target(buildozer):
|
||||
return TargetIos(buildozer)
|
18
setup.py
Normal file
18
setup.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
from distutils.core import setup
|
||||
|
||||
import buildozer
|
||||
|
||||
setup(
|
||||
name='buildozer',
|
||||
version=buildozer.__version__,
|
||||
author='Mathieu Virbel',
|
||||
author_email='mat@kivy.org',
|
||||
url='http://github.com/kivy/buildozer',
|
||||
license='MIT',
|
||||
packages=[
|
||||
'buildozer',
|
||||
'buildozer.targets'],
|
||||
package_data={'buildozer': ['default.spec']},
|
||||
scripts=['tools/buildozer'],
|
||||
description='Generic Python packager for Android / iOS and Desktop'
|
||||
)
|
5
tools/buildozer
Executable file
5
tools/buildozer
Executable file
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env python2.7
|
||||
|
||||
if __name__ == '__main__':
|
||||
from buildozer import run
|
||||
run()
|
Loading…
Reference in a new issue