Skip to content

Commit 4f746d1

Browse files
committed
Initial support for PySide6
- Add a new bootstrap for Qt - This bootstrap will be used by `pyside6-android-deploy` tool shipped with PySide6, which interally calls pythonforandroid using buildozer. - The Qt bootstrap depends on recipes PySide6 and shiboken6 among other mandatory recipes. The recipes for PySide6 and shiboken6 resides in the PySide repository - https://code.qt.io/cgit/pyside/pyside-setup.git/tree/sources/pyside-tools/deploy_lib/android/recipes - The PythonActivity entrypoint class is derived from QtActivity class which is the main acitivty class when a Qt C++ application is packaged for Android. The jar containing QtActivity class is supplied through buildozer `android.add_jars` option. - The C wrapper binary to the application main.py is named as `main_{abi_name}` instead of just `main` for other bootstraps. - Multi architecture deployment is not supported at the moment. - Adapt tests based on the new Qt bootstrap
1 parent 0df46c8 commit 4f746d1

File tree

22 files changed

+613
-5
lines changed

22 files changed

+613
-5
lines changed

pythonforandroid/bootstraps/common/build/build.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def get_bootstrap_name():
8383
if PYTHON is not None and not exists(PYTHON):
8484
PYTHON = None
8585

86-
if _bootstrap_name in ('sdl2', 'webview', 'service_only'):
86+
if _bootstrap_name in ('sdl2', 'webview', 'service_only', 'qt'):
8787
WHITELIST_PATTERNS.append('pyconfig.h')
8888

8989
environment = jinja2.Environment(loader=jinja2.FileSystemLoader(
@@ -222,7 +222,7 @@ def compile_py_file(python_file, optimize_python=True):
222222

223223
def make_package(args):
224224
# If no launcher is specified, require a main.py/main.pyc:
225-
if (get_bootstrap_name() != "sdl" or args.launcher is None) and \
225+
if (get_bootstrap_name() not in ["sdl", "qt"] or args.launcher is None) and \
226226
get_bootstrap_name() not in ["webview", "service_library"]:
227227
# (webview doesn't need an entrypoint, apparently)
228228
if args.private is None or (
@@ -543,6 +543,7 @@ def make_package(args):
543543
}
544544
if get_bootstrap_name() == "sdl2":
545545
render_args["url_scheme"] = url_scheme
546+
546547
render(
547548
'AndroidManifest.tmpl.xml',
548549
manifest_path,
@@ -601,6 +602,24 @@ def make_package(args):
601602
join(res_dir, 'values/strings.xml'),
602603
**render_args)
603604

605+
# Library resources from Qt
606+
# These are referred by QtLoader.java in Qt6AndroidBindings.jar
607+
# qt_libs and load_local_libs are loaded at App startup
608+
if get_bootstrap_name() == "qt":
609+
qt_libs = args.qt_libs.split(",")
610+
load_local_libs = args.load_local_libs.split(",")
611+
init_classes = args.init_classes.split(",")
612+
init_classes = ":".join(init_classes)
613+
arch = get_dist_info_for("archs")[0]
614+
render(
615+
'libs.tmpl.xml',
616+
join(res_dir, 'values/libs.xml'),
617+
qt_libs=qt_libs,
618+
load_local_libs=load_local_libs,
619+
init_classes=init_classes,
620+
arch=arch
621+
)
622+
604623
if exists(join("templates", "custom_rules.tmpl.xml")):
605624
render(
606625
'custom_rules.tmpl.xml',
@@ -951,6 +970,14 @@ def create_argument_parser():
951970
help='Use that parameter if you need to implement your own PythonServive Java class')
952971
ap.add_argument('--activity-class-name', dest='activity_class_name', default=DEFAULT_PYTHON_ACTIVITY_JAVA_CLASS,
953972
help='The full java class name of the main activity')
973+
if get_bootstrap_name() == "qt":
974+
ap.add_argument('--qt-libs', dest='qt_libs', required=True,
975+
help='comma separated list of Qt libraries to be loaded')
976+
ap.add_argument('--load-local-libs', dest='load_local_libs', required=True,
977+
help='comma separated list of Qt plugin libraries to be loaded')
978+
ap.add_argument('--init-classes', dest='init_classes',
979+
help='comma separated list of java class names to be loaded from the Qt jar files, '
980+
'specified through add_jar cli option')
954981

955982
return ap
956983

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import sh
2+
from os.path import join
3+
from pythonforandroid.toolchain import (
4+
Bootstrap, current_directory, info, info_main, shprint)
5+
from pythonforandroid.util import ensure_dir, rmdir
6+
7+
8+
class QtBootstrap(Bootstrap):
9+
name = 'qt'
10+
recipe_depends = ['python3', 'genericndkbuild', 'PySide6', 'shiboken6']
11+
# this is needed because the recipes PySide6 and shiboken6 resides in the PySide Qt repository
12+
# - https://code.qt.io/cgit/pyside/pyside-setup.git/
13+
# Without this some tests will error because it cannot find the recipes within pythonforandroid
14+
# repository
15+
can_be_chosen_automatically = False
16+
17+
def assemble_distribution(self):
18+
info_main("# Creating Android project using Qt bootstrap")
19+
20+
rmdir(self.dist_dir)
21+
info("Copying gradle build")
22+
shprint(sh.cp, '-r', self.build_dir, self.dist_dir)
23+
24+
with current_directory(self.dist_dir):
25+
with open('local.properties', 'w') as fileh:
26+
fileh.write('sdk.dir={}'.format(self.ctx.sdk_dir))
27+
28+
arch = self.ctx.archs[0]
29+
if len(self.ctx.archs) > 1:
30+
raise ValueError("Trying to build for more than one arch. Qt bootstrap cannot handle that yet")
31+
32+
info(f"Bootstrap running with arch {arch}")
33+
34+
with current_directory(self.dist_dir):
35+
info("Copying Python distribution")
36+
37+
self.distribute_libs(arch, [self.ctx.get_libs_dir(arch.arch)])
38+
self.distribute_aars(arch)
39+
self.distribute_javaclasses(self.ctx.javaclass_dir,
40+
dest_dir=join("src", "main", "java"))
41+
42+
python_bundle_dir = join(f'_python_bundle__{arch.arch}', '_python_bundle')
43+
ensure_dir(python_bundle_dir)
44+
site_packages_dir = self.ctx.python_recipe.create_python_bundle(
45+
join(self.dist_dir, python_bundle_dir), arch)
46+
47+
if not self.ctx.with_debug_symbols:
48+
self.strip_libraries(arch)
49+
self.fry_eggs(site_packages_dir)
50+
super().assemble_distribution()
51+
52+
53+
bootstrap = QtBootstrap()
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.gradle
2+
/build/
3+
4+
# Ignore Gradle GUI config
5+
gradle-app.setting
6+
7+
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
8+
!gradle-wrapper.jar
9+
10+
# Cache of project
11+
.gradletasknamecache
12+
13+
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
14+
# gradle/wrapper/gradle-wrapper.properties
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# prevent user to include invalid extensions
2+
*.apk
3+
*.aab
4+
*.apks
5+
*.pxd
6+
7+
# eggs
8+
*.egg-info
9+
10+
# unit test
11+
unittest/*
12+
13+
# python config
14+
config/makesetup
15+
16+
# unused encodings
17+
lib-dynload/*codec*
18+
encodings/cp*.pyo
19+
encodings/tis*
20+
encodings/shift*
21+
encodings/bz2*
22+
encodings/iso*
23+
encodings/undefined*
24+
encodings/johab*
25+
encodings/p*
26+
encodings/m*
27+
encodings/euc*
28+
encodings/k*
29+
encodings/unicode_internal*
30+
encodings/quo*
31+
encodings/gb*
32+
encodings/big5*
33+
encodings/hp*
34+
encodings/hz*
35+
36+
# unused python modules
37+
bsddb/*
38+
wsgiref/*
39+
hotshot/*
40+
pydoc_data/*
41+
tty.pyo
42+
anydbm.pyo
43+
nturl2path.pyo
44+
LICENCE.txt
45+
macurl2path.pyo
46+
dummy_threading.pyo
47+
audiodev.pyo
48+
antigravity.pyo
49+
dumbdbm.pyo
50+
sndhdr.pyo
51+
__phello__.foo.pyo
52+
sunaudio.pyo
53+
os2emxpath.pyo
54+
multiprocessing/dummy*
55+
56+
# unused binaries python modules
57+
lib-dynload/termios.so
58+
lib-dynload/_lsprof.so
59+
lib-dynload/*audioop.so
60+
lib-dynload/_hotshot.so
61+
lib-dynload/_heapq.so
62+
lib-dynload/_json.so
63+
lib-dynload/grp.so
64+
lib-dynload/resource.so
65+
lib-dynload/pyexpat.so
66+
lib-dynload/_ctypes_test.so
67+
lib-dynload/_testcapi.so
68+
69+
# odd files
70+
plat-linux3/regen
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
# Uncomment this if you're using STL in your project
3+
# See CPLUSPLUS-SUPPORT.html in the NDK documentation for more information
4+
# APP_STL := stlport_static
5+
6+
# APP_ABI := armeabi armeabi-v7a x86
7+
APP_ABI := $(ARCH)
8+
APP_PLATFORM := $(NDK_API)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
LOCAL_PATH := $(call my-dir)
2+
3+
include $(CLEAR_VARS)
4+
5+
LOCAL_MODULE := main_$(PREFERRED_ABI)
6+
7+
# Add your application source files here...
8+
LOCAL_SRC_FILES := start.c
9+
10+
LOCAL_CFLAGS += -I$(PYTHON_INCLUDE_ROOT) $(EXTRA_CFLAGS)
11+
12+
LOCAL_SHARED_LIBRARIES := python_shared
13+
14+
LOCAL_LDLIBS := -llog $(EXTRA_LDLIBS)
15+
16+
LOCAL_LDFLAGS += -L$(PYTHON_LINK_ROOT) $(APPLICATION_ADDITIONAL_LDFLAGS)
17+
18+
include $(BUILD_SHARED_LIBRARY)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
LOCAL_PATH := $(call my-dir)
2+
3+
include $(CLEAR_VARS)
4+
5+
LOCAL_MODULE := main_$(PREFERRED_ABI)
6+
7+
LOCAL_SRC_FILES := start.c
8+
9+
include $(BUILD_SHARED_LIBRARY)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
#define BOOTSTRAP_USES_NO_SDL_HEADERS
3+
4+
const char bootstrap_name[] = "qt";

pythonforandroid/bootstraps/qt/build/src/main/assets/.gitkeep

Whitespace-only changes.

pythonforandroid/bootstraps/qt/build/src/main/java/.gitkeep

Whitespace-only changes.

0 commit comments

Comments
 (0)