From cab8f2be64ba379b638554a5a612e5f66f08402a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 18 Nov 2018 19:21:22 +0100 Subject: [PATCH] doc: document the new android_create_apk() macro. --- doc/changelog.dox | 2 + doc/platforms-android.dox | 232 ++++++++++++++++++++++++++++++-------- 2 files changed, 186 insertions(+), 48 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 6fddf7cf5..752aafce4 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -88,6 +88,8 @@ r>>) @subsection changelog-latest-buildsystem Build system +- Experimental support for creating Android APKs directly using CMake without + Gradle involved. See @ref platforms-android-apps for more information. - The `Magnum::AndroidApplication` target was missing a few dependency libraries when Magnum was used as a CMake subproject diff --git a/doc/platforms-android.dox b/doc/platforms-android.dox index 3bf9dbf94..9c74a0a5d 100644 --- a/doc/platforms-android.dox +++ b/doc/platforms-android.dox @@ -39,10 +39,13 @@ command-line tools, without Android Studio involved. At the very least you need to have Android SDK and Android NDK installed. Running console utilities and tests on the device don't need much more, in case -you want to develop actual applications, you need also Gradle and SDK platform + -SDK platform build tools for version of your choice. Gradle is able to download -all the dependencies on its own, however it's also possible to install system -packages for a cleaner setup. +you want to develop actual applications, you need also the SDK and a platform + +SDK platform build tools for version of your choice. + +For APK building it's possible to use either an experimental support in CMake +or the official way with Gradle. Gradle is able to download all the +dependencies on its own, however it's also possible to install system packages +for a cleaner setup. @note On ArchLinux it's the `gradle` package and the following AUR packages, adapt the version numbers as necessary: @@ -95,14 +98,6 @@ information about OpenGL testing. @section platforms-android-apps Building and installing graphics apps -Building of graphics applications is managed fully using Gradle, which also -builds your CMake project internally. It's possible to use other means such as -`ndk-build`, but CMake is the officially preferred way. The following guide -assumes you have Gradle installed in a system-wide location available in -@cb{.sh} $PATH @ce. See the [Gradle installation docs](https://docs.gradle.org/current/userguide/installation.html) -for more information, @ref platforms-android-gradlew "see below" if you want to -use the `gradlew` wrappers instead. - In case you don't have an OpenGL ES build set up yet, you need to copy `FindEGL.cmake` and `FindOpenGLES2.cmake` (or `FindOpenGLES3.cmake`) from the [modules/](https://github.com/mosra/magnum/tree/master/modules) directory in @@ -139,9 +134,134 @@ else() endif() @endcode -Then you need to create a `build.gradle` file that references your root -`CMakeLists.txt`. Assuming it's saved right next to your root `CMakeLists.txt`, -the most minimal version might look like this: +@subsection platforms-android-apps-manifest Android manifest file + +For Android you additionally need an `AndroidManifest.xml` file, which +describes various properties of the Android package. A minimal stripped-down +version is: + +@code{.xml-jinja} + + + + + + + + + + + + + +@endcode + +Replace @cb{.jinja} {{ package }} @ce with Java-like package name for your app +(in this case it could be e.g. @cpp "cz.mosra.magnum.my_application" @ce, for +example), @cb{.jinja} {{ app_name }} @ce with human-readable app name that's +displayed in the system (so e.g. @cpp "My Application" @ce) and finally the +@cb{.jinja} {{ lib_name }} @ce is name of the library that you compiled with +CMake, which in this case would be @cpp "my-application" @ce. + +The @cb{.xml} @ce says that +the minimal OpenGL ES version is 2.0, change it in case you require a different +version. Consult [the Android developer documentation](https://developer.android.com/guide/topics/manifest/manifest-intro.html) +for further information about the manifest file. + +With this set up, you have two options how to build the final APK, either using +plain CMake or using Gradle. + +@subsection platforms-android-apps-cmake Using CMake + +The `toolchains` repository contains an `UseAndroid.cmake` module that allows +you to create an APK in a significantly faster and simpler way than when using +Gradle. + +@attention This feature is in an experimental stage and at this time it doesn't + support compilation of any resources (such as icons) or Java sources. It + also builds only the ABI and configuration that corresponds to the + particular CMake build directory. It's thus recommended to employ this + approach for fast iteration during development alongside the classic Gradle + build @ref platforms-android-apps-gradle "described below" that will get + used for the final release builds. + +Download contents of the toolchains repository from https://github.com/mosra/toolchains +or add it as a Git submodule, add its module path to `CMAKE_MODULE_PATH` and +include the `UseAndroid` module. Wrapping it in a check for presence of +`CMAKE_ANDROID_NDK` will make it possible to have the pure CMake and a Gradle +build coexist --- because Gradle internally uses CMake 3.6 which doesn't know +about `CMAKE_ANDROID_NDK` yet. + +@code{.cmake} +if(CORRADE_TARGET_ANDROID AND CMAKE_ANDROID_NDK) + list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/toolchains/modules/") + include(UseAndroid) +endif() +@endcode + +On the first run, the macro will attempt to detect SDK location, Android Build +Tools version and Android Platfrom version and it prints them to the output +like this: + +@code{.shell-session} +$ cmake . +… +-- ANDROID_SDK not set, detected /opt/android-sdk +-- ANDROID_BUILD_TOOLS_VERSION not set, detected 27.0.3 +-- ANDROID_PLATFORM_VERSION not set, detected 27 +… +@endcode + +If you don't like what it detected (or the detection failed), edit these values +in CMake cache. After that, pass your library target name and location of the +`AndroidManifest.xml` file to @cmake android_create_apk() @ce. Continuing from +the above: + +@code{.cmake} +add_library(my-application SHARED MyApplication.cpp) +android_create_apk(my-application AndroidManifest.xml) +@endcode + +Note that even thought it doesn't make any sense, the `aapt` tool *demands* the +manifest file to be called exactly `AndroidManifest.xml`. It can be, however, +in any location. This will create an APK named `my-application.apk` and +additionally also provide a new target, `my-application-deploy`, that will use +`adb install -r` to install the built APK on the device. Building and uploading +the application can be then done in a single step: + +@code{.shell-session} +$ ninja my-application-deploy +[5/5] Installing my-application.apk +Success +@endcode + +(Or e.g. `cmake --build . --target my-application-deploy` if you don't use +Ninja.) + +For full compatibility with the Gradle build wrap the call again in a check for +`CMAKE_ANDROID_NDK` and put the manifest in the `src/main` subdirectory, where +Gradle expects it: + +@code{.cmake} +add_library(my-application SHARED MyApplication.cpp) +if(CMAKE_ANDROID_NDK) + android_create_apk(my-application src/main/AndroidManifest.xml) +endif() +@endcode + +@subsection platforms-android-apps-gradle Using Gradle + +Besides plain CMake and Gradle it's also possible to use the classic +`ndk-build`. but that's the least recommended way and it might not be supported +in newer NDK builds. The following guide assumes you have Gradle installed in a +system-wide location available in @cb{.sh} $PATH @ce. See the +[Gradle installation docs](https://docs.gradle.org/current/userguide/installation.html) +for more information, @ref platforms-android-gradlew "see below" if you want to +use the `gradlew` wrappers instead. + +Create a `build.gradle` file that references your root `CMakeLists.txt`. +Assuming it's saved right next to your root `CMakeLists.txt`, the most minimal +version might look like this: @code{.gradle} buildscript { @@ -207,38 +327,8 @@ android { The [official documentation](https://developer.android.com/studio/projects/gradle-external-native-builds.html#configure-gradle) contains a more complete overview of all possibilities. -Another important file is `src/main/AndroidManifest.xml`, which says some -properties about the Android package. The location is also important, it has to -be placed inside `src/main` subdirectory, *not* straight besides the -`build.gradle` file. A minimal stripped-down version is: - -@code{.xml-jinja} - - - - - - - - - - - - - -@endcode - -Replace @cb{.jinja} {{ package }} @ce with Java-like package name for your app -(in this case it could be e.g. @cpp "cz.mosra.magnum.my_application" @ce, for -example), @cb{.jinja} {{ app_name }} @ce with human-readable app name that's -displayed in the system (so e.g. @cpp "My Application" @ce) and finally the -@cb{.jinja} {{ lib_name }} @ce is name of the library that you compiled with -CMake, which in this case would be @cpp "my-application" @ce. - -The @cb{.xml} @ce says that -the minimal OpenGL ES version is 2.0, change it in case you require a different -version. Consult [the Android developer documentation](https://developer.android.com/guide/topics/manifest/manifest-intro.html) -for further information about the manifest file. +For the `AndroidManifest.xml` file, Gradle expects it to be placed inside the +`src/main` subdirectory, *not* straight besides the `build.gradle` file. With everything set up, you are now ready to build the project by simply executing the following from the directory with your `build.gradle`. During the @@ -310,7 +400,10 @@ cmake .. \ cmake --build . --target install @endcode -And the `build.gradle` for your app then looks like this: +The `build.gradle` for your app then looks like below. + +@attention Building multiple ABIs is currently not supported with the CMake + @cmake android_create_apk() @ce macro, using Gradle is the only way. @code{.gradle} buildscript { @@ -618,6 +711,49 @@ gradle build @section platforms-android-troubleshooting Troubleshooting +@subsection platforms-android-troubleshooting-signing-failed Signing the APK using CMake fails + +At the moment the location and passphrase for the Android signing keystore is +implicitly set to @cb{.sh} $HOME/.android/debug.keystore @ce with `android` as +a password, since that's the location (and password) that Gradle uses. If you +see output similar to this: + +@code{.shell-session} +[1/2] Signing my-application.apk +FAILED: my-application.apk +… +Failed to load signer "signer #1" +java.io.FileNotFoundException: /.android/debug.keystore +@endcode + +then you either don't have the keystore generated yet or it's in some other +location. Similar error can happen if the password is incrrect, in which case +it will say the following instead: + +@code{.shell-session} +Failed to load signer "signer #1" +java.io.IOException: Keystore was tampered with, or password was incorrect +@endcode + +Generating a debug keystore can be done by running through the Gradle build at +least once. It's also possible to create a keystore +[using Android Studio](https://developer.android.com/studio/publish/app-signing#generate-key) +or using the `keytool` utility that's bundled with Java SDK. + +The location and password is controlled via the `ANDROID_APKSIGNER_KEY` +variable, edit your CMake cache to point it to a different location or +password. Note that the command parameters have to be delimited by `;` instead +of a space. For example, setting the location to `/etc/android.keystore` with a +password `secret` can be done like this: + +@code{.sh} +cmake . -DANDROID_APKSIGNER_KEY="--ks;/etc/android.keystore;--ks-pass;pass:secret" +@endcode + +You can also add further arguments to `apksigner` here. See the +[official documentaton for apksigner](https://developer.android.com/studio/command-line/apksigner) +for details. + @subsection platforms-android-troubleshooting-cant-find Gradle CMake can't find dependencies Gradle by default searches only in the NDK install path. If you have your