Three Ways to Use Android NDK Cross Compiler

Today I was asked to help compile an Android executable from C source file on Mac OS X platform. Of course it is a cross compile task. Since I have Android NDK installed on my Mac, I thought it would be nice if I can make good use of that. I have used Android NDK to compile shared libraries for Android, so I would like to summary the possible ways to compile an Android executable using Android NDK.

Note: While writing this article, I found the official guide from Andriod was very nice and easy to read. Some of the examples are borrowed from there for the summary purpose.

1. Use the ndk-build and an Android.mk with BUILD_EXECUTABLE

The Android.mk and the Application.mk files are really tiny GNU makefile fragments that the build system parses once or more. The Android.mk file is useful for defining project-wide settings that Application.mk, the build system, and your environment variables leave undefined. It can also override project-wide settings for specific modules.

The Android.mk must resides in a subdirectory of your project’s $PROJECT/jni/ directory, and describes your sources and shared libraries to the build system. The Application.mk also usually resides under$PROJECT/jni/. We can also place it under a sub-directory of the top-level $NDK/apps/ directory. Example of using ndk-build, an Android.mk and an Application.mk with BUILD_EXECUTABLE: # Filename: Android.mk LOCAL_PATH :=$(call my-dir)

include $(CLEAR_VARS) LOCAL_MODULE := foo LOCAL_SRC_FILES := foo.c include$(BUILD_EXECUTABLE)
# Filename: Application.mk
APP_ABI: armeabi areabi-v7a

Then in the terminal, jut change directory to the $PROJECT/jni folder and invoke ndk-build:$ ndk-build
[armeabi] Compile thumb  : foo <= foo.c
[armeabi] Executable     : foo
[armeabi] Install        : foo => libs/armeabi/foo
[armeabi-v7a] Compile thumb  : foo <= foo.c
[armeabi-v7a] Executable     : foo
[armeabi-v7a] Install        : foo => libs/armeabi-v7a/foo

We are all set and the compiled executable is installed under $PROJECT/libs/<APP_ABI> now. 2. Use Standalone Toolchain You can use the toolchains provided with the Android NDK independently. Steps: 1. Selecting Your Toolchain. Before anything else, you need to decide which processing architecture your standalone toolchain is going to target. Each architecture corresponds to a different toolchain name, as Table 1 shows. Table 1. APP_ABI settings for different instruction sets. Architecture Value ARM-based arm-linux-androideabi-<gcc-version> x86-based x86-<gcc-version> MIPS-based mipsel-linux-android-<gcc-version> ARM64-based aarch64-linux-android-<gcc-version> x86-64-based x86_64-<gcc-version> MIPS64-based mips64el-linux-android--<gcc-version> 2. Selecting Your Sysroot The next thing you need to do is define your sysroot (A sysroot is a directory containing the system headers and libraries for your target). To define the sysroot, you must must know the Android API level you want to target for native support; available native APIs vary by Android API level. Native APIs for the respective Android API levels reside under$NDK/platforms/; each API-level directory, in turn, contains subdirectories for the various CPUs and architectures. The following example shows how to define a sysroot for a build targeting Android 5.0 (API level 21), for ARM architecture:

SYSROOT=$<ANDROID_NDK>/platforms/android-21/arch-arm 3. Invoking the Compiler The simplest way to build is by invoking the appropriate compiler directly from the command line, using the --sysroot option to indicate the location of the system files for the platform you’re targeting. For example: export CC="$<ANDROID_NDK>/toolchains/arm-linux-androideabi-<gcc-version>/prebuilt/ \
<your_machine_arch>/bin/arm-linux-androideabi-gcc-<gcc-version> --sysroot=$SYSROOT"$CC -o foo.o -c foo.c

Caveat

for Clang, you need to perform an additional two steps:

• Add the appropriate -target for the target architecture, as Table 2 shows.

• Add assembler and linker support by adding the -gcc-toolchain option, as in the following example:

-gcc-toolchain $NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64 Ultimately, a command to compile using Clang might look like this: export CC="$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/ \
linux-x86/bin/arm-linux-androideabi-gcc-4.8 --sysroot=$SYSROOT" -target \ armv7-none-linux-androideabi \ -gcc-toolchain$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64"
$CC -o foo.o -c foo.c 3. Make a Ready-to-use Standalone Toolchain (advanced) The NDK provides the make-standalone-toolchain.sh shell script to allow you to perform a customized toolchain installation from the command line. The script is located in the$NDK/build/tools/ directory, where $NDK is the installation root for the NDK. An example of the use of this script appears below:$NDK/build/tools/make-standalone-toolchain.sh \
--arch=arm --platform=android-21 --install-dir=/tmp/my-android-toolchain

This command creates a directory named /tmp/my-android-toolchain/, containing a copy of the android-21/arch-arm sysroot, and of the toolchain binaries for a 32-bit ARM architecture.

Note that the toolchain binaries do not depend on or contain host-specific paths, in other words, you can install them in any location, or even move them if you need to.

By default, the build system uses the 32-bit, ARM-based GCC 4.8 toolchain. You can specify a different value, however, by specifying --arch=<toolchain> as an option. Alternatively, you can use the --toolchain=<toolchain> option. See Tables on Toolchain for more details.

You can make these settings directly, as in the following example:

export PATH=/tmp/my-android-toolchain/bin:\$PATH
export CC=arm-linux-androideabi-gcc   # or export CC=clang
export CXX=arm-linux-androideabi-g++  # or export CXX=clang++

Note that if you omit the -install-dir option, the make-standalone-toolchain.sh shell script creates a tarball in tmp/ndk/<toolchain-name>.tar.bz2. This tarball makes it easy to archive, as well as to redistribute the binaries.

This standalone toolchain provides an additional benefit, as well, in that it contains a working copy of a C++ STL library, with working exceptions and RTTI support. For more options and details, use --help or go to NDK Guides -> Standalone Toolchain.

This work by Zengwen Yuan is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
comments powered by Disqus