Wiki Page Content

Differences between revisions 1 and 53 (spanning 52 versions)
Revision 1 as of 2014-06-18 21:42:07
Size: 1629
Comment: First version
Revision 53 as of 2018-11-21 15:47:19
Size: 16412
Comment: Rename "Update" section to "Troubleshooting"
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
== Building SDL2 for Android ==

First, a
lot of information can be found in [[https://hg.libsdl.org/SDL/file/tip/README-android.txt|SDL2/README-android.txt]].

The following offers simple walkthroughs to compile SDL Android apps.

=
== Pre-requisites ===
#pragma section-numbers 2

= Building SDL2 for Android =

<<TableOfContents()>>

== Existing documentation ==

A
lot of information can be found in [[https://hg.libsdl.org/SDL/file/default/docs/README-android.md|SDL/docs/README-android.md]].

This page is more walkthrough-oriented.


== Pre-requisites ==
Line 10: Line 17:
sudo apt-get install openjdk-7-jdk ant
}}}
 * Install NDK (tested with r9d)
 * Install the latest SDK, run `android` and install API 12

=== Test 1: SDL wrapper for simple programs ===
sudo apt install openjdk-8-jdk ant android-sdk-platform-tools-common
}}}
 * Install NDK (tested with [[https://dl.google.com/android/repository/android-ndk-r10e-linux-x86_64.zip|r10e]])
 * Install the latest SDK, run `tools/bin/sdkmanager` (or `tools/android` pre-2017) and install one API (>=12; >= 19 for SDL >= 2.0.8; >=26 if for SDL >= 2.0.9)
 * Configure your environment variables, e.g.: {{{
PATH="/usr/src/android-ndk-r8c:$PATH" # for 'ndk-build'
PATH="/usr/src/android-sdk-linux/tools:$PATH" # for 'android'
PATH="/usr/src/android-sdk-linux/platform-tools:$PATH" # for 'adb'
export ANDROID_HOME="/usr/src/android-sdk-linux" # for gradle
export ANDROID_NDK_HOME="/usr/src/android-ndk-rXX" # for gradle
}}}

== Simple builds ==

=== SDL wrapper for simple programs ===
Line 18: Line 34:
 cd /usr/src/SDL2/build-scripts/
 ./androidbuild.sh org.libsdl.testgles ../test/testgles.c
cd /usr/src/SDL2/build-scripts/
./androidbuild.sh org.libsdl.testgles ../test/testgles.c
Line 22: Line 38:
 cd /usr/src/SDL2/build/org.libsdl.testgles/
 ant debug install
cd /usr/src/SDL2/build/org.libsdl.testgles/
ant debug install       # SDL <= 2.0.7
./gradlew installDebug # SDL >= 2.0.8
Line 27: Line 44:
 * triple taret armeabi/armeabi-v7a/x86 compilation!  * multiple targets armeabi-v7a/arm64-v8a/x86/x86_64 compilation
Line 30: Line 47:

=== Test 2: SDL wrapper + SDL_image ===

First, modify showimage.c to show a simple embedded image (e.g. XPM).

To compile: {{{
 cd /usr/src/SDL2/build-scripts/
 ./androidbuild.sh org.libsdl.showimage /usr/src/SDL2_image/showimage.c
 cd /usr/src/SDL2/build/org.libsdl.showimage/
 ln -s /usr/src/SDL2_image jni/
 ln -s /usr/src/SDL2_image/external/libwebp-0.3.0 jni/webp
 # - Edit jni/Android.mk
 # LOCAL_SHARED_LIBRARIES := ... SDL2_image
 # - Edit android-project/src/org/libsdl/app/SDLActivity.java:
 # System.loadLibrary("SDL2_image");
 # - Edit jni/Application.mk: remove x86 where jpeg-9 doesn't build
 ndk-build -j$(nproc)
 ant debug install
==== Troubleshooting ====

Before running {{{./androidbuild.sh org.libsdl.testgles ../test/testgles.c}}}, I did the following:
 * use OpenJDK 8: execute {{{sudo update-alternatives --config java}}} and select jdk-8 as default; or use {{{JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 ./gradlew}}}
 * fixed in 2.0.9: in {{{/android-project/build.gradle}}} change (in BOTH places in the file code appears) from
{{{
    repositories {
        jcenter()
    }
}}}
to
{{{
    repositories {
        jcenter()
        google()
    }
}}}
 * {{{javax/xml/bind/annotation/XmlSchema}}} error: check the Android Gradle Plugin version in {{{/android-project/build.gradle}}}, e.g.
{{{ classpath 'com.android.tools.build:gradle:3.1.0' }}}
 * You can customize the Gradle version in {{{/android-project/gradle/wrapper/gradle-wrapper.properties}}}:
{{{ distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip }}}
 * You can customize your SDK/NDK versions in {{{android-project/app/build.gradle}}}: {{{
android {
    buildToolsVersion "28.0.1"
    compileSdkVersion 28
}}}
 * You can customize your targets depending on the NDK version: {{{
        externalNativeBuild {
            ndkBuild {
                arguments "APP_PLATFORM=android-14"
                abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}}}
 * {{{ABIs [x86_64, arm64-v8a] are not supported for platform. Supported ABIs are [armeabi, armeabi-v7a, x86, mips]}}}: upgrade to NDK >= 10
 * Using ant (SDL <= 2.0.7): edit {{{build-scripts/androidbuild.sh}}}, find the {{{$ANDROID update project}}} line, and add {{{--target android-XX}}} to it (replace XX with your installed API number)
 * TODO: check how we can use the distro's gradle instead of executing stuff from the Internet - {{{apt install gradle libgradle-android-plugin-java}}}

=== SDL wrapper + SDL_image NDK module ===

Let's modify `SDL2_image/showimage.c` to show a simple embedded image (e.g. XPM).

{{{#!highlight cpp
#include "SDL.h"
#include "SDL_image.h"

/* XPM */
static char * icon_xpm[] = {
  "32 23 3 1",
  " c #FFFFFF",
  ". c #000000",
  "+ c #FFFF00",
  " ",
  " ........ ",
  " ..++++++++.. ",
  " .++++++++++++. ",
  " .++++++++++++++. ",
  " .++++++++++++++++. ",
  " .++++++++++++++++++. ",
  " .+++....++++....+++. ",
  " .++++.. .++++.. .++++. ",
  " .++++....++++....++++. ",
  " .++++++++++++++++++++. ",
  " .++++++++++++++++++++. ",
  " .+++++++++..+++++++++. ",
  " .+++++++++..+++++++++. ",
  " .++++++++++++++++++++. ",
  " .++++++++++++++++++. ",
  " .++...++++++++...++. ",
  " .++............++. ",
  " .++..........++. ",
  " .+++......+++. ",
  " ..++++++++.. ",
  " ........ ",
  " "};

int main(int argc, char *argv[])
{
  SDL_Window *window;
  SDL_Renderer *renderer;
  SDL_Surface *surface;
  SDL_Texture *texture;
  int done;
  SDL_Event event;

  if (SDL_CreateWindowAndRenderer(0, 0, 0, &window, &renderer) < 0) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
        "SDL_CreateWindowAndRenderer() failed: %s", SDL_GetError());
    return(2);
  }

  surface = IMG_ReadXPMFromArray(icon_xpm);
  texture = SDL_CreateTextureFromSurface(renderer, surface);
  if (!texture) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
        "Couldn't load texture: %s", SDL_GetError());
    return(2);
  }
  SDL_SetWindowSize(window, 800, 480);

  done = 0;
  while (!done) {
    while (SDL_PollEvent(&event)) {
      if (event.type == SDL_QUIT)
        done = 1;
    }
    SDL_RenderCopy(renderer, texture, NULL, NULL);
    SDL_RenderPresent(renderer);
    SDL_Delay(100);
  }
  SDL_DestroyTexture(texture);

  SDL_Quit();
  return(0);
}
}}}

Then let's make an Android app out of it. To compile: {{{
cd /usr/src/SDL2/build-scripts/
./androidbuild.sh org.libsdl.showimage /usr/src/SDL2_image/showimage.c
cd /usr/src/SDL2/build/org.libsdl.showimage/
ln -s /usr/src/SDL2_image jni/
ln -s /usr/src/SDL2_image/external/libwebp-0.3.0 jni/webp
sed -i -e 's/^LOCAL_SHARED_LIBRARIES.*/& SDL2_image/' jni/src/Android.mk
ndk-build -j$(nproc)
ant debug install
Line 52: Line 175:


== Build an autotools-friendly environment ==

You use autotools in your project and can't be bothering understanding ndk-build's cryptic errors? This guide is for you!

Note: this environment can be used for CMake too.

=== Compile a shared binaries bundle for SDL and SDL_* ===

 * Get the latests SDL2_* releases: {{{
cd /usr/src/
wget https://libsdl.org/release/SDL2-2.0.5.tar.gz
wget https://www.libsdl.org/projects/SDL_image/release/SDL2_image-2.0.1.tar.gz
wget https://www.libsdl.org/projects/SDL_mixer/release/SDL2_mixer-2.0.1.tar.gz
wget https://www.libsdl.org/projects/SDL_net/release/SDL2_net-2.0.1.tar.gz
wget https://www.libsdl.org/projects/SDL_ttf/release/SDL2_ttf-2.0.14.tar.gz

tar xf SDL2-2.0.5.tar.gz
tar xf SDL2_image-2.0.1.tar.gz
tar xf SDL2_mixer-2.0.1.tar.gz
tar xf SDL2_net-2.0.1.tar.gz
tar xf SDL2_ttf-2.0.14.tar.gz

ln -s SDL2-2.0.5 SDL2
ln -s SDL2_image-2.0.1 SDL2_image
ln -s SDL2_mixer-2.0.1 SDL2_mixer
ln -s SDL2_net-2.0.1 SDL2_net
ln -s SDL2_ttf-2.0.14 SDL2_ttf
}}}
 * Start with a minimal build: {{{
cd /usr/src/SDL2/
cd build-scripts/
#hg revert --all # remove traces of previous builds
# edit androidbuild.sh and modify $ANDROID update project --target android-XX
./androidbuild.sh org.libsdl /dev/null
# doesn't matter if the actual build fails, it's just for setup
cd ../build/org.libsdl/
}}}
 * Remove reference to our dummy file: {{{
rm -rf jni/src/
}}}
 * Reference SDL_image, SDL_mixer, SDL_ttf, and their dependencies, as NDK modules: {{{
ln -s /usr/src/SDL2_image jni/
ln -s /usr/src/SDL2_image/external/libwebp-0.3.0 jni/webp
ln -s /usr/src/SDL2_mixer jni/
ln -s /usr/src/SDL2_mixer/external/libmikmod-3.1.12 jni/libmikmod
ln -s /usr/src/SDL2_mixer/external/smpeg2-2.0.0 jni/smpeg2
ln -s /usr/src/SDL2_net jni/
ln -s /usr/src/SDL2_ttf jni/
}}}
 * Optionnaly edit `jni/Android.mk` to disable some formats, e.g.: {{{
SUPPORT_MP3_SMPEG := false
include $(call all-subdir-makefiles)
}}}
 * Launch the build! {{{
ndk-build -j$(nproc)
}}}

Note: no need to add `System.loadLibrary` calls in `SDLActivity.java`, your application will be linked to them and Android's ld-linux loads them automatically.


=== Install SDL in a GCC toolchain ===

Now:
 * Copy the NDK into a traditional GCC toolchain (leave android-14 as-is): {{{
/usr/src/android-ndk-r8c/build/tools/make-standalone-toolchain.sh \
  --platform=android-14 --install-dir=/usr/src/ndk-standalone-14-arm --arch=arm
}}}
 * Set your PATH (important, do it before any build): {{{
NDK_STANDALONE=/usr/src/ndk-standalone-14-arm
PATH=$NDK_STANDALONE/bin:$PATH
}}}
 * Install the SDL2 binaries in the toolchain: {{{
cd /usr/src/SDL2/build/org.libsdl/
for i in libs/armeabi/*; do ln -nfs $(pwd)/$i $NDK_STANDALONE/sysroot/usr/lib/; done
mkdir $NDK_STANDALONE/sysroot/usr/include/SDL2/
\cp jni/SDL/include/* $NDK_STANDALONE/sysroot/usr/include/SDL2/
\cp jni/*/SDL*.h $NDK_STANDALONE/sysroot/usr/include/SDL2/
}}}
 * Install `pkg-config` and install a host-triplet-prefixed symlink in the PATH (auto-detected by autoconf): {{{
VERSION=0.9.12
cd /usr/src/
wget http://rabbit.dereferenced.org/~nenolod/distfiles/pkgconf-$VERSION.tar.gz
tar xf pkgconf-$VERSION.tar.gz
cd pkgconf-$VERSION/
mkdir native-android/ && cd native-android/
../configure --prefix=$NDK_STANDALONE/sysroot/usr
make -j$(nproc)
make install
ln -s ../sysroot/usr/bin/pkgconf $NDK_STANDALONE/bin/arm-linux-androideabi-pkg-config
mkdir $NDK_STANDALONE/sysroot/usr/lib/pkgconfig/
}}}
 * Install pkg-config `.pc` files for SDL: {{{
cat <<'EOF' > $NDK_STANDALONE/sysroot/usr/lib/pkgconfig/sdl2.pc
prefix=/usr/src/ndk-standalone-14-arm/sysroot/usr
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: sdl2
Description: Simple DirectMedia Layer is a cross-platform multimedia library designed to provide low level access to audio, keyboard, mouse, joystick, 3D hardware via OpenGL, and 2D video framebuffer.
Version: 2.0.5
Requires:
Conflicts:
Libs: -lSDL2
Cflags: -I${includedir}/SDL2 -D_REENTRANT
EOF

cat <<'EOF' > $NDK_STANDALONE/sysroot/usr/lib/pkgconfig/SDL2_image.pc
prefix=/usr/src/ndk-standalone-14-arm/sysroot/usr
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: SDL2_image
Description: image loading library for Simple DirectMedia Layer
Version: 2.0.1
Requires: sdl2 >= 2.0.0
Libs: -L${libdir} -lSDL2_image
Cflags: -I${includedir}/SDL2
EOF

cat <<'EOF' > $NDK_STANDALONE/sysroot/usr/lib/pkgconfig/SDL2_mixer.pc
prefix=/usr/src/ndk-standalone-14-arm/sysroot/usr
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: SDL2_mixer
Description: mixer library for Simple DirectMedia Layer
Version: 2.0.1
Requires: sdl2 >= 2.0.0
Libs: -L${libdir} -lSDL2_mixer
Cflags: -I${includedir}/SDL2
EOF

cat <<'EOF' > $NDK_STANDALONE/sysroot/usr/lib/pkgconfig/SDL2_net.pc
prefix=/usr/src/ndk-standalone-14-arm/sysroot/usr
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: SDL2_net
Description: net library for Simple DirectMedia Layer
Version: 2.0.1
Requires: sdl2 >= 2.0.0
Libs: -L${libdir} -lSDL2_net
Cflags: -I${includedir}/SDL2
EOF

cat <<'EOF' > $NDK_STANDALONE/sysroot/usr/lib/pkgconfig/SDL2_ttf.pc
prefix=/usr/src/ndk-standalone-14-arm/sysroot/usr
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: SDL2_ttf
Description: ttf library for Simple DirectMedia Layer with FreeType 2 support
Version: 2.0.14
Requires: sdl2 >= 2.0.0
Libs: -L${libdir} -lSDL2_ttf
Cflags: -I${includedir}/SDL2
EOF
}}}

=== Building other dependencies ===

You can add any other libraries (e.g.: SDL2_gfx, freetype, gettext, gmp...) using commands like: {{{
mkdir cross-android/ && cd cross-android/
../configure --host=arm-linux-androideabi --prefix=$NDK_STANDALONE/sysroot/usr \
  --with-some-option --enable-another-option \
  --disable-shared
make -j$(nproc)
make install
}}}

Static builds (`--disable-shared`) are recommended for simplicity (no additional `.so` to declare).

Example with SDL2_gfx: {{{
VERSION=1.0.3
wget http://www.ferzkopp.net/Software/SDL2_gfx/SDL2_gfx-$VERSION.tar.gz
tar xf SDL2_gfx-$VERSION.tar.gz
mv SDL2_gfx-$VERSION/ SDL2_gfx/
cd SDL2_gfx/
mkdir cross-android/ && cd cross-android/
../configure --host=arm-linux-androideabi --prefix=$NDK_STANDALONE/sysroot/usr \
  --disable-shared --disable-mmx
make -j$(nproc)
make install
}}}

You can compile YOUR application using this technique, with some more steps to tell Android how to run it using JNI.


=== Build your autotools app ===

First, prepare an Android project:
 * Copy and adapt the `/usr/src/SDL2/android-project` skeleton as explained in `README-android.md`. You can leave it as-is in a first step.
 * Make links to the SDL binaries as well: {{{
mkdir -p libs/armeabi/
for i in /usr/src/SDL2/build/org.libsdl/libs/armeabi/*; do ln -nfs $i libs/armeabi/; done
}}}

Make your project Android-aware:
 * Add `/usr/src/SDL2/src/main/android/SDL_android_main.c` in your project (comment out the line referencing "SDL_internal.h"). Compile it as C (not C++).
 * In your {{{configure.ac}}}, detect Android: {{{
AM_CONDITIONAL(ANDROID, test "$host" = "arm-unknown-linux-androideabi")
}}}
 * In your {{{Makefile.am}}}, tell Automake you'll build executables as libraries, using something like: {{{
if ANDROID
  # Build .so JNI libs rather than executables
  AM_CFLAGS = -fPIC
  AM_LDFLAGS += -shared
  COMMON_OBJS += SDL_android_main.c
endif
}}}
 * Cross-compile your project using the GCC toolchain environment we created: {{{
PATH=$NDK_STANDALONE/bin:$PATH
mkdir cross-android/ && cd cross-android/
../configure --host=arm-linux-androideabi \
  --prefix=/android-aint-posix \
  --with-your-option --enable-your-other-option ...
make
}}}
 * Do this again for any additional arch you want to support (TODO: see how to support `armeabi-v7a` and document what devices support it); something like: {{{
mkdir cross-android-v7a/ && cd cross-android-v7a/
# .o: -march=armv5te -mtune=xscale -msoft-float -mthumb => -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=softfp -mthumb
# .so: -march=armv7-a -Wl,--fix-cortex-a8
CFLAGS="-g -O2 -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=softfp -mthumb" LFDLAGS="-march=armv7-a -Wl,--fix-cortex-a8" \
  ../configure --host=arm-linux-androideabi \
  ...
}}}

Now you can install your pre-built binaries and build the Android project:
 * Copy your program in `android-project/libs/armeabi/libmain.so`.
 * Build your Android `.apk`: {{{
android update project --name your_app --path . --target android-XX
ant debug
ant installd
}}}
 * You can run the application remotely: {{{
adb shell am start -a android.intenon.MAIN -n org.libsdl.app/org.libsdl.app.SDLActivity # replace with your app package
}}}
 * Your SDL2 Android app is running!

=== Build your CMake app ===

(Work In Progress)

You can use our Android GCC toolchain using a simple toolchain file: {{{
# CMake toolchain file
SET(CMAKE_SYSTEM_NAME Linux) # Tell CMake we're cross-compiling
include(CMakeForceCompiler)
# Prefix detection only works with compiler id "GNU"
CMAKE_FORCE_C_COMPILER(arm-linux-androideabi-gcc GNU)
SET(ANDROID TRUE)
}}}

You then call CMake like this: {{{
PATH=$NDK_STANDALONE/bin:$PATH
cmake \
  -D CMAKE_TOOLCHAIN_FILE=../android_toolchain.cmake \
  ...
}}}

== Troubleshootings ==

If {{{ant installd}}} categorically refuses to install with {{{Failure [INSTALL_FAILED_INSUFFICIENT_STORAGE]}}}, even if you have free local storage, that may mean anything. Check logcat first: {{{
adb logcat
}}}
If the error logs are not helpful (likely ;')) try locating all past traces of the application: {{{
find / -name "org...."
}}}
and remove them all.

If the problem persists, you may try installing on the SD card: {{{
adb install -s bin/app-debug.apk
}}}

-----

If you get in your logcat: {{{
SDL: Couldn't locate Java callbacks, check that they're named and typed correctly
}}}
this probably means your {{{SDLActivity.java}}} is out-of-sync with your libSDL2.so.

Building SDL2 for Android

1. Existing documentation

A lot of information can be found in SDL/docs/README-android.md.

This page is more walkthrough-oriented.

2. Pre-requisites

  • Install minimal Java environment. For instance, in Debian/Ubuntu:

    sudo apt install openjdk-8-jdk ant android-sdk-platform-tools-common
  • Install NDK (tested with r10e)

  • Install the latest SDK, run tools/bin/sdkmanager (or tools/android pre-2017) and install one API (>=12; >= 19 for SDL >= 2.0.8; >=26 if for SDL >= 2.0.9)

  • Configure your environment variables, e.g.:

    PATH="/usr/src/android-ndk-r8c:$PATH"                   # for 'ndk-build'
    PATH="/usr/src/android-sdk-linux/tools:$PATH"           # for 'android'
    PATH="/usr/src/android-sdk-linux/platform-tools:$PATH"  # for 'adb'
    export ANDROID_HOME="/usr/src/android-sdk-linux"        # for gradle
    export ANDROID_NDK_HOME="/usr/src/android-ndk-rXX"      # for gradle

3. Simple builds

3.1. SDL wrapper for simple programs

  • Compile a sample app (calls ndk-build):

    cd /usr/src/SDL2/build-scripts/
    ./androidbuild.sh org.libsdl.testgles ../test/testgles.c
  • Follow the instructions to install on your device:

    cd /usr/src/SDL2/build/org.libsdl.testgles/
    ant debug install       # SDL <= 2.0.7
    ./gradlew installDebug  # SDL >= 2.0.8

Notes:

  • multiple targets armeabi-v7a/arm64-v8a/x86/x86_64 compilation
  • application doesn't quit

3.1.1. Troubleshooting

Before running ./androidbuild.sh org.libsdl.testgles ../test/testgles.c, I did the following:

  • use OpenJDK 8: execute sudo update-alternatives --config java and select jdk-8 as default; or use JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 ./gradlew

  • fixed in 2.0.9: in /android-project/build.gradle change (in BOTH places in the file code appears) from

    repositories {
        jcenter()
    }

to

    repositories {
        jcenter()
        google()
    }
  • javax/xml/bind/annotation/XmlSchema error: check the Android Gradle Plugin version in /android-project/build.gradle, e.g.

 classpath 'com.android.tools.build:gradle:3.1.0' 

  • You can customize the Gradle version in /android-project/gradle/wrapper/gradle-wrapper.properties:

 distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip 

  • You can customize your SDK/NDK versions in android-project/app/build.gradle:

    android {
        buildToolsVersion "28.0.1"
        compileSdkVersion 28
  • You can customize your targets depending on the NDK version:

            externalNativeBuild {
                ndkBuild {
                    arguments "APP_PLATFORM=android-14"
                    abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
  • ABIs [x86_64, arm64-v8a] are not supported for platform. Supported ABIs are [armeabi, armeabi-v7a, x86, mips]: upgrade to NDK >= 10

  • Using ant (SDL <= 2.0.7): edit build-scripts/androidbuild.sh, find the $ANDROID update project line, and add --target android-XX to it (replace XX with your installed API number)

  • TODO: check how we can use the distro's gradle instead of executing stuff from the Internet - apt install gradle libgradle-android-plugin-java

3.2. SDL wrapper + SDL_image NDK module

Let's modify SDL2_image/showimage.c to show a simple embedded image (e.g. XPM).

#include "SDL.h"
#include "SDL_image.h"

/* XPM */
static char * icon_xpm[] = {
  "32 23 3 1",
  "     c #FFFFFF",
  ".    c #000000",
  "+    c #FFFF00",
  "                                ",
  "            ........            ",
  "          ..++++++++..          ",
  "         .++++++++++++.         ",
  "        .++++++++++++++.        ",
  "       .++++++++++++++++.       ",
  "      .++++++++++++++++++.      ",
  "      .+++....++++....+++.      ",
  "     .++++.. .++++.. .++++.     ",
  "     .++++....++++....++++.     ",
  "     .++++++++++++++++++++.     ",
  "     .++++++++++++++++++++.     ",
  "     .+++++++++..+++++++++.     ",
  "     .+++++++++..+++++++++.     ",
  "     .++++++++++++++++++++.     ",
  "      .++++++++++++++++++.      ",
  "      .++...++++++++...++.      ",
  "       .++............++.       ",
  "        .++..........++.        ",
  "         .+++......+++.         ",
  "          ..++++++++..          ",
  "            ........            ",
  "                                "};

int main(int argc, char *argv[])
{
  SDL_Window *window;
  SDL_Renderer *renderer;
  SDL_Surface *surface;
  SDL_Texture *texture;
  int done;
  SDL_Event event;

  if (SDL_CreateWindowAndRenderer(0, 0, 0, &window, &renderer) < 0) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
        "SDL_CreateWindowAndRenderer() failed: %s", SDL_GetError());
    return(2);
  }

  surface = IMG_ReadXPMFromArray(icon_xpm);
  texture = SDL_CreateTextureFromSurface(renderer, surface);
  if (!texture) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
        "Couldn't load texture: %s", SDL_GetError());
    return(2);
  }
  SDL_SetWindowSize(window, 800, 480);

  done = 0;
  while (!done) {
    while (SDL_PollEvent(&event)) {
      if (event.type == SDL_QUIT)
        done = 1;
    }
    SDL_RenderCopy(renderer, texture, NULL, NULL);
    SDL_RenderPresent(renderer);
    SDL_Delay(100);
  }
  SDL_DestroyTexture(texture);

  SDL_Quit();
  return(0);
}

Then let's make an Android app out of it. To compile:

cd /usr/src/SDL2/build-scripts/
./androidbuild.sh org.libsdl.showimage /usr/src/SDL2_image/showimage.c
cd /usr/src/SDL2/build/org.libsdl.showimage/
ln -s /usr/src/SDL2_image jni/
ln -s /usr/src/SDL2_image/external/libwebp-0.3.0 jni/webp
sed -i -e 's/^LOCAL_SHARED_LIBRARIES.*/& SDL2_image/' jni/src/Android.mk
ndk-build -j$(nproc)
ant debug install

Notes:

  • application doesn't restart properly

4. Build an autotools-friendly environment

You use autotools in your project and can't be bothering understanding ndk-build's cryptic errors? This guide is for you!

Note: this environment can be used for CMake too.

4.1. Compile a shared binaries bundle for SDL and SDL_*

  • Get the latests SDL2_* releases:

    cd /usr/src/
    wget https://libsdl.org/release/SDL2-2.0.5.tar.gz
    wget https://www.libsdl.org/projects/SDL_image/release/SDL2_image-2.0.1.tar.gz
    wget https://www.libsdl.org/projects/SDL_mixer/release/SDL2_mixer-2.0.1.tar.gz
    wget https://www.libsdl.org/projects/SDL_net/release/SDL2_net-2.0.1.tar.gz
    wget https://www.libsdl.org/projects/SDL_ttf/release/SDL2_ttf-2.0.14.tar.gz
    
    tar xf SDL2-2.0.5.tar.gz
    tar xf SDL2_image-2.0.1.tar.gz
    tar xf SDL2_mixer-2.0.1.tar.gz
    tar xf SDL2_net-2.0.1.tar.gz
    tar xf SDL2_ttf-2.0.14.tar.gz
    
    ln -s SDL2-2.0.5 SDL2
    ln -s SDL2_image-2.0.1 SDL2_image
    ln -s SDL2_mixer-2.0.1 SDL2_mixer
    ln -s SDL2_net-2.0.1 SDL2_net
    ln -s SDL2_ttf-2.0.14 SDL2_ttf
  • Start with a minimal build:

    cd /usr/src/SDL2/
    cd build-scripts/
    #hg revert --all  # remove traces of previous builds
    # edit androidbuild.sh and modify $ANDROID update project --target android-XX
    ./androidbuild.sh org.libsdl /dev/null
    # doesn't matter if the actual build fails, it's just for setup
    cd ../build/org.libsdl/
  • Remove reference to our dummy file:

    rm -rf jni/src/
  • Reference SDL_image, SDL_mixer, SDL_ttf, and their dependencies, as NDK modules:

    ln -s /usr/src/SDL2_image jni/
    ln -s /usr/src/SDL2_image/external/libwebp-0.3.0 jni/webp
    ln -s /usr/src/SDL2_mixer jni/
    ln -s /usr/src/SDL2_mixer/external/libmikmod-3.1.12 jni/libmikmod
    ln -s /usr/src/SDL2_mixer/external/smpeg2-2.0.0 jni/smpeg2
    ln -s /usr/src/SDL2_net jni/
    ln -s /usr/src/SDL2_ttf jni/
  • Optionnaly edit jni/Android.mk to disable some formats, e.g.:

    SUPPORT_MP3_SMPEG := false
    include $(call all-subdir-makefiles)
  • Launch the build!

    ndk-build -j$(nproc)

Note: no need to add System.loadLibrary calls in SDLActivity.java, your application will be linked to them and Android's ld-linux loads them automatically.

4.2. Install SDL in a GCC toolchain

Now:

  • Copy the NDK into a traditional GCC toolchain (leave android-14 as-is):

    /usr/src/android-ndk-r8c/build/tools/make-standalone-toolchain.sh \
      --platform=android-14 --install-dir=/usr/src/ndk-standalone-14-arm --arch=arm
  • Set your PATH (important, do it before any build):

    NDK_STANDALONE=/usr/src/ndk-standalone-14-arm
    PATH=$NDK_STANDALONE/bin:$PATH
  • Install the SDL2 binaries in the toolchain:

    cd /usr/src/SDL2/build/org.libsdl/
    for i in libs/armeabi/*; do ln -nfs $(pwd)/$i $NDK_STANDALONE/sysroot/usr/lib/; done
    mkdir $NDK_STANDALONE/sysroot/usr/include/SDL2/
    \cp jni/SDL/include/* $NDK_STANDALONE/sysroot/usr/include/SDL2/
    \cp jni/*/SDL*.h $NDK_STANDALONE/sysroot/usr/include/SDL2/
  • Install pkg-config and install a host-triplet-prefixed symlink in the PATH (auto-detected by autoconf):

    VERSION=0.9.12
    cd /usr/src/
    wget http://rabbit.dereferenced.org/~nenolod/distfiles/pkgconf-$VERSION.tar.gz
    tar xf pkgconf-$VERSION.tar.gz
    cd pkgconf-$VERSION/
    mkdir native-android/ && cd native-android/
    ../configure --prefix=$NDK_STANDALONE/sysroot/usr
    make -j$(nproc)
    make install
    ln -s ../sysroot/usr/bin/pkgconf $NDK_STANDALONE/bin/arm-linux-androideabi-pkg-config
    mkdir $NDK_STANDALONE/sysroot/usr/lib/pkgconfig/
  • Install pkg-config .pc files for SDL:

    cat <<'EOF' > $NDK_STANDALONE/sysroot/usr/lib/pkgconfig/sdl2.pc
    prefix=/usr/src/ndk-standalone-14-arm/sysroot/usr
    exec_prefix=${prefix}
    libdir=${exec_prefix}/lib
    includedir=${prefix}/include
    Name: sdl2
    Description: Simple DirectMedia Layer is a cross-platform multimedia library designed to provide low level access to audio, keyboard, mouse, joystick, 3D hardware via OpenGL, and 2D video framebuffer.
    Version: 2.0.5
    Requires:
    Conflicts:
    Libs: -lSDL2
    Cflags: -I${includedir}/SDL2   -D_REENTRANT
    EOF
    
    cat <<'EOF' > $NDK_STANDALONE/sysroot/usr/lib/pkgconfig/SDL2_image.pc
    prefix=/usr/src/ndk-standalone-14-arm/sysroot/usr
    exec_prefix=${prefix}
    libdir=${exec_prefix}/lib
    includedir=${prefix}/include
    Name: SDL2_image
    Description: image loading library for Simple DirectMedia Layer
    Version: 2.0.1
    Requires: sdl2 >= 2.0.0
    Libs: -L${libdir} -lSDL2_image
    Cflags: -I${includedir}/SDL2
    EOF
    
    cat <<'EOF' > $NDK_STANDALONE/sysroot/usr/lib/pkgconfig/SDL2_mixer.pc
    prefix=/usr/src/ndk-standalone-14-arm/sysroot/usr
    exec_prefix=${prefix}
    libdir=${exec_prefix}/lib
    includedir=${prefix}/include
    Name: SDL2_mixer
    Description: mixer library for Simple DirectMedia Layer
    Version: 2.0.1
    Requires: sdl2 >= 2.0.0
    Libs: -L${libdir} -lSDL2_mixer
    Cflags: -I${includedir}/SDL2
    EOF
    
    cat <<'EOF' > $NDK_STANDALONE/sysroot/usr/lib/pkgconfig/SDL2_net.pc
    prefix=/usr/src/ndk-standalone-14-arm/sysroot/usr
    exec_prefix=${prefix}
    libdir=${exec_prefix}/lib
    includedir=${prefix}/include
    Name: SDL2_net
    Description: net library for Simple DirectMedia Layer
    Version: 2.0.1
    Requires: sdl2 >= 2.0.0
    Libs: -L${libdir} -lSDL2_net
    Cflags: -I${includedir}/SDL2
    EOF
    
    cat <<'EOF' > $NDK_STANDALONE/sysroot/usr/lib/pkgconfig/SDL2_ttf.pc
    prefix=/usr/src/ndk-standalone-14-arm/sysroot/usr
    exec_prefix=${prefix}
    libdir=${exec_prefix}/lib
    includedir=${prefix}/include
    Name: SDL2_ttf
    Description: ttf library for Simple DirectMedia Layer with FreeType 2 support
    Version: 2.0.14
    Requires: sdl2 >= 2.0.0
    Libs: -L${libdir} -lSDL2_ttf
    Cflags: -I${includedir}/SDL2
    EOF

4.3. Building other dependencies

You can add any other libraries (e.g.: SDL2_gfx, freetype, gettext, gmp...) using commands like:

mkdir cross-android/ && cd cross-android/
../configure --host=arm-linux-androideabi --prefix=$NDK_STANDALONE/sysroot/usr \
  --with-some-option --enable-another-option \
  --disable-shared
make -j$(nproc)
make install

Static builds (--disable-shared) are recommended for simplicity (no additional .so to declare).

Example with SDL2_gfx:

VERSION=1.0.3
wget http://www.ferzkopp.net/Software/SDL2_gfx/SDL2_gfx-$VERSION.tar.gz
tar xf SDL2_gfx-$VERSION.tar.gz
mv SDL2_gfx-$VERSION/ SDL2_gfx/
cd SDL2_gfx/
mkdir cross-android/ && cd cross-android/
../configure --host=arm-linux-androideabi --prefix=$NDK_STANDALONE/sysroot/usr \
  --disable-shared --disable-mmx
make -j$(nproc)
make install

You can compile YOUR application using this technique, with some more steps to tell Android how to run it using JNI.

4.4. Build your autotools app

First, prepare an Android project:

  • Copy and adapt the /usr/src/SDL2/android-project skeleton as explained in README-android.md. You can leave it as-is in a first step.

  • Make links to the SDL binaries as well:

    mkdir -p libs/armeabi/
    for i in /usr/src/SDL2/build/org.libsdl/libs/armeabi/*; do ln -nfs $i libs/armeabi/; done

Make your project Android-aware:

  • Add /usr/src/SDL2/src/main/android/SDL_android_main.c in your project (comment out the line referencing "SDL_internal.h"). Compile it as C (not C++).

  • In your configure.ac, detect Android:

    AM_CONDITIONAL(ANDROID, test "$host" = "arm-unknown-linux-androideabi")
  • In your Makefile.am, tell Automake you'll build executables as libraries, using something like:

    if ANDROID
      # Build .so JNI libs rather than executables
      AM_CFLAGS = -fPIC
      AM_LDFLAGS += -shared
      COMMON_OBJS += SDL_android_main.c
    endif
  • Cross-compile your project using the GCC toolchain environment we created:

    PATH=$NDK_STANDALONE/bin:$PATH
    mkdir cross-android/ && cd cross-android/
    ../configure --host=arm-linux-androideabi \
      --prefix=/android-aint-posix \
      --with-your-option --enable-your-other-option ...
    make
  • Do this again for any additional arch you want to support (TODO: see how to support armeabi-v7a and document what devices support it); something like:

    mkdir cross-android-v7a/ && cd cross-android-v7a/
    # .o: -march=armv5te -mtune=xscale -msoft-float -mthumb  =>  -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=softfp -mthumb
    # .so: -march=armv7-a -Wl,--fix-cortex-a8
    CFLAGS="-g -O2 -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=softfp -mthumb" LFDLAGS="-march=armv7-a -Wl,--fix-cortex-a8" \
      ../configure --host=arm-linux-androideabi \
      ...

Now you can install your pre-built binaries and build the Android project:

  • Copy your program in android-project/libs/armeabi/libmain.so.

  • Build your Android .apk:

    android update project --name your_app --path . --target android-XX
    ant debug
    ant installd
  • You can run the application remotely:

    adb shell am start -a android.intenon.MAIN -n org.libsdl.app/org.libsdl.app.SDLActivity  # replace with your app package
  • Your SDL2 Android app is running!

4.5. Build your CMake app

(Work In Progress)

You can use our Android GCC toolchain using a simple toolchain file:

# CMake toolchain file
SET(CMAKE_SYSTEM_NAME Linux)  # Tell CMake we're cross-compiling
include(CMakeForceCompiler)
# Prefix detection only works with compiler id "GNU"
CMAKE_FORCE_C_COMPILER(arm-linux-androideabi-gcc GNU)
SET(ANDROID TRUE)

You then call CMake like this:

PATH=$NDK_STANDALONE/bin:$PATH
cmake \
  -D CMAKE_TOOLCHAIN_FILE=../android_toolchain.cmake \
  ...

5. Troubleshootings

If ant installd categorically refuses to install with Failure [INSTALL_FAILED_INSUFFICIENT_STORAGE], even if you have free local storage, that may mean anything. Check logcat first:

adb logcat

If the error logs are not helpful (likely ;')) try locating all past traces of the application:

find / -name "org...."

and remove them all.

If the problem persists, you may try installing on the SD card:

adb install -s bin/app-debug.apk


If you get in your logcat:

SDL: Couldn't locate Java callbacks, check that they're named and typed correctly

this probably means your SDLActivity.java is out-of-sync with your libSDL2.so.

None: Android (last edited 2018-11-21 16:00:50 by SylvainBeucler)

Feedback
Please include your contact information if you'd like to receive a reply.
Submit