Upgrade p4a #19

Open
jessopb wants to merge 36 commits from upgrade_p4a into master
3 changed files with 174 additions and 42 deletions
Showing only changes of commit 5a103bfbee - Show all commits

View file

@ -237,7 +237,7 @@ main.py that loads it.''')
# Delete the old assets. # Delete the old assets.
shutil.rmtree(assets_dir, ignore_errors=True) shutil.rmtree(assets_dir, ignore_errors=True)
ensure_dir(assets_dir) ensure_dir(assets_dir)
# remove make_python_zip()
# Add extra environment variable file into tar-able directory: # Add extra environment variable file into tar-able directory:
env_vars_tarpath = tempfile.mkdtemp(prefix="p4a-extra-env-") env_vars_tarpath = tempfile.mkdtemp(prefix="p4a-extra-env-")
with open(os.path.join(env_vars_tarpath, "p4a_env_vars.txt"), "w") as f: with open(os.path.join(env_vars_tarpath, "p4a_env_vars.txt"), "w") as f:
@ -251,7 +251,7 @@ main.py that loads it.''')
# Package up the private data (public not supported). # Package up the private data (public not supported).
use_setup_py = get_dist_info_for("use_setup_py", use_setup_py = get_dist_info_for("use_setup_py",
error_if_missing=False) is True error_if_missing=False) is True
private_tar_dirs = [env_vars_tarpath] tar_dirs = [env_vars_tarpath]
_temp_dirs_to_clean = [] _temp_dirs_to_clean = []
try: try:
if args.private: if args.private:
@ -261,13 +261,22 @@ main.py that loads it.''')
): ):
print('No setup.py/pyproject.toml used, copying ' print('No setup.py/pyproject.toml used, copying '
'full private data into .apk.') 'full private data into .apk.')
private_tar_dirs.append(args.private) tar_dirs.append(args.private)
else: else:
print("Copying main.py's ONLY, since other app data is " print("Copying main.py's ONLY, since other app data is "
"expected in site-packages.") "expected in site-packages.")
main_py_only_dir = tempfile.mkdtemp() main_py_only_dir = tempfile.mkdtemp()
_temp_dirs_to_clean.append(main_py_only_dir) _temp_dirs_to_clean.append(main_py_only_dir)
# skip this:
# if exists(join(args.private, "main.pyo")):
# shutil.copyfile(join(args.private, "main.pyo"),
# join(main_py_only_dir, "main.pyo"))
# elif exists(join(args.private, "main.py")):
# shutil.copyfile(join(args.private, "main.py"),
# join(main_py_only_dir, "main.py"))
# tar_dirs.append(main_py_only_dir)
# Check all main.py files we need to copy: # Check all main.py files we need to copy:
copy_paths = ["main.py", join("service", "main.py")] copy_paths = ["main.py", join("service", "main.py")]
for copy_path in copy_paths: for copy_path in copy_paths:
@ -292,10 +301,10 @@ main.py that loads it.''')
) )
# Append directory with all main.py's to result apk paths: # Append directory with all main.py's to result apk paths:
private_tar_dirs.append(main_py_only_dir) tar_dirs.append(main_py_only_dir)
if get_bootstrap_name() == "webview": # if get_bootstrap_name() == "webview":
for asset in listdir('webview_includes'): # for asset in listdir('webview_includes'):
shutil.copy(join('webview_includes', asset), join(assets_dir, asset)) # shutil.copy(join('webview_includes', asset), join(assets_dir, asset))
for asset in args.assets: for asset in args.assets:
asset_src, asset_dest = asset.split(":") asset_src, asset_dest = asset.split(":")
@ -316,7 +325,7 @@ main.py that loads it.''')
) )
make_tar( make_tar(
join(assets_dir, "private.tar"), join(assets_dir, "private.tar"),
private_tar_dirs, tar_dirs,
byte_compile_python=args.byte_compile_python, byte_compile_python=args.byte_compile_python,
optimize_python=args.optimize_python, optimize_python=args.optimize_python,
) )

View file

@ -10,6 +10,7 @@ import tarfile
import time import time
import subprocess import subprocess
import shutil import shutil
import json
from zipfile import ZipFile from zipfile import ZipFile
import sys import sys
from distutils.version import LooseVersion from distutils.version import LooseVersion
@ -18,12 +19,58 @@ from fnmatch import fnmatch
import jinja2 import jinja2
## WHAT IS DISTINFO>JSON?
def get_dist_info_for(key):
try:
with open(join(dirname(__file__), 'dist_info.json'), 'r') as fileh:
info = json.load(fileh)
bootstrap = str(info["bootstrap"])
value = str(info[key])
except (OSError, KeyError) as e:
print("BUILD FAILURE: Couldn't extract bootstrap name " +
print("BUILD FAILURE: Couldn't extract the key `" + key + "` " +
"from dist_info.json: " + str(e))
sys.exit(1)
return bootstrap
return value
def get_hostpython():
return get_dist_info_for('hostpython')
def get_python_version():
return get_dist_info_for('python_version')
def get_bootstrap_name():
return get_dist_info_for('python_version')
def get_bootstrap_name():
# try:
# with open(join(dirname(__file__), 'dist_info.json'), 'r') as fileh:
# info = json.load(fileh)
# bootstrap = str(info["bootstrap"])
# except (OSError, KeyError) as e:
# print("BUILD FAILURE: Couldn't extract bootstrap name " +
# "from dist_info.json: " + str(e))
# sys.exit(1)
# return bootstrap
return get_dist_info_for('bootstrap')
if os.name == 'nt':
ANDROID = 'android.bat'
ANT = 'ant.bat'
else:
ANDROID = 'android'
ANT = 'ant'
curdir = dirname(__file__) curdir = dirname(__file__)
# Try to find a host version of Python that matches our ARM version. # Try to find a host version of Python that matches our ARM version.
PYTHON = join(curdir, 'python-install', 'bin', 'python.host') # PYTHON = join(curdir, 'python-install', 'bin', 'python.host')
PYTHON = get_python_version()
if not exists(PYTHON): if not exists(PYTHON):
print('Could not find hostpython, will not compile to .pyo (this is normal with python3)')
PYTHON = None PYTHON = None
BLACKLIST_PATTERNS = [ BLACKLIST_PATTERNS = [
@ -125,7 +172,6 @@ def make_python_zip():
if not exists('private'): if not exists('private'):
print('No compiled python is present to zip, skipping.') print('No compiled python is present to zip, skipping.')
print('this should only be the case if you are using the CrystaX python')
return return
global python_files global python_files
@ -158,7 +204,7 @@ def make_python_zip():
zf.close() zf.close()
def make_tar(tfn, source_dirs, ignore_path=[]): def make_tar(tfn, source_dirs, ignore_path=[], optimize_python=True):
''' '''
Make a zip file `fn` from the contents of source_dis. Make a zip file `fn` from the contents of source_dis.
''' '''
@ -179,7 +225,7 @@ def make_tar(tfn, source_dirs, ignore_path=[]):
files = [] files = []
for sd in source_dirs: for sd in source_dirs:
sd = realpath(sd) sd = realpath(sd)
compile_dir(sd) compile_dir(sd, optimize_python=optimize_python)
files += [(x, relpath(realpath(x), sd)) for x in listfiles(sd) files += [(x, relpath(realpath(x), sd)) for x in listfiles(sd)
if select(x)] if select(x)]
@ -208,18 +254,27 @@ def make_tar(tfn, source_dirs, ignore_path=[]):
tf.close() tf.close()
def compile_dir(dfn): def compile_dir(dfn, optimize_python=True):
''' '''
Compile *.py in directory `dfn` to *.pyo Compile *.py in directory `dfn` to *.pyo
''' '''
# -OO = strip docstrings # -OO = strip docstrings
if PYTHON is None: if PYTHON is None:
return return
subprocess.call([PYTHON, '-OO', '-m', 'compileall', '-f', dfn]) args = [PYTHON, '-m', 'compileall', '-b', '-f', dfn]
if optimize_python:
# -OO = strip docstrings
args.insert(1, '-OO')
return_code = subprocess.call(args)
if return_code != 0:
print('Error while running "{}"'.format(' '.join(args)))
print('This probably means one of your Python files has a syntax '
'error, see logs above')
exit(1)
def make_package(args): def make_package(args):
# Ignore warning if the launcher is in args # Ignore warning if the launcher is in args
# since boostrap is 'lbry', removed get_bootstrap_name checks
if not args.launcher: if not args.launcher:
if not (exists(join(realpath(args.private), 'main.py')) or if not (exists(join(realpath(args.private), 'main.py')) or
exists(join(realpath(args.private), 'main.pyo'))): exists(join(realpath(args.private), 'main.pyo'))):
@ -229,20 +284,23 @@ started by a file with a different name, rename it to main.py or add a
main.py that loads it.''') main.py that loads it.''')
exit(1) exit(1)
assets_dir = "src/main/assets"
# Delete the old assets. # Delete the old assets.
try_unlink('src/main/assets/public.mp3') try_unlink(join(assets_dir, 'public.mp3'))
try_unlink('src/main/assets/private.mp3') try_unlink(join(assets_dir, 'private.mp3'))
ensure_dir(assets_dir)
# In order to speedup import and initial depack, # In order to speedup import and initial depack,
# construct a python27.zip # construct a python27.zip
# still here as of 12/18/18
make_python_zip() make_python_zip()
# Package up the private data (public not supported). # Package up the private data (public not supported).
tar_dirs = [args.private] tar_dirs = [args.private]
if exists('private'): for python_bundle_dir in ('private', 'crystax_python', '_python_bundle'):
tar_dirs.append('private') if exists(python_bundle_dir):
if exists('crystax_python'): tar_dirs.append(python_bundle_dir)
tar_dirs.append('crystax_python')
if args.private: if args.private:
make_tar('src/main/assets/private.mp3', tar_dirs, args.ignore_path) make_tar('src/main/assets/private.mp3', tar_dirs, args.ignore_path)
@ -255,9 +313,13 @@ main.py that loads it.''')
url_scheme = 'kivy' url_scheme = 'kivy'
# Prepare some variables for templating process # Prepare some variables for templating process
res_dir = "src/main/res"
# bootstrap is lbry so we def need these
default_icon = 'templates/lbry-icon.png' default_icon = 'templates/lbry-icon.png'
default_presplash = 'templates/kivy-presplash.jpg' default_presplash = 'templates/kivy-presplash.jpg'
shutil.copy(args.icon or default_icon, 'src/main/res/drawable/icon.png') shutil.copy(args.icon or default_icon,
join(res_dir, 'drawable/icon.png')
)
shutil.copy(args.presplash or default_presplash, shutil.copy(args.presplash or default_presplash,
'src/main/res/drawable/presplash.jpg') 'src/main/res/drawable/presplash.jpg')
@ -324,9 +386,15 @@ main.py that loads it.''')
sticky = 'sticky' in options sticky = 'sticky' in options
service_names.append(name) service_names.append(name)
# 12/18/18
service_target_path = \
'src/main/java/{}/Service{}.java'.format(
args.package.replace(".", "/"),
name.capitalize()
)
render( render(
'Service.tmpl.java', 'Service.tmpl.java',
'src/main/java/{}/Service{}.java'.format(args.package.replace(".", "/"), name.capitalize()), service_target_path,
name=name, name=name,
entrypoint=entrypoint, entrypoint=entrypoint,
args=args, args=args,
@ -359,11 +427,13 @@ main.py that loads it.''')
# Copy the AndroidManifest.xml to the dist root dir so that ant # Copy the AndroidManifest.xml to the dist root dir so that ant
# can also use it # can also use it
manifest_path = "src/main/AndroidManifest.xml"
if exists('AndroidManifest.xml'): if exists('AndroidManifest.xml'):
remove('AndroidManifest.xml') remove('AndroidManifest.xml')
shutil.copy(join('src', 'main', 'AndroidManifest.xml'), shutil.copy(join('src', 'main', 'AndroidManifest.xml'),
'AndroidManifest.xml') 'AndroidManifest.xml')
# String resources
render( render(
'strings.tmpl.xml', 'strings.tmpl.xml',
'src/main/res/values/strings.xml', 'src/main/res/values/strings.xml',
@ -423,6 +493,7 @@ main.py that loads it.''')
args=args, args=args,
versioned_name=versioned_name) versioned_name=versioned_name)
if exists(join("templates", "custom_rules.tmpl.xml")):
render( render(
'custom_rules.tmpl.xml', 'custom_rules.tmpl.xml',
'custom_rules.xml', 'custom_rules.xml',
@ -435,9 +506,44 @@ main.py that loads it.''')
if exists('build.properties'): if exists('build.properties'):
os.remove('build.properties') os.remove('build.properties')
# # Apply java source patches if any are present:
# if exists(join('src', 'patches')):
# print("Applying Java source code patches...")
# for patch_name in os.listdir(join('src', 'patches')):
# patch_path = join('src', 'patches', patch_name)
# print("Applying patch: " + str(patch_path))
# try:
# subprocess.check_output([
# # -N: insist this is FORWARd patch, don't reverse apply
# # -p1: strip first path component
# # -t: batch mode, don't ask questions
# "patch", "-N", "-p1", "-t", "-i", patch_path
# ])
# except subprocess.CalledProcessError as e:
# if e.returncode == 1:
# # Return code 1 means it didn't apply, this will
# # usually mean it is already applied.
# print("Warning: failed to apply patch (" +
# "exit code 1), " +
# "assuming it is already applied: " +
# str(patch_path)
# )
# else:
# raise e
def parse_args(args=None): def parse_args(args=None):
global BLACKLIST_PATTERNS, WHITELIST_PATTERNS, PYTHON global BLACKLIST_PATTERNS, WHITELIST_PATTERNS, PYTHON
default_android_api = 12 default_android_api = 21
# Get the default minsdk, equal to the NDK API that this dist it built against
with open('dist_info.json', 'r') as fileh:
info = json.load(fileh)
if 'ndk_api' not in info:
print('Failed to read ndk_api from dist info')
default_android_api = 12 # The old default before ndk_api was introduced
else:
default_android_api = info['ndk_api']
ndk_api = info['ndk_api']
import argparse import argparse
ap = argparse.ArgumentParser(description='''\ ap = argparse.ArgumentParser(description='''\
Package a Python application for Android. Package a Python application for Android.
@ -551,8 +657,18 @@ tools directory of the Android SDK.
if args.name and args.name[0] == '"' and args.name[-1] == '"': if args.name and args.name[0] == '"' and args.name[-1] == '"':
args.name = args.name[1:-1] args.name = args.name[1:-1]
# if args.sdk_version == -1: if ndk_api != args.min_sdk_version:
# args.sdk_version = args.min_sdk_version print(('WARNING: --minsdk argument does not match the api that is '
'compiled against. Only proceed if you know what you are '
'doing, otherwise use --minsdk={} or recompile against api '
'{}').format(ndk_api, args.min_sdk_version))
if not args.allow_minsdk_ndkapi_mismatch:
print('You must pass --allow-minsdk-ndkapi-mismatch to build '
'with --minsdk different to the target NDK api from the '
'build step')
exit(1)
else:
print('Proceeding with --minsdk not matching build target api')
if args.sdk_version != -1: if args.sdk_version != -1:
print('WARNING: Received a --sdk argument, but this argument is ' print('WARNING: Received a --sdk argument, but this argument is '
@ -573,7 +689,7 @@ tools directory of the Android SDK.
if args.try_system_python_compile: if args.try_system_python_compile:
# Hardcoding python2.7 is okay for now, as python3 skips the # Hardcoding python2.7 is okay for now, as python3 skips the
# compilation anyway # compilation anyway
if not exists('crystax_python'):
python_executable = 'python2.7' python_executable = 'python2.7'
try: try:
subprocess.call([python_executable, '--version']) subprocess.call([python_executable, '--version'])
@ -598,6 +714,9 @@ tools directory of the Android SDK.
if x.strip() and not x.strip().startswith('#')] if x.strip() and not x.strip().startswith('#')]
WHITELIST_PATTERNS += patterns WHITELIST_PATTERNS += patterns
if args.private is None:
print('Need --private directory with app files to package for .apk')
exit(1)
make_package(args) make_package(args)
return args return args

