Skip to content
Open
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
10 changes: 10 additions & 0 deletions ci/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ class TargetPython(Enum):
'vlc',
# need extra gfortran NDK system add-on
'lapack', 'scipy',
# 403 on https://jqueryui.com/resources/download/jquery-ui-1.12.1.zip
'matplotlib',
# requires kernel headers
'evdev',
# xslt-config: not found (missing dev packages of libxml2 and libxslt)
'lxml',
# The headers or library files could not be found for zlib
'Pillow',
# OpenCV requires Android SDK Tools revision 14 or newer
'opencv',
])

BROKEN_RECIPES = {
Expand Down
2 changes: 1 addition & 1 deletion pythonforandroid/archs.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class Arch:
common_cppflags = [
'-DANDROID',
'-D__ANDROID_API__={ctx.ndk_api}',
'-I{ctx.ndk_dir}/sysroot/usr/include/{command_prefix}',
'-I{ctx.ndk_sysroot}/usr/include/{command_prefix}',
'-I{python_includes}',
]

Expand Down
84 changes: 80 additions & 4 deletions pythonforandroid/bootstraps/common/build/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import tarfile
import tempfile
import time
import glob
import sh

from distutils.version import LooseVersion
from fnmatch import fnmatch
Expand Down Expand Up @@ -79,7 +81,7 @@ def get_bootstrap_name():
BLACKLIST_PATTERNS.append('*.py')

WHITELIST_PATTERNS = []
if get_bootstrap_name() in ('sdl2', 'webview', 'service_only'):
if get_bootstrap_name() in ('sdl2', 'webview', 'qt5', 'service_only'):
WHITELIST_PATTERNS.append('pyconfig.h')

python_files = []
Expand Down Expand Up @@ -233,6 +235,50 @@ def compile_dir(dfn, optimize_python=True):
'error, see logs above')
exit(1)

def make_qml_rcc(assets_dir):
def should_include_in_qrc(fname):
if os.path.isdir(fname):
return False
basename = os.path.basename(fname)
if basename in ('Makefile', ):
return False
ext = os.path.splitext(basename)[1]
if ext in ('.so', '.h', '.cpp'):
return False
return True

# hardcoded for now, should be made automatic/configurable
components = ['qtdeclarative', 'qtquickcontrols2', 'qtmultimedia']
qt5_path = join('jni', 'qt5')
with open('android_rcc_bundle.qrc', 'w') as qrc_file:
qrc_file.write('<!DOCTYPE RCC><RCC version="1.0"><qresource>')

for qmlcomp in components:
qmlfiles = glob.glob(join(qt5_path, qmlcomp, 'qml', '**'), recursive=True)
qmlfiles.sort()
for qmlfile in qmlfiles:
if should_include_in_qrc(qmlfile):
alias = qmlfile.replace(join(qt5_path, qmlcomp), '')[1:]
print(alias + ':' + qmlfile)
qrc_file.write(f'<file alias="{alias}">{qmlfile}</file>')

# dirty hack to include material style files in resource file
# these should be available from the material style plugin
# but somehow this doesn't work (TODO)
basepath = join(qt5_path, 'qtquickcontrols2', 'src', 'imports', 'controls', 'material')
qmlfiles = glob.glob(join(basepath, '**'), recursive=True)
qmlfiles.sort()
for qmlfile in qmlfiles:
if should_include_in_qrc(qmlfile):
alias = qmlfile.replace(basepath, 'qml/QtQuick/Controls.2/Material')
print(alias + ':' + qmlfile)
qrc_file.write(f'<file alias="{alias}">{qmlfile}</file>')

qrc_file.write('</qresource></RCC>')

rcc = sh.Command(join(qt5_path, 'qtbase', 'bin', 'rcc'))
rcc('--root', '/android_rcc_bundle/', '--binary', '-o',
join(assets_dir, 'android_rcc_bundle.rcc'), 'android_rcc_bundle.qrc')

def make_package(args):
# If no launcher is specified, require a main.py/main.pyo:
Expand Down Expand Up @@ -335,14 +381,31 @@ def make_package(args):
# Remove extra env vars tar-able directory:
shutil.rmtree(env_vars_tarpath)

if get_bootstrap_name() == "qt5":
print("Generating QML resource file")
make_qml_rcc(assets_dir)

# Prepare some variables for templating process
res_dir = "src/main/res"
default_icon = 'templates/kivy-icon.png'
default_presplash = 'templates/kivy-presplash.jpg'
shutil.copy(
args.icon or default_icon,
join(res_dir, 'drawable/icon.png')
join(res_dir, 'mipmap/icon.png')
)
if args.icon_fg and args.icon_bg:
shutil.copy(args.icon_fg, join(res_dir, 'mipmap/icon_foreground.png'))
shutil.copy(args.icon_bg, join(res_dir, 'mipmap/icon_background.png'))
with open(join(res_dir, 'mipmap-anydpi-v26/icon.xml'), "w") as fd:
fd.write("""<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/icon_background"/>
<foreground android:drawable="@mipmap/icon_foreground"/>
</adaptive-icon>
""")
elif args.icon_fg or args.icon_bg:
print("WARNING: Received an --icon_fg or an --icon_bg argument, but not both. "
"Ignoring.")

if args.enable_androidx:
shutil.copy('templates/gradle.properties', 'gradle.properties')
Expand Down Expand Up @@ -397,13 +460,13 @@ def make_package(args):
if not args.numeric_version:
# Set version code in format (arch-minsdk-app_version)
arch = get_dist_info_for("archs")[0]
arch_dict = {"x86_64": "9", "arm64-v8a": "8", "armeabi-v7a": "7", "x86": "6"}
arch_dict = {"x86_64": "4", "arm64-v8a": "3", "armeabi-v7a": "2", "x86": "1"}
arch_code = arch_dict.get(arch, '1')
min_sdk = args.min_sdk_version
for i in args.version.split('.'):
version_code *= 100
version_code += int(i)
args.numeric_version = "{}{}{}".format(arch_code, min_sdk, version_code)
args.numeric_version = "{}{}".format(arch_code, version_code)

if args.intent_filters:
with open(args.intent_filters) as fd:
Expand Down Expand Up @@ -563,6 +626,13 @@ def make_package(args):
join(res_dir, 'values/strings.xml'),
**render_args)

if get_bootstrap_name() == "qt5":
render(
'arrays.tmpl.xml',
join(res_dir, 'values', 'arrays.xml'),
arch=get_dist_info_for("archs")[0],
python_lib= "python%s" % get_python_version() )

if exists(join("templates", "custom_rules.tmpl.xml")):
render(
'custom_rules.tmpl.xml',
Expand Down Expand Up @@ -672,6 +742,12 @@ def parse_args_and_make_package(args=None):
ap.add_argument('--icon', dest='icon',
help=('A png file to use as the icon for '
'the application.'))
ap.add_argument('--icon-fg', dest='icon_fg',
help=('A png file to use as the foreground of the adaptive icon '
'for the application.'))
ap.add_argument('--icon-bg', dest='icon_bg',
help=('A png file to use as the background of the adaptive icon '
'for the application.'))
ap.add_argument('--service', dest='services', action='append', default=[],
help='Declare a new service entrypoint: '
'NAME:PATH_TO_PY[:foreground]')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ allprojects {
}
}

tasks.withType(AbstractArchiveTask) {
preserveFileTimestamps = false
reproducibleFileOrder = true
}

{% if is_library %}
apply plugin: 'com.android.library'
{% else %}
Expand All @@ -45,17 +50,24 @@ android {
}
{%- endif %}

{% if args.sign -%}
signingConfigs {
release {
storeFile file(System.getenv("P4A_RELEASE_KEYSTORE"))
keyAlias System.getenv("P4A_RELEASE_KEYALIAS")
storePassword System.getenv("P4A_RELEASE_KEYSTORE_PASSWD")
keyPassword System.getenv("P4A_RELEASE_KEYALIAS_PASSWD")
}
}

{%- endif %}
signingConfigs {
{% if debug_build -%}
debug {
storeFile file(System.getenv("P4A_DEBUG_KEYSTORE"))
keyAlias System.getenv("P4A_DEBUG_KEYALIAS")
storePassword System.getenv("P4A_DEBUG_KEYSTORE_PASSWD")
keyPassword System.getenv("P4A_DEBUG_KEYALIAS_PASSWD")
}
{%- endif %}
{% if args.sign -%}
release {
storeFile file(System.getenv("P4A_RELEASE_KEYSTORE"))
keyAlias System.getenv("P4A_RELEASE_KEYALIAS")
storePassword System.getenv("P4A_RELEASE_KEYSTORE_PASSWD")
keyPassword System.getenv("P4A_RELEASE_KEYALIAS_PASSWD")
}
{%- endif %}
}

{% if args.packaging_options -%}
packagingOptions {
Expand All @@ -67,6 +79,9 @@ android {

buildTypes {
debug {
{% if debug_build -%}
signingConfig signingConfigs.debug
{%- endif %}
}
release {
{% if args.sign -%}
Expand Down Expand Up @@ -101,6 +116,11 @@ android {
}
}

// Do not compress Qt binary resources file
aaptOptions {
noCompress 'rcc'
}

}

dependencies {
Expand Down
63 changes: 63 additions & 0 deletions pythonforandroid/bootstraps/qt5/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from pythonforandroid.toolchain import Bootstrap, current_directory, info, info_main, shprint
from pythonforandroid.util import ensure_dir
from os.path import join
import sh
import glob

class Qt5Bootstrap(Bootstrap):
name = 'qt5'

recipe_depends = list(
set(Bootstrap.recipe_depends).union({'qt5'})
)

def distribute_aidl(self, aidl_dir, dest_dir="src"):
'''Copy existing javaclasses from build dir to current dist dir.'''
info('Copying aidl files')
filenames = glob.glob(join(aidl_dir,'*'))
if len(filenames) > 0:
ensure_dir(dest_dir)
shprint(sh.cp, '-a', *filenames, dest_dir)

def assemble_distribution(self):
info_main('# Creating Android project from build and {} bootstrap'.format(
self.name))

info('This currently just copies the build stuff straight from the build dir.')
shprint(sh.rm, '-rf', self.dist_dir)
shprint(sh.cp, '-r', self.build_dir, self.dist_dir)
with current_directory(self.dist_dir):
with open('local.properties', 'w') as fileh:
fileh.write('sdk.dir={}'.format(self.ctx.sdk_dir))

arch = self.ctx.archs[0]
if len(self.ctx.archs) > 1:
raise ValueError('built for more than one arch, but bootstrap cannot handle that yet')
info('Bootstrap running with arch {}'.format(arch))

with current_directory(self.dist_dir):
info('Copying python distribution')

self.distribute_libs(arch, [self.ctx.get_libs_dir(arch.arch)])
self.distribute_aars(arch)
self.distribute_javaclasses(self.ctx.javaclass_dir,
dest_dir=join("src", "main", "java"))
self.distribute_aidl(self.ctx.aidl_dir,
dest_dir=join("src", "main", "aidl"))

python_bundle_dir = join('_python_bundle', '_python_bundle')
ensure_dir(python_bundle_dir)
site_packages_dir = self.ctx.python_recipe.create_python_bundle(
join(self.dist_dir, python_bundle_dir), arch)

if 'sqlite3' not in self.ctx.recipe_build_order:
with open('blacklist.txt', 'a') as fileh:
fileh.write('\nsqlite3/*\nlib-dynload/_sqlite3.so\n')

if not self.ctx.with_debug_symbols:
self.strip_libraries(arch)
self.fry_eggs(site_packages_dir)
super().assemble_distribution()


bootstrap = Qt5Bootstrap()
82 changes: 82 additions & 0 deletions pythonforandroid/bootstraps/qt5/build/blacklist.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# prevent user to include invalid extensions
*.apk
*.pxd

# eggs
*.egg-info

# unit test
unittest/*

# python config
config/makesetup

# unused kivy files (platform specific)
kivy/input/providers/wm_*
kivy/input/providers/mactouch*
kivy/input/providers/probesysfs*
kivy/input/providers/mtdev*
kivy/input/providers/hidinput*
kivy/core/camera/camera_videocapture*
kivy/core/spelling/*osx*
kivy/core/video/video_pyglet*
kivy/tools
kivy/tests/*
kivy/*/*.h
kivy/*/*.pxi

# unused encodings
lib-dynload/*codec*
encodings/cp*.pyo
encodings/tis*
encodings/shift*
encodings/bz2*
encodings/iso*
encodings/undefined*
encodings/johab*
encodings/p*
encodings/m*
encodings/euc*
encodings/k*
encodings/unicode_internal*
encodings/quo*
encodings/gb*
encodings/big5*
encodings/hp*
encodings/hz*

# unused python modules
bsddb/*
wsgiref/*
hotshot/*
pydoc_data/*
tty.pyo
anydbm.pyo
nturl2path.pyo
LICENCE.txt
macurl2path.pyo
dummy_threading.pyo
audiodev.pyo
antigravity.pyo
dumbdbm.pyo
sndhdr.pyo
__phello__.foo.pyo
sunaudio.pyo
os2emxpath.pyo
multiprocessing/dummy*

# unused binaries python modules
lib-dynload/termios.so
lib-dynload/_lsprof.so
lib-dynload/*audioop.so
lib-dynload/_hotshot.so
lib-dynload/_heapq.so
lib-dynload/_json.so
lib-dynload/grp.so
lib-dynload/resource.so
lib-dynload/pyexpat.so
lib-dynload/_ctypes_test.so
lib-dynload/_testcapi.so

# odd files
plat-linux3/regen
1 change: 1 addition & 0 deletions pythonforandroid/bootstraps/qt5/build/jni/Android.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include $(call all-subdir-makefiles)
7 changes: 7 additions & 0 deletions pythonforandroid/bootstraps/qt5/build/jni/Application.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

# Uncomment this if you're using STL in your project
# See CPLUSPLUS-SUPPORT.html in the NDK documentation for more information
# APP_STL := stlport_static

# APP_ABI := armeabi armeabi-v7a x86
APP_ABI := $(ARCH)
Loading