mirror of https://github.com/libsdl-org/SDL
android: create android project in create-android-project.py python script
This script supersedes androidbuild.sh, and also supports using a SDL3 prefab archive
This commit is contained in:
parent
2d05dcc1f7
commit
50ae47af5e
|
@ -40,7 +40,11 @@ jobs:
|
|||
- name: Create Gradle project
|
||||
if: ${{ matrix.platform.gradle }}
|
||||
run: |
|
||||
build-scripts/androidbuild.sh org.libsdl.testspriteminimal test/testspriteminimal.c test/icon.h
|
||||
python build-scripts/create-android-project.py \
|
||||
--output "build" \
|
||||
--variant copy \
|
||||
org.libsdl.testspriteminimal \
|
||||
test/testspriteminimal.c test/icon.h
|
||||
echo ""
|
||||
echo "Project contents:"
|
||||
echo ""
|
||||
|
|
|
@ -461,7 +461,6 @@ jobs:
|
|||
sparse-checkout: 'build-scripts/build-release.py'
|
||||
- name: 'Setup Android NDK'
|
||||
uses: nttld/setup-ndk@v1
|
||||
id: setup_ndk
|
||||
with:
|
||||
local-cache: true
|
||||
ndk-version: r21e
|
||||
|
@ -500,3 +499,71 @@ jobs:
|
|||
with:
|
||||
name: android
|
||||
path: '${{ github.workspace }}/dist'
|
||||
|
||||
android-verify:
|
||||
needs: [android, src]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'Set up Python'
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: 'Setup Android NDK'
|
||||
uses: nttld/setup-ndk@v1
|
||||
with:
|
||||
local-cache: true
|
||||
ndk-version: r21e
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
- name: 'Download source archives'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: sources
|
||||
path: '${{ github.workspace }}'
|
||||
- name: 'Download Android .aar archive'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: android
|
||||
path: '${{ github.workspace }}'
|
||||
- name: 'Untar ${{ needs.src.outputs.src-tar-gz }}'
|
||||
id: src
|
||||
run: |
|
||||
mkdir -p /tmp/tardir
|
||||
tar -C /tmp/tardir -v -x -f "${{ github.workspace }}/${{ needs.src.outputs.src-tar-gz }}"
|
||||
echo "path=/tmp/tardir/${{ needs.src.outputs.project }}-${{ needs.src.outputs.version }}" >>$GITHUB_OUTPUT
|
||||
- name: 'Create gradle project'
|
||||
id: create-gradle-project
|
||||
run: |
|
||||
python ${{ steps.src.outputs.path }}/build-scripts/create-android-project.py \
|
||||
org.libsdl.testspriteminimal \
|
||||
${{ steps.src.outputs.path }}/test/testspriteminimal.c \
|
||||
${{ steps.src.outputs.path }}/test/icon.h \
|
||||
--variant aar \
|
||||
--output "/tmp/projects"
|
||||
echo "path=/tmp/projects/org.libsdl.testspriteminimal" >>$GITHUB_OUTPUT
|
||||
|
||||
echo ""
|
||||
echo "Project contents:"
|
||||
echo ""
|
||||
find "/tmp/projects/org.libsdl.testspriteminimal"
|
||||
- name: 'Remove SDL sources to make sure they are not used'
|
||||
run: |
|
||||
rm -rf "${{ steps.src.outputs.path }}"
|
||||
- name: 'Copy SDL3 aar into Gradle project'
|
||||
run: |
|
||||
cp "${{ github.workspace }}/${{ needs.android.outputs.android-aar }}" "${{ steps.create-gradle-project.outputs.path }}/app/libs"
|
||||
|
||||
echo ""
|
||||
echo "Project contents:"
|
||||
echo ""
|
||||
find "${{ steps.create-gradle-project.outputs.path }}"
|
||||
- name: 'Build app (Gradle & ndk-build)'
|
||||
run: |
|
||||
cd "${{ steps.create-gradle-project.outputs.path }}"
|
||||
./gradlew -i assembleRelease -PBUILD_WITH_CMAKE=1
|
||||
- name: 'Build app (Gradle & CMake)'
|
||||
run: |
|
||||
cd "${{ steps.create-gradle-project.outputs.path }}"
|
||||
./gradlew -i assembleRelease
|
||||
|
|
|
@ -2,11 +2,6 @@ cmake_minimum_required(VERSION 3.6)
|
|||
|
||||
project(GAME)
|
||||
|
||||
# armeabi-v7a requires cpufeatures library
|
||||
# include(AndroidNdkModules)
|
||||
# android_ndk_import_module_cpufeatures()
|
||||
|
||||
|
||||
# SDL sources are in a subfolder named "SDL"
|
||||
add_subdirectory(SDL)
|
||||
|
||||
|
|
|
@ -4,15 +4,16 @@ 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 := YourSourceHere.c
|
||||
LOCAL_SRC_FILES := \
|
||||
YourSourceHere.c
|
||||
|
||||
SDL_PATH := ../SDL # SDL
|
||||
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include # SDL
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := SDL3
|
||||
|
||||
LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -lOpenSLES -llog -landroid
|
||||
LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -lOpenSLES -llog -landroid # SDL
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
|
|
@ -1,106 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
SOURCES=()
|
||||
MKSOURCES=""
|
||||
CURDIR=`pwd -P`
|
||||
|
||||
# Fetch sources
|
||||
if [[ $# -ge 2 ]]; then
|
||||
for src in ${@:2}
|
||||
do
|
||||
SOURCES+=($src)
|
||||
MKSOURCES="$MKSOURCES $(basename $src)"
|
||||
done
|
||||
else
|
||||
if [ -n "$1" ]; then
|
||||
while read src
|
||||
do
|
||||
SOURCES+=($src)
|
||||
MKSOURCES="$MKSOURCES $(basename $src)"
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$1" ] || [ -z "$SOURCES" ]; then
|
||||
echo "Usage: androidbuild.sh com.yourcompany.yourapp < sources.list"
|
||||
echo "Usage: androidbuild.sh com.yourcompany.yourapp source1.c source2.c ...sourceN.c"
|
||||
echo "To copy SDL source instead of symlinking: COPYSOURCE=1 androidbuild.sh ... "
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SDLPATH="$( cd "$(dirname "$0")/.." ; pwd -P )"
|
||||
|
||||
if [ -z "$ANDROID_HOME" ];then
|
||||
echo "Please set the ANDROID_HOME directory to the path of the Android SDK"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$ANDROID_HOME/ndk-bundle" -a -z "$ANDROID_NDK_HOME" ]; then
|
||||
echo "Please set the ANDROID_NDK_HOME directory to the path of the Android NDK"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
APP="$1"
|
||||
APPARR=(${APP//./ })
|
||||
BUILDPATH="$SDLPATH/build/$APP"
|
||||
|
||||
# Start Building
|
||||
|
||||
rm -rf $BUILDPATH
|
||||
mkdir -p $BUILDPATH
|
||||
|
||||
cp -r $SDLPATH/android-project/* $BUILDPATH
|
||||
|
||||
# Copy SDL sources
|
||||
mkdir -p $BUILDPATH/app/jni/SDL
|
||||
if [ -z "$COPYSOURCE" ]; then
|
||||
ln -s $SDLPATH/src $BUILDPATH/app/jni/SDL
|
||||
ln -s $SDLPATH/include $BUILDPATH/app/jni/SDL
|
||||
else
|
||||
cp -r $SDLPATH/src $BUILDPATH/app/jni/SDL
|
||||
cp -r $SDLPATH/include $BUILDPATH/app/jni/SDL
|
||||
fi
|
||||
|
||||
cp -r $SDLPATH/LICENSE.txt $BUILDPATH/app/jni/SDL
|
||||
cp -r $SDLPATH/README.md $BUILDPATH/app/jni/SDL
|
||||
cp -r $SDLPATH/Android.mk $BUILDPATH/app/jni/SDL
|
||||
cp -r $SDLPATH/CMakeLists.txt $BUILDPATH/app/jni/SDL
|
||||
cp -r $SDLPATH/cmake $BUILDPATH/app/jni/SDL
|
||||
sed -i -e "s|YourSourceHere.c|$MKSOURCES|g" $BUILDPATH/app/jni/src/Android.mk
|
||||
sed -i -e "s|YourSourceHere.c|$MKSOURCES|g" $BUILDPATH/app/jni/src/CMakeLists.txt
|
||||
sed -i -e "s|org\.libsdl\.app|$APP|g" $BUILDPATH/app/build.gradle
|
||||
sed -i -e "s|org\.libsdl\.app|$APP|g" $BUILDPATH/app/src/main/AndroidManifest.xml
|
||||
|
||||
# Copy user sources
|
||||
for src in "${SOURCES[@]}"
|
||||
do
|
||||
cp $src $BUILDPATH/app/jni/src
|
||||
done
|
||||
|
||||
# Create an inherited Activity
|
||||
cd $BUILDPATH/app/src/main/java
|
||||
for folder in "${APPARR[@]}"
|
||||
do
|
||||
mkdir -p $folder
|
||||
cd $folder
|
||||
done
|
||||
|
||||
# Uppercase the first char in the activity class name because it's Java
|
||||
ACTIVITY="$(echo $folder | awk '{$1=toupper(substr($1,0,1))substr($1,2)}1')Activity"
|
||||
sed -i -e "s|\"SDLActivity\"|\"$ACTIVITY\"|g" $BUILDPATH/app/src/main/AndroidManifest.xml
|
||||
|
||||
# Fill in a default Activity
|
||||
cat >"$ACTIVITY.java" <<__EOF__
|
||||
package $APP;
|
||||
|
||||
import org.libsdl.app.SDLActivity;
|
||||
|
||||
public class $ACTIVITY extends SDLActivity
|
||||
{
|
||||
}
|
||||
__EOF__
|
||||
|
||||
# Update project and build
|
||||
echo "To build and install to a device for testing, run the following:"
|
||||
echo "cd $BUILDPATH"
|
||||
echo "./gradlew installDebug"
|
|
@ -694,7 +694,7 @@ class Releaser:
|
|||
zip_object.write(test_library, arcname=f"prefab/modules/{self.project}_test/libs/android.{android_abi}/lib{self.project}_test.a")
|
||||
zip_object.writestr(f"prefab/modules/{self.project}_test/libs/android.{android_abi}/abi.json", self.get_prefab_abi_json_text(abi=android_abi, cpp=False, shared=False))
|
||||
|
||||
self.artifacts[f"android-prefab-aar"] = aar_path
|
||||
self.artifacts[f"android-aar"] = aar_path
|
||||
|
||||
@classmethod
|
||||
def extract_sdl_version(cls, root: Path, project: str):
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
#!/usr/bin/env python
|
||||
import os
|
||||
from argparse import ArgumentParser
|
||||
from pathlib import Path
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
|
||||
SDL_ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
def extract_sdl_version():
|
||||
"""
|
||||
Extract SDL version from SDL3/SDL_version.h
|
||||
"""
|
||||
|
||||
with open(SDL_ROOT / "include/SDL3/SDL_version.h") as f:
|
||||
data = f.read()
|
||||
|
||||
major = int(next(re.finditer(r"#define\s+SDL_MAJOR_VERSION\s+([0-9]+)", data)).group(1))
|
||||
minor = int(next(re.finditer(r"#define\s+SDL_MINOR_VERSION\s+([0-9]+)", data)).group(1))
|
||||
micro = int(next(re.finditer(r"#define\s+SDL_MICRO_VERSION\s+([0-9]+)", data)).group(1))
|
||||
return f"{major}.{minor}.{micro}"
|
||||
|
||||
def replace_in_file(path, regex_what, replace_with):
|
||||
with open(path, "r") as f:
|
||||
data = f.read()
|
||||
|
||||
new_data, count = re.subn(regex_what, replace_with, data)
|
||||
|
||||
assert count > 0, f"\"{regex_what}\" did not match anything in \"{path}\""
|
||||
|
||||
with open(path, "w") as f:
|
||||
f.write(new_data)
|
||||
|
||||
|
||||
def android_mk_use_prefab(path):
|
||||
"""
|
||||
Replace relative SDL inclusion with dependency on prefab package
|
||||
"""
|
||||
|
||||
with open(path) as f:
|
||||
data = "".join(line for line in f.readlines() if "# SDL" not in line)
|
||||
|
||||
data, _ = re.subn("[\n]{3,}", "\n\n", data)
|
||||
|
||||
newdata = data + textwrap.dedent("""
|
||||
# https://google.github.io/prefab/build-systems.html
|
||||
|
||||
# Add the prefab modules to the import path.
|
||||
$(call import-add-path,/out)
|
||||
|
||||
# Import SDL3 so we can depend on it.
|
||||
$(call import-module,prefab/SDL3)
|
||||
""")
|
||||
|
||||
with open(path, "w") as f:
|
||||
f.write(newdata)
|
||||
|
||||
def cmake_mk_no_sdl(path):
|
||||
"""
|
||||
Don't add the source directories of SDL/SDL_image/SDL_mixer/...
|
||||
"""
|
||||
|
||||
with open(path) as f:
|
||||
lines = f.readlines()
|
||||
|
||||
newlines = []
|
||||
for line in lines:
|
||||
if "add_subdirectory(SDL" in line:
|
||||
while newlines[-1].startswith("#"):
|
||||
newlines = newlines[:-1]
|
||||
continue
|
||||
newlines.append(line)
|
||||
|
||||
newdata, _ = re.subn("[\n]{3,}", "\n\n", "".join(newlines))
|
||||
|
||||
with open(path, "w") as f:
|
||||
f.write(newdata)
|
||||
|
||||
def gradle_add_prefab_and_aar(path, aar):
|
||||
with open(path) as f:
|
||||
data = f.read()
|
||||
|
||||
data, count = re.subn("android {", textwrap.dedent("""
|
||||
android {
|
||||
buildFeatures {
|
||||
prefab true
|
||||
}"""), data)
|
||||
assert count == 1
|
||||
|
||||
data, count = re.subn("dependencies {", textwrap.dedent(f"""
|
||||
dependencies {{
|
||||
implementation files('libs/{aar}')"""), data)
|
||||
assert count == 1
|
||||
|
||||
with open(path, "w") as f:
|
||||
f.write(data)
|
||||
|
||||
|
||||
def main():
|
||||
description = "Create a simple Android gradle project from input sources."
|
||||
epilog = "You need to manually copy a prebuilt SDL3 Android archive into the project tree when using the aar variant."
|
||||
parser = ArgumentParser(description=description, allow_abbrev=False)
|
||||
parser.add_argument("package_name", metavar="PACKAGENAME", help="Android package name e.g. com.yourcompany.yourapp")
|
||||
parser.add_argument("sources", metavar="SOURCE", nargs="*", help="Source code of your application. The files are copied to the output directory.")
|
||||
parser.add_argument("--variant", choices=["copy", "symlink", "aar"], default="copy", help="Choose variant of SDL project (copy: copy SDL sources, symlink: symlink SDL sources, aar: use Android aar archive)")
|
||||
parser.add_argument("--output", "-o", default=SDL_ROOT / "build", type=Path, help="Location where to store the Android project")
|
||||
parser.add_argument("--version", default=None, help="SDL3 version to use as aar dependency (only used for aar variant)")
|
||||
|
||||
args = parser.parse_args()
|
||||
if not args.sources:
|
||||
print("Reading source file paths from stdin (press CTRL+D to stop)")
|
||||
args.sources = [path for path in sys.stdin.read().strip().split() if path]
|
||||
if not args.sources:
|
||||
parser.error("No sources passed")
|
||||
|
||||
if not os.getenv("ANDROID_HOME"):
|
||||
print("WARNING: ANDROID_HOME environment variable not set", file=sys.stderr)
|
||||
if not os.getenv("ANDROID_NDK_HOME"):
|
||||
print("WARNING: ANDROID_NDK_HOME environment variable not set", file=sys.stderr)
|
||||
|
||||
args.sources = [Path(src) for src in args.sources]
|
||||
|
||||
build_path = args.output / args.package_name
|
||||
|
||||
# Remove the destination folder
|
||||
shutil.rmtree(build_path, ignore_errors=True)
|
||||
|
||||
# Copy the Android project
|
||||
shutil.copytree(SDL_ROOT / "android-project", build_path)
|
||||
|
||||
# Add the source files to the ndk-build and cmake projects
|
||||
replace_in_file(build_path / "app/jni/src/Android.mk", r"YourSourceHere\.c", " \\\n ".join(src.name for src in args.sources))
|
||||
replace_in_file(build_path / "app/jni/src/CMakeLists.txt", r"YourSourceHere\.c", "\n ".join(src.name for src in args.sources))
|
||||
|
||||
# Remove placeholder source "YourSourceHere.c"
|
||||
(build_path / "app/jni/src/YourSourceHere.c").unlink()
|
||||
|
||||
# Copy sources to output folder
|
||||
for src in args.sources:
|
||||
if not src.is_file():
|
||||
parser.error(f"\"{src}\" is not a file")
|
||||
shutil.copyfile(src, build_path / "app/jni/src" / src.name)
|
||||
|
||||
sdl_project_files = (
|
||||
SDL_ROOT / "src",
|
||||
SDL_ROOT / "include",
|
||||
SDL_ROOT / "LICENSE.txt",
|
||||
SDL_ROOT / "README.md",
|
||||
SDL_ROOT / "Android.mk",
|
||||
SDL_ROOT / "CMakeLists.txt",
|
||||
SDL_ROOT / "cmake",
|
||||
)
|
||||
if args.variant == "copy":
|
||||
(build_path / "app/jni/SDL").mkdir(exist_ok=True, parents=True)
|
||||
for sdl_project_file in sdl_project_files:
|
||||
# Copy SDL project files and directories
|
||||
if sdl_project_file.is_dir():
|
||||
shutil.copytree(sdl_project_file, build_path / "app/jni/SDL" / sdl_project_file.name)
|
||||
elif sdl_project_file.is_file():
|
||||
shutil.copyfile(sdl_project_file, build_path / "app/jni/SDL" / sdl_project_file.name)
|
||||
elif args.variant == "symlink":
|
||||
(build_path / "app/jni/SDL").mkdir(exist_ok=True, parents=True)
|
||||
# Create symbolic links for all SDL project files
|
||||
for sdl_project_file in sdl_project_files:
|
||||
os.symlink(sdl_project_file, build_path / "app/jni/SDL" / sdl_project_file.name)
|
||||
elif args.variant == "aar":
|
||||
if not args.version:
|
||||
args.version = extract_sdl_version()
|
||||
|
||||
major = args.version.split(".")[0]
|
||||
aar = f"SDL{ major }-{ args.version }.aar"
|
||||
|
||||
# Remove all SDL java classes
|
||||
shutil.rmtree(build_path / "app/src/main/java")
|
||||
|
||||
# Use prefab to generate include-able files
|
||||
gradle_add_prefab_and_aar(build_path / "app/build.gradle", aar=aar)
|
||||
|
||||
# Make sure to use the prefab-generated files and not SDL sources
|
||||
android_mk_use_prefab(build_path / "app/jni/src/Android.mk")
|
||||
cmake_mk_no_sdl(build_path / "app/jni/CMakeLists.txt")
|
||||
|
||||
aar_libs_folder = build_path / "app/libs"
|
||||
aar_libs_folder.mkdir(parents=True)
|
||||
with (aar_libs_folder / "copy-sdl-aars-here.txt").open("w") as f:
|
||||
f.write(f"Copy {aar} to this folder.\n")
|
||||
|
||||
print(f"WARNING: copy { aar } to { aar_libs_folder }", file=sys.stderr)
|
||||
|
||||
# Create entry activity, subclassing SDLActivity
|
||||
activity = args.package_name[args.package_name.rfind(".") + 1:].capitalize() + "Activity"
|
||||
activity_path = build_path / "app/src/main/java" / args.package_name.replace(".", "/") / f"{activity}.java"
|
||||
activity_path.parent.mkdir(parents=True)
|
||||
with activity_path.open("w") as f:
|
||||
f.write(textwrap.dedent(f"""
|
||||
package {args.package_name};
|
||||
|
||||
import org.libsdl.app.SDLActivity;
|
||||
|
||||
public class {activity} extends SDLActivity
|
||||
{{
|
||||
}}
|
||||
"""))
|
||||
|
||||
# Add the just-generated activity to the Android manifest
|
||||
replace_in_file(build_path / "app/src/main/AndroidManifest.xml", "SDLActivity", activity)
|
||||
|
||||
# Update project and build
|
||||
print("To build and install to a device for testing, run the following:")
|
||||
print(f"cd {build_path}")
|
||||
print("./gradlew installDebug")
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
|
@ -40,19 +40,19 @@ src/core/android/SDL_android.c
|
|||
Building an app
|
||||
================================================================================
|
||||
|
||||
For simple projects you can use the script located at build-scripts/androidbuild.sh
|
||||
For simple projects you can use the script located at build-scripts/create-android-project.py
|
||||
|
||||
There's two ways of using it:
|
||||
|
||||
androidbuild.sh com.yourcompany.yourapp < sources.list
|
||||
androidbuild.sh com.yourcompany.yourapp source1.c source2.c ...sourceN.c
|
||||
./create-android-project.py com.yourcompany.yourapp < sources.list
|
||||
./create-android-project.py com.yourcompany.yourapp source1.c source2.c ...sourceN.c
|
||||
|
||||
sources.list should be a text file with a source file name in each line
|
||||
Filenames should be specified relative to the current directory, for example if
|
||||
you are in the build-scripts directory and want to create the testgles.c test, you'll
|
||||
run:
|
||||
|
||||
./androidbuild.sh org.libsdl.testgles ../test/testgles.c
|
||||
./create-android-project.py org.libsdl.testgles ../test/testgles.c
|
||||
|
||||
One limitation of this script is that all sources provided will be aggregated into
|
||||
a single directory, thus all your source files should have a unique name.
|
||||
|
@ -61,7 +61,7 @@ Once the project is complete the script will tell you where the debug APK is loc
|
|||
If you want to create a signed release APK, you can use the project created by this
|
||||
utility to generate it.
|
||||
|
||||
Finally, a word of caution: re running androidbuild.sh wipes any changes you may have
|
||||
Finally, a word of caution: re running create-android-project.py wipes any changes you may have
|
||||
done in the build directory for the app!
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue