Android.mk (Command Line)

From Crypto++ Wiki
Jump to navigation Jump to search

Android.mk and Application.mk refer to building the library from the command line using ndk-build and the Android standard build files. Android.mk and Application.mk is a separate project like Autotools and Cmake. The project files are located at GitHub | cryptopp-android.

The Android.mk and Application.mk are based on by Alex Afanasyev's early pull request. The pull request went unmerged because we did not want to add the directory structure to accommodate Android builds.

Since Android.mk and Application.mk are standard Android build files there is not much to using them. Android.mk and Application.mk produce three artifacts. The artifacts are libcryptopp_static.a, libcryptopp_shared.so and cryptest.exe. In practice you will probably only use libcryptopp_static.a. You must use libcryptopp_shared.so if two or more components use Crypto++.

The Android project files are maintained by the community, and not the library. If you want to use Android build system then download it from GitHub | cryptopp-android. If you have an improvement, then make the change and submit a pull request. It is a separate project to ensure folks don't accidentally use it. (Our unofficial Autotools and CMake projects are separate projects, too).

You should visit C++ Library Support in the Android docs. The Android docs provide a lot of important information that will help you use C++ effectively on Android. For example the docs recommend using ReLinker to relieve you of manually loading libraries in the correct order.

A related page is Android Setup (Command Line), which discusses how to setup an Android build machine. Another is Android (Command Line), which builds the library from the command line using GNUmakefile-cross. A second page of interest is Wrapper DLL, which discusses creating wrapper DLLs and shared objects that garbage collect unused symbols and use small export tables.

NDK Targets

The latest Android NDK's support four targets. According to ndk-r19c, they are listed below. You can specify one of them using APP_ABI.

  • x86
  • x86_64
  • armeabi-v7a
  • arm64-v8a

Android NDK's no longer support armeabi. If you are trying to use earlier NDK's, like ndk-r16c, then the library may not build correctly. Also see Issue 863, Android and neon_vector_type attribute is not supported for this target and Crypto++ and neon_vector_type attribute is not supported for this target using Android NDK? on Stack Overflow.

NDK Path

ndk-build is part of the Android NDK. The commands shown below were executed with the NDK and SDK on-path. Both ANDROID_NDK_ROOT and ANDROID_SDK_ROOT were set in the environment, and then a new path was exported with export PATH=$PATH:$ANDROID_NDK_ROOT:$ANDROID_SDK_ROOT/tools:$ANDROID_SDK_ROOT/platform-tools. ANDROID_SDK_ROOT is not needed for this wiki page.

Always set ANDROID_NDK_ROOT and ANDROID_SDK_ROOT because the Android tools use them internally. Also see Recommended NDK Directory? on the Android NDK mailing list. Also see Android Setup (Command Line).

Application.mk

Application.mk provides the applications settings. You should change the values to suit your taste. The build file is setup to build the library in place. If you drop Android.mk and Application.mk in an Eclipse or Android Studio project then you should delete the lines that set NDK_PROJECT_PATH and APP_BUILD_SCRIPT.

APP_ABI := all
APP_PLATFORM := android-21
APP_STL := c++_shared

CRYPTOPP_ROOT := $(call my-dir)
NDK_PROJECT_PATH := $(CRYPTOPP_ROOT)
APP_BUILD_SCRIPT := $(CRYPTOPP_ROOT)/Android.mk

Android.mk

Android.mk provides the recipes to build the library and test program. Application.mk is setup to build the library in place through the variable CRYPTOPP_PATH. The variable is empty so the concatenation of CRYPTOPP_PATH prepended to a source file like test.cpp results in test.cpp.

LOCAL_PATH := $(call my-dir)

CRYPTOPP_PATH :=
...

If the Crypto++ source files are located one directory up then you would use something like the following. The concatenation of CRYPTOPP_PATH prepended to a source file like test.cpp results in ../cryptopp/test.cpp.

LOCAL_PATH := $(call my-dir)

CRYPTOPP_PATH := ../cryptopp/
...

The rest of the file consists of source files in CRYPTOPP_SRC_FILES in CRYPTOPP_TEST_FILES, and the declarations to build the modules shared object, static library and cryptest.exe program.

ndk-build

ndk-build or Ant can be used to build the library from the command line. The documentation for the build tool is at ndk-build.

If you are building the library in place then your command line will be similar to the following.

cd cryptopp
ndk-build NDK_PROJECT_PATH="$PWD" NDK_APPLICATION_MK="$PWD/Application.mk" V=1

If you are building from Eclipse or Android Studio then drop Application.mk and Android.mk in the jni/ directory and let the IDE build the library for you.

Though Application.mk states APP_ABI := all you can build for a single architecture by overriding APP_ABI like shown below.

cd cryptopp
ndk-build APP_ABI=armeabi-v7a NDK_PROJECT_PATH="$PWD" NDK_APPLICATION_MK="$PWD/Application.mk" V=1

Activity

