Since the revision 5 of the Native Development Kit previously discussed, the NDK can be customized to obtain a standard GCC cross compiler toolchain. This means that you can use it with standard Makefile and make command, or with more sophisticated build tools like automake, CMake or Bakefile. No more Android.mk ant Application.mk proprietary format required ! This is great in many situation where you want to recompile a complex software with big libraries. Lets have a try !
For this experimental article, I’ll assume that you already have good knowledge of Android development, SDK, ADB and some good understanding of C/C++ compilation workflow (gcc, make and Makefile).
- Android SDK (yes, the Java part !) : I’ll use API-level 5 (Android 2.0) but the minimum is API-level 3 (Android 1.5): “~/android-sdk-linux_x86/”
- Android NDK r5b (the C/C++ part): “~/android-ndk-r5b/”
- GNU Make 3.81 or later (“make” package under Ubuntu or Debian)
- GNU awk or equivalent (nawk says the documentation, I have mawk)
To make this work under Windows (XP or 7) you’ll need Cygwin 1.7 installed, but it’s not that simple (the standalone toolchain officially dos not support Cygwin…). For that reason I’ve decided to work on this entirely under Linux (latest Ubuntu in my case). I you want to try to make it work under Windows, you could exploit useful information from the following blog post http://www.pocketmagic.net/?p=1462 and from some other Internet sources.
So let’s start by reading the “/android-ndk-r5b/docs/STANDALONE-TOOLCHAIN.html“, and proceed directly with the instructions on section 3 “Invoking the compiler (the easy way)”. Under a Linux console, type:
~/android-ndk-r5b/build/tools/make-standalone-toolchain.sh --platform=android-5 --install-dir=$HOME/android-ndk-r5b/standalone-toolchain-api5
Then add this few lines to the end of your .bashrc file and restart a console (or type them directly on the console):
PATH=$PATH:$HOME/android-sdk-linux_x86/tools
PATH=$PATH:$HOME/android-sdk-linux_x86/platform-tools
PATH=$PATH:$HOME/android-ndk-r5b/standalone-toolchain-api5/bin/
Then, create a standard hello world “hello-ndk.c”:
#include <stdio.h> int main(void) { printf("Hello from NDK\n"); return 0; }
At this stage, compiling for Android works easily with the following command:
arm-linux-androideabi-gcc hello-ndk.c -o hello-ndk
Before testing it, we would like to add a bit of android API in it. You can easily complete it with a minimal Android log (logcat for Eclipse) :
#include <stdio.h> #include <android/log.h> #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "hello-ndk", __VA_ARGS__)) int main(void) { printf("Hello from NDK\n"); LOGI("Hello from NDK"); return 0; }
For this, you need to link explicitly with a the “liblog” library :
- arm-linux-androideabi-gcc hello-ndk.c -l log -o hello-ndk
Here is a Makefile to handle this with a simple “make” command (or other “make clean” and “make clean all” variants):
CC = arm-linux-androideabi-gcc CFLAGS = -Wall -g LDFLAGS = -llog SRC =hello-ndk.c OBJ =$(SRC:.c=.o) EXE =hello-ndk all: $(SRC) $(EXE) $(EXE): $(OBJ) $(CC) -o $@ $^ $(LDFLAGS) %.o: %.c $(CC) -o $@ -c $< $(CFLAGS) clean: rm -f *.o $(EXE)
This create the standalone executable hello-ndk, so if your device is rooted, you can use adb to copy and execute “hello-ndk” on it, like this:
% adb shell $ su # mkdir /data/tmp # chmod 777 /data/tmp # exit $ exit % adb push hello-ndk /data/tmp % adb shell $ /data/tmp/hello-ndk
You should get a “Hello from NDK” on the command line, and the same message on the logcat window of Eclipse (in green, info level).
For normal users that are not root (no “su” command), there is no way (oops, see edit bellow) of launching directly a native application under Android: you have to package it in a standard Android application .apk and use it from Java code. See http://gimite.net/en/index.php?Run%20native%20executable%20in%20Android%20App.
edit: look at the comment from pitypang, the path /data/local/tmp should work for an unrooted phone !
So we need to build instead a shared library, a “.so” file to be loaded by a Java application (through Java Native Interface, JNI). For this, we need to modify the Makefile like this :
CC = arm-linux-androideabi-gcc CFLAGS = -Wall -g LDFLAGS = -llog -shared SRC =hello-ndk.c OBJ =$(SRC:.c=.o) EXE =libhello-ndk.so
With this you will get a libhello-ndk.so shared library. This will do nothing interesting by itself; we need to invoke the native function from Java, or to use the NativeActivity.
I will update this post later to make this works.
Edit: look also at my post on CMake for Android
I get the following when I try this on Linux. Studied the stand alone toolchain document. Followed the easy mode. Tried even the hard mode they explain. All end up at the same results.
Nice tutorial though. Not much out there on this stuff.
arm-linux-androideabi-g++ –sysroot=/opt/android-8//sysroot/ test.o -L/opt/android-8//sysroot/usr//lib -Wl,–fix-cortex-a8 -Wl,-Map,.map -lz -llog -lGLESv2 -o libs/armeabi//libfoo.so
/opt/android-8/bin/../lib/gcc/arm-linux-androideabi/4.4.3/../../../../arm-linux-androideabi/bin/ld: warning: libcutils.so, needed by /opt/android-8//sysroot/usr//lib/libGLESv2.so, not found (try using -rpath or -rpath-link)
/opt/android-8/bin/../lib/gcc/arm-linux-androideabi/4.4.3/../../../../arm-linux-androideabi/bin/ld: warning: libEGL.so, needed by /opt/android-8//sysroot/usr//lib/libGLESv2.so, not found (try using -rpath or -rpath-link)
/opt/android-8//sysroot//usr/lib/crtbegin_dynamic.o: In function `_start’:
bionic/libc/arch-arm/bionic/crtbegin_dynamic.S:(.text+0x14): undefined reference to `main’
/opt/android-8//sysroot/usr//lib/libGLESv2.so: undefined reference to `android::egl_get_image_for_current_context(void*)’
Add -shared to LD_FLAGS fixed it
nice tutorial. what you say about needing root to execute native compiled code is not exactly correct though. I’m not sure about android phones I’ve not seen, but on all the ones I’ve seen (bunch of HTC, Motorola models), the /data/local/tmp/ is totally controllable by shell, so you can push a binary, make it executable with chmod and execute it just fine.
Thanks for the tip, I’ll need to try it !
nice tutorial.
I tried to execute the above example with ndk-r6b, I’m getting following link error
arm-linux-androideabi-gcc -o libhello-ndk.so hello-ndk.o -llog -shared
/home/apradeep/softwares/android-ndk-r6b/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/bin/../lib/gcc/arm-linux-androideabi/4.4.3/../../../../arm-linux-androideabi/bin/ld: crtbegin_so.o: No such file: No such file or directory
collect2: ld returned 1 exit status
make: *** [libhello-ndk.so] Error 1
I tried with ndk-r5b. It’s working fine.
Thankyou