13. June 2018

Android Studio NDK could not start mips64el-linux-android-strip

If you’re using Android Studio to build C++ application with NDK you might encounter following error during build:

org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:transformNativeLibsWithStripDebugSymbolForDebug'.
...
Caused by: java.io.IOException: Cannot run program ".../Android/Sdk/ndk-bundle/toolchains/mips64el-linux-android-4.9/prebuilt/linux-x86_64/bin/mips64el-linux-android-strip": 
error=2, No such file or directory

The problem is caused by the upgrade of NDK in Android Studio to NDK v17. This version has a different structure of tools. It’s not compatible with Android Experimental Plugin v0.11.

The directory which should contain binary of tools contains just file NOTICE-MIPS64:

This mips64el-linux-android-4.9 directory exists to make the NDK compatible with the Android
SDK's Gradle plugin, version 3.0.1 and earlier, which expects the NDK
to have a MIPS64 toolchain directory.

The solution is to download NDK v16 from NDK older releases. Extract it and replace former ndk-bundle directory.

Commands for macOS or Linux users:

cd ~/Android/Sdk
unzip ~/Downloads/android-ndk-r16b-linux-x86_64.zip
mv ndk-bundle ndk-bundle-v17
mv android-ndk-r16b ndk-bundle

If you’re Windows user the location of NDK is typically in your profile AppData\Local\Android. Do not forget to unblock the ZIP after downloading (right click, Properties, Unblock, Ok)

Commands for Windows users:

cd ~\AppData\Local\Android\Sdk
unzip ~\Downloads\android-ndk-r16b-windows-x86_64.zip
mv ndk-bundle ndk-bundle-v17
mv android-ndk-r16b ndk-bundle

Start Android Studio. It should prompt you to update NDK. Do not confirm this request, it will replace NDK with v17. You should see the following screen in SDK Manager:

You can find related sample source code at GitHub in sdl2-android-example – branch gradle-4-using-android-experimental-plugin repository. Further articles about SDL2 and Android are available under the tag SDL2.

Since Google is moving away from Gradle Android Experimental plugin you might consider using Gradle 5 + CMake. The migration is relatively easy and it could save you the trouble of being stuck with old NDK. You need to create CMakeLists.txt instead of build.gradle for C/C++ parts of the code. Example of migration is at GitHub – sdl2-android-example – PR#6.

23. September 2017

SDL2_ttf for Android with TrueType font support

Common library for displaying a text in SDL2 application is SDL2_ttf. The library is a wrapper on FreeType library. It is necessary to add both libraries into a project.

Let’s start with FreeType dependency. Add build.gradle file. Just be aware that there will be many excluded files. The library itself is very huge, and only a fraction of functions is necessary for the application.

...
sources {
      main {
          jni {
              source {
                  srcDir "src"
                  exclude "autofit"
                  exclude "smooth/smooth.c"
...
                   // Including this file in build causes duplications, because it includes directly C files
                   exclude "truetype/truetype.c"
                   exclude "type1"
                   exclude "type42"
                   exclude "winfonts"
              }
          }
      }
}

Adding SDL2_ttf is similar to other libraries like SDL2_jpeg or SDL2_mixer.

SDL2_ttf and FreeType modules should be also registered at settings.gradle:

include ':freetype'
...
include ':SDL2_ttf'

The first step to displaying text on the screen is to initialize the library and load a font:

#include "SDL_ttf.h"
...    
if (TTF_Init() == -1) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "TTF_Init: %s\n", TTF_GetError());
    return 7;
}

The next step is to load a font. The file should be stored in app/src/main/assets.

TTF_Font *font = TTF_OpenFont("blazed.ttf", 32);
if (!font) {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
                 "Unable to load font: %s\n", TTF_GetError());
    return 8;
}

The next step is to render a text:

SDL_Color textColor = { 255, 240, 0, 255 };
SDL_Surface* solid = TTF_RenderText_Solid(font, "SDL2 Android Example", textColor);