View file

@ -23,7 +23,7 @@ else:
# don't use sh after 1.12.5, we have performance issues # don't use sh after 1.12.5, we have performance issues
# https://github.com/amoffat/sh/issues/378 # https://github.com/amoffat/sh/issues/378
install_reqs = ['appdirs', 'colorama>=0.3.3', 'sh>=1.10,<1.12.5', 'jinja2', install_reqs = ['appdirs', 'colorama>=0.3.3', 'sh>=1.10,<1.12.5', 'jinja2',
'six'] 'six', 'enum34']
# By specifying every file manually, package_data will be able to # By specifying every file manually, package_data will be able to
# include them in binary distributions. Note that we have to add # include them in binary distributions. Note that we have to add
@ -54,13 +54,17 @@ recursively_include(package_data, 'pythonforandroid/bootstraps/webview',
recursively_include(package_data, 'pythonforandroid', recursively_include(package_data, 'pythonforandroid',
['liblink', 'biglink', 'liblink.sh']) ['liblink', 'biglink', 'liblink.sh'])
with open(join(dirname(__file__), 'README.rst')) as fileh: with open(join(dirname(__file__), 'README.md',
encoding="utf-8",
errors="replace")) as fileh:
long_description = fileh.read() long_description = fileh.read()
init_filen = join(dirname(__file__), 'pythonforandroid', '__init__.py') init_filen = join(dirname(__file__), 'pythonforandroid', '__init__.py')
version = None version = None
try: try:
with open(init_filen) as fileh: with open(init_filen,
encoding="utf-8",
errors="replace") as fileh:
lines = fileh.readlines() lines = fileh.readlines()
except IOError: except IOError:
pass pass