When you build your Java application or activity you should not link against libcryptopp_shared.so. The shared object includes nearly the entire library and it is large. libcryptopp_shared.so will load slowly because the export table is large and take up a lot of memory.

Instead you should build a wrapper shared object that links against the static archive libcryptopp_static.a. The linker will discard most of the [unused] symbols in the static archive so you will have a smaller and faster shared object that only includes what it needs from the library.

An example of a wrapper shared object is available at Android Activity. Below is a snippet that might be an exported function from your wrapper shared object that exports a "one-shot hash". The full wiki article is available at Wrapper DLL.

#include <sha.h>
#include <stdint.h>

#if __GNUC__ >= 4
  #define DLL_PUBLIC __attribute__ ((visibility ("default")))
  #define DLL_LOCAL  __attribute__ ((visibility ("hidden")))
#else
  #define DLL_PUBLIC
  #define DLL_LOCAL
#endif

extern "C" DLL_PUBLIC
int sha256_hash(uint8_t* digest, size_t dsize,
         const uint8_t* message, size_t msize)
{
    using CryptoPP::Exception;
    using CryptoPP::SHA256;

    try
    {
        SHA256().CalculateTruncatedDigest(digest, dsize, message, msize);
        return 0;  // success
    }
    catch(const Exception&)
    {
        return 1;  // failure
    }
}

Problems

There are several problems we are aware of when using Android.mk. Each is an instance problem of a bigger issue. The bigger issue is, the source files are a matched set and mixing them can cause problems. The problems below happen when versions of Crypto++ and cryptopp-android are mixed and matched.

NDK Version

The first problem with using Android.mk is the NDK version. Each version of Android.mk depends on a particular NDK or successive versions of the NDK. "Successive versions" means, for example, NDK r15 through NDK r17 or NDK r18 through NDK r20.

The Android Project changes too many things too frequently and it often breaks our scripts.

setenv-android.sh

The second problem with using Android.mk is setenv-android.sh. Each version of setenv-android.sh depends on a particular NDK or successive versions of the NDK. "Successive versions" means, for example, NDK r15 through NDK r17 or NDK r18 through NDK r20.

The Android Project changes too many things too frequently and it often breaks our scripts.

GNUmakefile-cross

The third problem with using Android.mk is GNUmakefile-cross. Source files change from one version of the library to another. Source files are added and deleted, and an old version of the library is probably out of sync with GNUmakefile-cross.

Android.mk

The fourth problem with using Android.mk is Android.mk. Source files change from one version of the library to another. Source files are added and deleted, and an old version of the library is probably out of sync with Android.mk.

Source files

In general source files change from one version of the library to another. Source files are added and deleted, and an old version of the library is probably out of sync with Android.mk. An example of this can be seen a Issue #1, Test executable fails to build.

defines

This is similar to source file changes, but it is also its own little unique problem. #defines will change to make the library easier to use for users. For example, Crypto++ 8.1 used a compile time feature test to set CRYPTOPP_DISABLE_MIXED_ASM for Clang. Crypto++ 8.3 unconditionally sets CRYPTOPP_DISABLE_MIXED_ASM for Clang. When using Crypto++ 8.1 sources you must manually set CRYPTOPP_DISABLE_MIXED_ASM for NDK builds.

Workaround

The best workaround we have is use:

  • latest NDK and SDK
  • matched Crypto++ and cryptopp-android

A matched Crypto++ and cryptopp-android means using, for example, Crypto++ Master and cryptopp-android Master. cryptopp-android Master follows Crypto++ Master and there should not be problems.

Older platforms are trickier. For example, Issue #1, Test executable fails to build, used NDK r20, Crypto++ 8.1 and cryptopp-android Master. The problem was, Crypto++ 8.1 setenv-android.sh was broke for NDK r20, and recent check-ins added another break.

To fix the Crypto++ 8.1, start with:

  • continue to use NDK r20
  • download and unzip Crypto++ 8.1
  • download setenv-android.sh from Crypto++ Master
  • download GNUmakefile-cross from Crypto++ Master
  • download Android.mk (and friends) from cryptopp-android Master
  • patch existing files
  • fix missing files

"Patch existing files" is a bit generic and open-ended. CRYPTOPP_DISABLE_MIXED_ASM is an example of a define that needs to be set.

"Fix missing files" is a bit generic and open-ended. For example, if you use Master's Android.mk with Crypto++ 8.1 then you will get an error similar to "no recipe to build xts.o" because XTS was recently added, and it is not present in Crypto++ 8.1. The fix is to remove xts.h and xts.cpp from Android.mk.

Issue #1, Test executable fails to build also had a trickier fix. Crypto++ Master switched from 32-bit to 64-bit for Donna curves on 64-bit platforms, so symbols went missing. The fix was to go back to using 32-bit curves for Donna.

If you have a compile error after downloading the latest components and removing missing files, then open a bug report.

Downloads

cryptopp-android - GitHub project with cryptopp-android project files.

android-prng - GitHub project with Android Activity which uses the Crypto++ shared object.