SDL_Texture* solidTexture = SDL_CreateTextureFromSurface(renderer, solid);
SDL_RenderCopy(renderer, solidTexture, NULL, &dstrect);
SDL_FreeSurface(solid);

Here is the result:

You can find the source code at GitHub in sdl2-android-example repository. Further articles about SDL2 and Android are available under the tag SDL2.

7. September 2017

Gradle Experimental Plugin Android error: Cannot invoke method dependencies()

Update: Engineers at Google fixed the issue. Thank you.

After initial setup of SDL2 application for Android, I decided to add one activity before launching SDL. So I created the new MainActivity which should launch SDLActivity after a tap on the button.

This time Gradle Experimental Plugin gave me a very fancy error: Cannot invoke method dependencies() on null object.

As always the error message does not provide too many hints how to solve the issue.

Android Studio automatically just appended a dependency into the Gradle file during creation of new MainActivity. So the code in build.gradle looked like this:

tasks.whenTaskAdded { task ->
    if (task.name.contains('compile')) {
        task.dependsOn ':main:distributeLib'
    }
}dependencies {
    compile 'com.android.support.constraint:constraint-layout:+'
}

Little bit weird code for dependencies. The solution was just to add a new line.

tasks.whenTaskAdded { task ->
    if (task.name.contains('compile')) {
        task.dependsOn ':main:distributeLib'
    }
}

dependencies {
    compile 'com.android.support.constraint:constraint-layout:+'
}

The whole project is available at github.com/georgik/sdl2-android-example project.

More articles about SDL2 for Android are available under tag sdl2.

3. September 2017

Difference between libraries and main Android application based on SDL2 built by Gradle 4

Gradle Android Experimental Plugin works without problem with Android Studio and it is the recommended approach use it when starting a new project with NDK.

It’s very easy to get started, but there are some traps. Let’s explore common mistake when writing Gradle build script for Android with NDK.

In case of an application which is based on C libraries, the goal is clear. Build libraries and then link everything into the final project.

The tricky part is the definition of dependencies. The definition for the main and for libraries is different. If you use the same style of definition you may run into error like: Android Studio is not able to import the project, because of missing .so file.

Here is the definition which you can find in build.gradle for the main module:

model {
     repositories {
        libs(PrebuiltLibraries) {
            SDL2 {
                headers.srcDir "../SDL2/include"
                binaries.withType(SharedLibraryBinary) {
                    sharedLibraryFile = file("${gradle.libDistributionRoot}/SDL2/lib/${targetPlatform.getName()}/libSDL2.so")
                }
            }
            SDL2_image {
                headers.srcDir "../SDL2_image/include"
                binaries.withType(SharedLibraryBinary) {
                    sharedLibraryFile = file("${gradle.libDistributionRoot}/SDL2_image/lib/${targetPlatform.getName()}/libSDL2_image.so")
                }
            }

        }
    }

   android {
        compileSdkVersion = gradle.sdkVersion
        buildToolsVersion = '25.0.3'

        defaultConfig {
            minSdkVersion.apiLevel = 13
            versionCode = 1
            versionName = '1.0'
        }
        ndk {
            moduleName = 'main'
            cppFlags.addAll([
                    "-I" + file("../SDL2/include/").absolutePath,
                    "-I" + file("../SDL2_image/include/").absolutePath,
            ])
            CFlags.addAll([
                    "-I" + file("../SDL2/include/").absolutePath,
                    "-I" + file("../SDL2_image/include/").absolutePath,
            ])
            stl "stlport_static"
        }

        sources {
            main {
                jni {
                    dependencies {
                        library 'SDL2' linkage 'shared'
                        library 'SDL2_jpeg' linkage 'shared'
                        library 'SDL2_png' linkage 'shared'
                        library 'SDL2_image' linkage 'shared'
                    }
                    source {
                        srcDirs 'src/'
                    }
                }
            }
        }

    }
}

