Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions doc/source/distutils.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@

distutils/setuptools integration
================================

Instead of running p4a via the command line, you can integrate with
distutils and setup.py.

The base command is::

python setup.py apk

The files included in the APK will be all those specified in the
``package_data`` argument to setup. For instance, the following
example will include all .py and .png files in the ``testapp``
folder::

from distutils.core import setup
from setup

setup(
name='testapp_setup',
version='1.1',
description='p4a setup.py example',
author='Your Name',
author_email='youremail@address.com',
packages=find_packages(),
options=options,
package_data={'testapp': ['*.py', '*.png']}
)

The app name and version will also be read automatically from the
setup.py.

The Android package name uses ``org.test.lowercaseappname``
if not set explicitly.

The ``--private`` argument is set automatically using the
package_data, you should *not* set this manually.

The target architecture defaults to ``--armeabi``.

All of these automatic arguments can be overridden by passing them manually on the command line, e.g.::

python setup.py apk --name="Testapp Setup" --version=2.5

Adding p4a arguments in setup.py
--------------------------------

Instead of providing extra arguments on the command line, you can
store them in setup.py by passing the ``options`` parameter to
:code:`setup`. For instance::

from distutils.core import setup
from setuptools import find_packages

options = {'apk': {'debug': None, # use None for arguments that don't pass a value
'requirements': 'sdl2,pyjnius,kivy,python2',
'android-api': 19,
'ndk-dir': '/path/to/ndk',
'dist-name': 'bdisttest',
}}

packages = find_packages()
print('packages are', packages)

setup(
name='testapp_setup',
version='1.1',
description='p4a setup.py example',
author='Your Name',
author_email='youremail@address.com',
packages=find_packages(),
options=options,
package_data={'testapp': ['*.py', '*.png']}
)

These options will be automatically included when you run ``python
setup.py apk``. Any options passed on the command line will override
these values.

Adding p4a arguments in setup.cfg
---------------------------------

You can also provide p4a arguments in the setup.cfg file, as normal
for distutils. The syntax is::

[apk]

argument=value

requirements=sdl2,kivy
1 change: 1 addition & 0 deletions doc/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Contents
quickstart
buildoptions
commands
distutils
recipes
bootstraps
services
Expand Down
16 changes: 0 additions & 16 deletions pythonforandroid/bdist_apk.py

This file was deleted.

140 changes: 140 additions & 0 deletions pythonforandroid/bdistapk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
from __future__ import print_function
from setuptools import Command
from pythonforandroid import toolchain

import sys
from os.path import realpath, join, exists, dirname, curdir, basename, split
from os import makedirs
from glob import glob
from shutil import rmtree, copyfile


def argv_contains(t):
for arg in sys.argv:
if arg.startswith(t):
return True
return False


class BdistAPK(Command):
description = 'Create an APK with python-for-android'

user_options = []

def initialize_options(self):
for option in self.user_options:
setattr(self, option[0].strip('=').replace('-', '_'), None)

option_dict = self.distribution.get_option_dict('apk')

# This is a hack, we probably aren't supposed to loop through
# the option_dict so early because distutils does exactly the
# same thing later to check that we support the
# options. However, it works...
for (option, (source, value)) in option_dict.items():
setattr(self, option, str(value))


def finalize_options(self):

setup_options = self.distribution.get_option_dict('apk')
for (option, (source, value)) in setup_options.items():
if source == 'command line':
continue
if not argv_contains('--' + option):
if value in (None, 'None'):
sys.argv.append('--{}'.format(option))
else:
sys.argv.append('--{}={}'.format(option, value))

# Inject some argv options from setup.py if the user did not
# provide them
if not argv_contains('--name'):
name = self.distribution.get_name()
sys.argv.append('--name={}'.format(name))
self.name = name

if not argv_contains('--package'):
package = 'org.test.{}'.format(self.name.lower().replace(' ', ''))
print('WARNING: You did not supply an Android package '
'identifier, trying {} instead.'.format(package))
print(' This may fail if this is not a valid identifier')
sys.argv.append('--package={}'.format(package))

if not argv_contains('--version'):
version = self.distribution.get_version()
sys.argv.append('--version={}'.format(version))

if not argv_contains('--arch'):
arch = 'armeabi'
self.arch = arch
sys.argv.append('--arch={}'.format(arch))

def run(self):

self.prepare_build_dir()

from pythonforandroid.toolchain import main
sys.argv[1] = 'apk'
main()

def prepare_build_dir(self):

if argv_contains('--private'):
print('WARNING: Received --private argument when this would '
'normally be generated automatically.')
print(' This is probably bad unless you meant to do '
'that.')

bdist_dir = 'build/bdist.android-{}'.format(self.arch)
rmtree(bdist_dir)
makedirs(bdist_dir)

globs = []
for directory, patterns in self.distribution.package_data.items():
for pattern in patterns:
globs.append(join(directory, pattern))

filens = []
for pattern in globs:
filens.extend(glob(pattern))

main_py_dirs = []
for filen in filens:
new_dir = join(bdist_dir, dirname(filen))
if not exists(new_dir):
makedirs(new_dir)
print('Including {}'.format(filen))
copyfile(filen, join(bdist_dir, filen))
if basename(filen) in ('main.py', 'main.pyo'):
main_py_dirs.append(filen)

# This feels ridiculous, but how else to define the main.py dir?
# Maybe should just fail?
if len(main_py_dirs) == 0:
print('ERROR: Could not find main.py, so no app build dir defined')
print('You should name your app entry point main.py')
exit(1)
if len(main_py_dirs) > 1:
print('WARNING: Multiple main.py dirs found, using the shortest path')
main_py_dirs = sorted(main_py_dirs, key=lambda j: len(split(j)))

sys.argv.append('--private={}'.format(join(realpath(curdir), bdist_dir,
dirname(main_py_dirs[0]))))


def _set_user_options():
# This seems like a silly way to do things, but not sure if there's a
# better way to pass arbitrary options onwards to p4a
user_options = [('requirements=', None, None),]
for i, arg in enumerate(sys.argv):
if arg.startswith('--'):
if ('=' in arg or
(i < (len(sys.argv) - 1) and not sys.argv[i+1].startswith('-'))):
user_options.append((arg[2:].split('=')[0] + '=', None, None))
else:
user_options.append((arg[2:], None, None))

BdistAPK.user_options = user_options

_set_user_options()
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def recursively_include(results, directory, patterns):
'p4a = pythonforandroid.toolchain:main',
],
'distutils.commands': [
'bdist_apk = pythonforandroid.bdist_apk:BdistAPK',
'apk = pythonforandroid.bdistapk:BdistAPK',
],
},
classifiers = [
Expand Down
Empty file.
29 changes: 29 additions & 0 deletions testapps/testapp_setup/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

from distutils.core import setup
from setuptools import find_packages

options = {'apk': {'debug': None,
'requirements': 'sdl2,pyjnius,kivy,python2',
'android-api': 19,
'ndk-dir': '/home/asandy/android/crystax-ndk-10.3.1',
'dist-name': 'bdisttest',
'ndk-version': '10.3.1',
}}

package_data = {'': ['*.py',
'*.png']
}

packages = find_packages()
print('packages are', packages)

setup(
name='testapp_setup',
version='1.1',
description='p4a setup.py test',
author='Alexander Taylor',
author_email='alexanderjohntaylor@gmail.com',
packages=find_packages(),
options=options,
package_data={'testapp': ['*.py', '*.png']}
)
Binary file added testapps/testapp_setup/testapp/colours.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading