Skip to content

Add SDL3 bootstrap (alongside SDL3, SDL3_ttf, SDL3_mixer, SDL3_image recipes) for Kivy 3.0.0 #3125

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 12, 2025
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
25 changes: 17 additions & 8 deletions pythonforandroid/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
rmdir, move)
from pythonforandroid.recipe import Recipe

SDL_BOOTSTRAPS = ("sdl2", "sdl3")


def copy_files(src_root, dest_root, override=True, symlink=False):
for root, dirnames, filenames in walk(src_root):
Expand All @@ -39,7 +41,7 @@ def copy_files(src_root, dest_root, override=True, symlink=False):


default_recipe_priorities = [
"webview", "sdl2", "service_only" # last is highest
"webview", "sdl2", "sdl3", "service_only" # last is highest
]
# ^^ NOTE: these are just the default priorities if no special rules
# apply (which you can find in the code below), so basically if no
Expand Down Expand Up @@ -150,18 +152,18 @@ def get_bootstrap_dirs(self):
return bootstrap_dirs

def _copy_in_final_files(self):
if self.name == "sdl2":
# Get the paths for copying SDL2's java source code:
sdl2_recipe = Recipe.get_recipe("sdl2", self.ctx)
sdl2_build_dir = sdl2_recipe.get_jni_dir()
src_dir = join(sdl2_build_dir, "SDL", "android-project",
if self.name in SDL_BOOTSTRAPS:
# Get the paths for copying SDL's java source code:
sdl_recipe = Recipe.get_recipe(self.name, self.ctx)
sdl_build_dir = sdl_recipe.get_jni_dir()
src_dir = join(sdl_build_dir, "SDL", "android-project",
"app", "src", "main", "java",
"org", "libsdl", "app")
target_dir = join(self.dist_dir, 'src', 'main', 'java', 'org',
'libsdl', 'app')

# Do actual copying:
info('Copying in SDL2 .java files from: ' + str(src_dir))
info('Copying in SDL .java files from: ' + str(src_dir))
if not os.path.exists(target_dir):
os.makedirs(target_dir)
copy_files(src_dir, target_dir, override=True)
Expand Down Expand Up @@ -193,7 +195,7 @@ def assemble_distribution(self):
@classmethod
def all_bootstraps(cls):
'''Find all the available bootstraps and return them.'''
forbidden_dirs = ('__pycache__', 'common')
forbidden_dirs = ('__pycache__', 'common', '_sdl_common')
bootstraps_dir = join(dirname(__file__), 'bootstraps')
result = set()
for name in listdir(bootstraps_dir):
Expand Down Expand Up @@ -272,6 +274,13 @@ def have_dependency_in_recipes(dep):
info('Using sdl2 bootstrap since it is in dependencies')
return cls.get_bootstrap("sdl2", ctx)

# Special rule: return SDL3 bootstrap if there's an sdl3 dep:
if (have_dependency_in_recipes("sdl3") and
"sdl3" in [b.name for b in acceptable_bootstraps]
):
info('Using sdl3 bootstrap since it is in dependencies')
return cls.get_bootstrap("sdl3", ctx)

# Special rule: return "webview" if we depend on common web recipe:
for possible_web_dep in known_web_packages:
if have_dependency_in_recipes(possible_web_dep):
Expand Down
49 changes: 49 additions & 0 deletions pythonforandroid/bootstraps/_sdl_common/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from os.path import join

import sh

from pythonforandroid.toolchain import (
Bootstrap, shprint, current_directory, info, info_main)
from pythonforandroid.util import ensure_dir, rmdir


class SDLGradleBootstrap(Bootstrap):
name = "_sdl_common"

recipe_depends = []

def assemble_distribution(self):
info_main("# Creating Android project ({})".format(self.name))

rmdir(self.dist_dir)
info("Copying SDL/gradle build")
shprint(sh.cp, "-r", self.build_dir, self.dist_dir)

# either the build use environment variable (ANDROID_HOME)
# or the local.properties if exists
with current_directory(self.dist_dir):
with open('local.properties', 'w') as fileh:
fileh.write('sdk.dir={}'.format(self.ctx.sdk_dir))

with current_directory(self.dist_dir):
info("Copying Python distribution")

self.distribute_javaclasses(self.ctx.javaclass_dir,
dest_dir=join("src", "main", "java"))

for arch in self.ctx.archs:
python_bundle_dir = join(f'_python_bundle__{arch.arch}', '_python_bundle')
ensure_dir(python_bundle_dir)

self.distribute_libs(arch, [self.ctx.get_libs_dir(arch.arch)])
site_packages_dir = self.ctx.python_recipe.create_python_bundle(
join(self.dist_dir, python_bundle_dir), arch)
if not self.ctx.with_debug_symbols:
self.strip_libraries(arch)
self.fry_eggs(site_packages_dir)

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

super().assemble_distribution()
22 changes: 13 additions & 9 deletions pythonforandroid/bootstraps/common/build/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from fnmatch import fnmatch
import jinja2

from pythonforandroid.bootstrap import SDL_BOOTSTRAPS
from pythonforandroid.util import rmdir, ensure_dir, max_build_tool_version


Expand Down Expand Up @@ -83,7 +84,7 @@ def get_bootstrap_name():
if PYTHON is not None and not exists(PYTHON):
PYTHON = None

if _bootstrap_name in ('sdl2', 'webview', 'service_only', 'qt'):
if _bootstrap_name in ('sdl2', 'sdl3', 'webview', 'service_only', 'qt'):
WHITELIST_PATTERNS.append('pyconfig.h')

environment = jinja2.Environment(loader=jinja2.FileSystemLoader(
Expand Down Expand Up @@ -220,6 +221,10 @@ def compile_py_file(python_file, optimize_python=True):
return ".".join([os.path.splitext(python_file)[0], "pyc"])


def is_sdl_bootstrap():
return get_bootstrap_name() in SDL_BOOTSTRAPS


def make_package(args):
# If no launcher is specified, require a main.py/main.pyc:
if (get_bootstrap_name() != "sdl" or args.launcher is None) and \
Expand Down Expand Up @@ -541,7 +546,7 @@ def make_package(args):
"debug": "debug" in args.build_mode,
"native_services": args.native_services
}
if get_bootstrap_name() == "sdl2":
if is_sdl_bootstrap():
render_args["url_scheme"] = url_scheme

render(
Expand Down Expand Up @@ -596,7 +601,7 @@ def make_package(args):
"args": args,
"private_version": hashlib.sha1(private_version.encode()).hexdigest()
}
if get_bootstrap_name() == "sdl2":
if is_sdl_bootstrap():
render_args["url_scheme"] = url_scheme
render(
'strings.tmpl.xml',
Expand Down Expand Up @@ -769,7 +774,7 @@ def create_argument_parser():
ap.add_argument('--private', dest='private',
help='the directory with the app source code files' +
' (containing your main.py entrypoint)',
required=(get_bootstrap_name() != "sdl2"))
required=(not is_sdl_bootstrap()))
ap.add_argument('--package', dest='package',
help=('The name of the java package the project will be'
' packaged under.'),
Expand All @@ -787,7 +792,7 @@ def create_argument_parser():
'same number of groups of numbers as previous '
'versions.'),
required=True)
if get_bootstrap_name() == "sdl2":
if is_sdl_bootstrap():
ap.add_argument('--launcher', dest='launcher', action='store_true',
help=('Provide this argument to build a multi-app '
'launcher, rather than a single app.'))
Expand Down Expand Up @@ -1044,7 +1049,7 @@ def _read_configuration():
args.orientation, args.manifest_orientation
)

if get_bootstrap_name() == "sdl2":
if is_sdl_bootstrap():
args.sdl_orientation_hint = get_sdl_orientation_hint(args.orientation)

if args.res_xmls and isinstance(args.res_xmls[0], list):
Expand Down Expand Up @@ -1073,10 +1078,9 @@ def _read_configuration():
if x.strip() and not x.strip().startswith('#')]
WHITELIST_PATTERNS += patterns

if args.private is None and \
get_bootstrap_name() == 'sdl2' and args.launcher is None:
if args.private is None and is_sdl_bootstrap() and args.launcher is None:
print('Need --private directory or ' +
'--launcher (SDL2 bootstrap only)' +
'--launcher (SDL2/SDL3 bootstrap only)' +
'to have something to launch inside the .apk!')
sys.exit(1)
make_package(args)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ SDL_PATH := ../../SDL
LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include

# Add your application source files here...
LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.c \
start.c
LOCAL_SRC_FILES := start.c

LOCAL_CFLAGS += -I$(PYTHON_INCLUDE_ROOT) $(EXTRA_CFLAGS)

LOCAL_SHARED_LIBRARIES := SDL2 python_shared
LOCAL_SHARED_LIBRARIES := python_shared

LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -llog $(EXTRA_LDLIBS)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,16 @@

#include "bootstrap_name.h"

#ifndef BOOTSTRAP_USES_NO_SDL_HEADERS
#ifdef BOOTSTRAP_NAME_SDL2
#include "SDL.h"
#include "SDL_opengles2.h"
#endif

#ifdef BOOTSTRAP_NAME_SDL3
#include "SDL3/SDL.h"
#include "SDL3/SDL_main.h"
#endif

#include "android/log.h"