If you look close enough, you’ll see that the first part of the file contains a definition of PrebuiltLibraries. These libraries are referencing directly the .so file. In the section dependencies, you can find reference to the declared dependencies.

Gradle will just build the app and it will grab all referenced .so files and put them into a final application. That is correct for the main module. When you’re writing build files for libraries, you should take a different approach.

Here is an example of SDL2_image/build.gradle file:

apply plugin: 'com.android.model.native'

model {

    android {
        compileSdkVersion = gradle.sdkVersion
        buildToolsVersion = '25.0.3'

        defaultConfig {
            minSdkVersion.apiLevel = 13
            versionCode = 1
            versionName = '1.0'
        }
        ndk {
            moduleName = 'SDL2_image'
            ldLibs.addAll(["GLESv1_CM", "EGL", "GLESv2", "log", "android", "dl"])
            CFlags.addAll(["-DGL_GLEXT_PROTOTYPES"])
            CFlags.addAll(["-I" + file("include/").absolutePath,
                           "-DGL_GLEXT_PROTOTYPES",
                           "-DLOAD_JPG",
                           "-DLOAD_PNG",
                           "-DLOAD_XPM"
            ])
        }

        sources {
            main {
                jni {
                    dependencies {
                        project ':SDL2' linkage 'shared'
                        project ':SDL2_jpeg' linkage 'shared'
                        project ':SDL2_png' linkage 'shared'
                    }
                    exportedHeaders {
                        srcDir "../SDL2/include"
                        srcDir "../SDL2_jpeg/include"
                        srcDir "../SDL2_png/include"
                    }
                    source {
                        srcDir "src"

                    }
                }
            }
        }

    }
}

Here you can see that there is no definition of PrebuiltLibraries, but the sources contain dependencies on projects of other libraries. In addition to that, there is exportedHeaders declaration which will tell the compiler where to find .h files. That is because you do not need any reference to .so dependencies when building .so library. You need just headers.

The different is very small in the dependencies section. It can simplify your build process if you use it correctly.

Here is an example of an error that occurs when the .so file is missing and library is referencing the file:

project refresh failed Error:Exception thrown while executing model rule: 
NdkComponentModelPlugin.Rules#createNativeBuildModel(NativeBuildConfig, ModelMap<androidbinaryinternal>, ModelMap<ndkabioptions>, NdkHandler) > create...

You can fix either build files or you can build missing libraries one by one.

The correct version of build files is available in sdl2-android-example project.

1. September 2017

SDL2 for Android API level 19

SDL2 example application works without problem with newer Androids API 21+. The problem was with API level 19. It took me some time to figure out reasons for very interesting errors.

Let’s examine error messages and investigate how to fix them.

When I’ve started sdl2-android-example on a device with API 19, it failed with error message: dlopen failed: cannot locate symbol “signal” referenced by libSDL2.so”

The root cause is not that obvious from the error message. It failed because the application has been built for API 21 instead of API 19.

The fixture is easy, just set sdkVersion to 19.

For this purpose, I’ve extracted sdkVersion to settings.gradle which can be found in the root directory of the project.

gradle.ext.sdkVersion = 19

Each module then has reference to this value:

model {
    android {
        compileSdkVersion = gradle.sdkVersion
...

Make complete clean and distributeLib to rebuild for proper API.

gradle clean dL

Hooray! Fixed. Let’s start the application.

Yey. New error: dlopen failed: could not load library “libSDL2_image.so” needed by “libmain.so”; caused by cannot locate symbol “png_set_longjmp_fn” referenced by “libSDL2_image.so”

The root cause of this issue is similar to the problem with JPEG described in the previous article. Paul Asiimwe found out that there is another library the name png and it has a different version so the application crashes.

To fix this issue it was necessary to rename whole png submodule to SDL2_png. Renaming requires clean and new distributeLib:

gradle clean dL

After these small tricks, the sdl2-android-example works even with API level 19.