#define ENTRYPOINT_MAXLEN 128
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ protected static ArrayList<String> getLibraries(File libsDir) {
addLibraryIfExists(libsList, "SDL2_image", libsDir);
addLibraryIfExists(libsList, "SDL2_mixer", libsDir);
addLibraryIfExists(libsList, "SDL2_ttf", libsDir);
addLibraryIfExists(libsList, "SDL3", libsDir);
addLibraryIfExists(libsList, "SDL3_image", libsDir);
addLibraryIfExists(libsList, "SDL3_mixer", libsDir);
addLibraryIfExists(libsList, "SDL3_ttf", libsDir);
libsList.add("python3.5m");
libsList.add("python3.6m");
libsList.add("python3.7m");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

#define BOOTSTRAP_USES_NO_SDL_HEADERS

const char bootstrap_name[] = "qt";
50 changes: 4 additions & 46 deletions pythonforandroid/bootstraps/sdl2/__init__.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,12 @@
from os.path import join
from pythonforandroid.bootstraps._sdl_common import SDLGradleBootstrap

import sh

from pythonforandroid.toolchain import (
Bootstrap, shprint, current_directory, info, info_main)
from pythonforandroid.util import ensure_dir, rmdir


class SDL2GradleBootstrap(Bootstrap):
name = 'sdl2'
class SDL2GradleBootstrap(SDLGradleBootstrap):
name = "sdl2"

recipe_depends = list(
set(Bootstrap.recipe_depends).union({'sdl2'})
set(SDLGradleBootstrap.recipe_depends).union({"sdl2"})
)

def assemble_distribution(self):
info_main("# Creating Android project ({})".format(self.name))

rmdir(self.dist_dir)
info("Copying SDL2/gradle build")
shprint(sh.cp, "-r", self.build_dir, self.dist_dir)

# either the build use environment variable (ANDROID_HOME)
# or the local.properties if exists
with current_directory(self.dist_dir):
with open('local.properties', 'w') as fileh:
fileh.write('sdk.dir={}'.format(self.ctx.sdk_dir))

with current_directory(self.dist_dir):
info("Copying Python distribution")

self.distribute_javaclasses(self.ctx.javaclass_dir,
dest_dir=join("src", "main", "java"))

for arch in self.ctx.archs:
python_bundle_dir = join(f'_python_bundle__{arch.arch}', '_python_bundle')
ensure_dir(python_bundle_dir)

self.distribute_libs(arch, [self.ctx.get_libs_dir(arch.arch)])
site_packages_dir = self.ctx.python_recipe.create_python_bundle(
join(self.dist_dir, python_bundle_dir), arch)
if not self.ctx.with_debug_symbols:
self.strip_libraries(arch)
self.fry_eggs(site_packages_dir)

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

super().assemble_distribution()


bootstrap = SDL2GradleBootstrap()
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := main

SDL_PATH := ../../SDL

LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include

# Add your application source files here...
LOCAL_SRC_FILES := start.c

LOCAL_CFLAGS += -I$(PYTHON_INCLUDE_ROOT) $(EXTRA_CFLAGS)

LOCAL_SHARED_LIBRARIES := SDL2 python_shared

LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -llog $(EXTRA_LDLIBS)

LOCAL_LDFLAGS += -L$(PYTHON_LINK_ROOT) $(APPLICATION_ADDITIONAL_LDFLAGS)

include $(BUILD_SHARED_LIBRARY)
12 changes: 12 additions & 0 deletions pythonforandroid/bootstraps/sdl3/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from pythonforandroid.bootstraps._sdl_common import SDLGradleBootstrap


class SDL3GradleBootstrap(SDLGradleBootstrap):
name = "sdl3"

recipe_depends = list(
set(SDLGradleBootstrap.recipe_depends).union({"sdl3"})
)


bootstrap = SDL3GradleBootstrap()
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := main

SDL_PATH := ../../SDL

LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include

# Add your application source files here...
LOCAL_SRC_FILES := start.c

LOCAL_CFLAGS += -I$(PYTHON_INCLUDE_ROOT) $(EXTRA_CFLAGS)

LOCAL_SHARED_LIBRARIES := SDL3 python_shared

LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -llog $(EXTRA_LDLIBS)

LOCAL_LDFLAGS += -L$(PYTHON_LINK_ROOT) $(APPLICATION_ADDITIONAL_LDFLAGS)

include $(BUILD_SHARED_LIBRARY)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := main

LOCAL_SRC_FILES := start.c

LOCAL_STATIC_LIBRARIES := SDL3_static


include $(BUILD_SHARED_LIBRARY)
$(call import-module,SDL)LOCAL_PATH := $(call my-dir)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

#define BOOTSTRAP_NAME_SDL3

const char bootstrap_name[] = "SDL3"; // capitalized for historic reasons

Loading
Loading