Android C native development using the NDK under Windows

A lot has changed since my previous article, on Android C native development, back in 2009.
The Android NDK has been released and the JNI development is not only possible, but also easy. Have a look on this article on the Android NDK.
The new challenge is to use the NDK to compile Native ARM-EABI executables that would run on an Android phone. I don’t want to setup the complete Google Android source code as I did in my first article.

This is possible, and I will provide the steps to do it:
Step 1. Install the NDK
Step 2. Install Cygwin
(as presented in the Android NDK Article)
While this guide is for Windows, you can easily adapt the following for a Linux box or a Mac.

Create a simple C code file, test.c

#include 

int main()
{
	printf("Hello Google Android world!\nwww.pocketmagic.net\n");
	return 1;
	exit(0);
}

Find the following locations:
NDK_ROOT (eg. D:\work_code\android\android-ndk-r4b\ )
NDK_ROOT\build\platforms\
NDK_ROOT\build\prebuilt\windows\arm-eabi-4.2.1\
You will need them.

When starting Cygwin using the Cygwin.bat BATCH File, make sure you define the PATH to the Android NDK:

set PATH=D:/work_code/android/android-sdk-windows/tools;D:/work_code/android/android-ndk-r4b;D:\work_code\android\android-ndk-r4b\build\prebuilt\windows\arm-eabi-4.2.1\bin\;$PATH;D:\work_code\android\android-ndk-r4b\build\platforms\android-5\arch-arm\usr\lib\;

Step 3. If you read my Android NDK article, you already know the NDK folder contains the ndk-build script, that is used to compile the JNI code.
We will build a similar script, to compile our test.c code file.

Let’s call this file ndk-comp and place it where ndk-build is.

#!/bin/sh 

OS='windows'
ANDROIDSDK='android-3'

PROGDIR=`dirname $0`
PROGDIR=`cd $PROGDIR && pwd`

ARMEABIGCC=$PROGDIR/build/prebuilt/$OS/arm-eabi-4.2.1/bin/arm-eabi-gcc
ARMEABILIB=$PROGDIR/build/platforms/$ANDROIDSDK/arch-arm/usr/lib
ARMEABIINC=$PROGDIR/build/platforms/$ANDROIDSDK/arch-arm/usr/include
ARMEABICRT=$PROGDIR/build/platforms/$ANDROIDSDK/arch-arm/usr/lib/crtbegin_dynamic.o

LINKER=/system/bin/linker

echo "GCC:"$ARMEABIGCC "LIB:"$ARMEABILIB "LINKER":$LINKER "PARAMS:"$@

$ARMEABIGCC $@ -Wl,-rpath-link=$ARMEABILIB,-dynamic-linker=$LINKER -L$ARMEABILIB $ARMEABICRT -I$ARMEABIINC -nostdlib -lc  

If you did everything correctly, you should be able to call this script from within Cygwin’s shell. Using cd command, navigate in Cygwin shell to the test.c folder. Compile it using ndk-comp -o test test.c :
compile android native c file using ndk 1
As you can see, running it on the device using ADB works. Here is another picture showing the executable started using the Terminal app, directly on the Android smartphone:
compile android native c file using ndk 2
It works well.

There is another even better way of compiling native C apps for Android using the NDK, as presented by Mamadou on his blog.
You can create a Makefile in the same folder with test.c then navigate there with the Cygwin shell and run make.
The Makefile is as follows:

APP := test
ROOT:=/cygdrive/d/work_code/android
NDK_PLATFORM_VER := 3
INSTALL_DIR := /data/tmp

ANDROID_NDK_ROOT:=$(ROOT)/android-ndk-r4b
ANDROID_NDK_HOST:=windows
ANDROID_SDK_ROOT:=$(ROOT)/android-sdk-windows
PREBUILD:=$(ANDROID_NDK_ROOT)/build/prebuilt/$(ANDROID_NDK_HOST)/arm-eabi-4.4.0
BIN := $(PREBUILD)/bin

CPP := $(BIN)/arm-eabi-g++
CC := $(BIN)/arm-eabi-gcc
CFLAGS := -I$(ANDROID_NDK_ROOT)/build/platforms/android-$(NDK_PLATFORM_VER)/arch-arm/usr/include
LDFLAGS := -Wl,--entry=main,-rpath-link=$(ANDROID_NDK_ROOT)/build/platforms/android-$(NDK_PLATFORM_VER)/arch-arm/usr/lib,-dynamic-linker=/system/bin/linker -L$(ANDROID_NDK_ROOT)/build/platforms/android-$(NDK_PLATFORM_VER)/arch-arm/usr/lib
LDFLAGS += -nostdlib -lc -disable-multilib


all: $(APP)

OBJS += $(APP).o

$(APP): $(OBJS)
	$(CPP) $(LDFLAGS) -o $@ $^

%.o: %.c
	$(CC) -c $(INCLUDE) $(CFLAGS) $< -o $@ 
install: $(APP)
	$(ANDROID_SDK_ROOT)/tools/adb push $(APP) $(INSTALL_DIR)/$(APP) 
	$(ANDROID_SDK_ROOT)/tools/adb shell chmod 777 $(INSTALL_DIR)/$(APP)

shell:
	$(ANDROID_SDK_ROOT)/tools/adb shell

run:
	$(ANDROID_SDK_ROOT)/tools/adb shell $(INSTALL_DIR)/$(APP)

r: $(APP)
	$(ANDROID_SDK_ROOT)/tools/adb push $(APP) $(INSTALL_DIR)/$(APP) 
	$(ANDROID_SDK_ROOT)/tools/adb shell chmod 777 $(INSTALL_DIR)/$(APP)
	$(ANDROID_SDK_ROOT)/tools/adb shell $(INSTALL_DIR)/$(APP)
	
clean:
	@rm -f $(APP).o $(APP)

Makefile
If you copy paste this, make sure you replace the spaces before "$(CPP) $(LDFLAGS.." and the other paragraphs with one TAB. Not doing so will result in an error:

$ make
Makefile:24: *** missing separator.  Stop.

ITX@itxserv /cygdrive/d/work_code/android/TBTS/Native

If everything is in place, you can use the following defined commands:
make clean - will delete all object files and prepare for a new build
make - will compile your code
make r - copies the compiled binary to your Android device, it sets executable permissions and launches it.

Here's an image showing that:
compile android native c file using ndk 3

For some reason this binary produces a segmentation fault, just before it terminates. I didn;t have the time to look into this, but Mamadou also had the error. For the moment I'd suggest you stick to my first script.

This article has 21 Comments

  1. Your method also has the benefit of working with the sourcery toolchain on older glibcs (the current android ndk will not work on glibc<2.11, eg, fedora 11).

  2. I modified your script a bit to make it work with ndk r5:
    http://ideone.com/lt6BW

    I did run into a problem while compiling a specific project though:
    http://ideone.com/QCj2G

    Seems that there’s a problem with a library somewhere, but I’m only getting started with C and GCC so I’d appreciate it if you could look for a workaround for.

    Thanks 🙂

  3. How does any of this change with NDK r5 having a script to build a standalone tool chain?

    Reading the docs indicates that the built tool chain tools don’t support cygwin drive/path naming scheme and that the NDK build tools normally handle this so if you use your own build system under cygwin you would have to clean this up. I’m not sure I’m following this since the example above uses a build system separate from NDK.

  4. It works on the emulator but when I try to run it on the phone, it says link_image[1995]: failed to link /data/local/test CANNOT LINK EXECUTABLE

  5. hacker:
    April 9th, 2011 at 9:14 pm

    It works on the emulator but when I try to run it on the phone, it says link_image[1995]: failed to link /data/local/test CANNOT LINK EXECUTABLE

    Maybe it’s not working because it needs to be compiled against the same sdk version as your phone? Dunno if you did that or not, cant think of anything else

  6. I don’t usually give up on something very easily. And this… this article… does NOT WORK AT ALL! OK, so I just wasted about 4 hours trying to download, extract, configure, DEBUG, DEBUG, TRACE, DEBUG, SEARCH, DEBUG SOME MORE… read, read, read, check, check, POP SOME FREAKING PAIN PILLS… No. This does NOT work.

    For one thing, the article is outdated… you don’t freakin’ get the old r4b version anymore, I get r5c. Completely different paths. Chaining together that OBSCENELY LONG “path”? Made my head pound. Swapping all the backslashes for forwards. Referencing the relative “/home” path (the HELL am I putting that crap in my C: root, it goes on F: with the rest of the Cygwin sprawling mess). Does it work? No. Does the file exist? YES. IT DOES. Not found: crtbegin_dynamic.o. Not found. Not found. Not found. File does not exist. FILE IS RIGHT FREAKING THERE, right where it says it doesn’t exist. That exact string it quotes. It exists. It says it doesn’t.

    I swear to god, I loathe the Linux world, with all my nerdraeg. So much broken code, sleazy hacks, shortsighted scripting, careless changes, and COMPLETE lack of organization. Utter chaos. God, it pisses me off so much that I’m sitting here having my computer tell me that a file DOESN’T exist, and there is no information anywhere online for this stuff. You know what? This kind of crap happens every time I try to do something cool with Android, too.

    I think I’ll go back to compiling a native build environment on the phone itself, thanks. I don’t mind waiting for my phone to compile its own programs. At least I know it’ll work, and I don’t have to trash my own PC with this Cygwin bloatware in order to make a single damn compile!

  7. Great article, I used the (to date) latest NDK r6 and built the sample on a Ubuntu 11.04 host. My target Android platform is a BeagleBoard (OMAP TI) w/pre-built Android 2.3.4 (Gingerbread) available from TI’s site. This saved me a lot of time sifting through the NDK documentation, thumbs up!

  8. Hi Radu,

    Sure no problem would be glad to post a pic (at least in return!). Not sure how to post a pic here? Just let me know how you prefer and an example. I’ll take a picture showing adb shell’s execution of the app (or can also show it on the Android Terminal app?).

    Quick question if you don’t mind. I also have your NativeScreen Capture working (partially….) as well on my Beagleboard (rev c4). However the framebuffer is using 32 bpp, as I’m using the DVI output for video display. So you have only conversion for 24, so I need to add 32 bpp support I just haven’t researched the changes yet. I think it’d be good to include as everyone else out there who has the Beagleboard will probably thank you 🙂 If you have the code for it I can test it immediately or guide me through the changes. I will also work on it.

    FYI – I dumped out some runtime data from your program running on the Beagleboard:

    # ./fb_grabber

    vi.xres = 640
    vi.yres = 480
    vi.xresv = 640
    vi.yresv = 2880
    vi.xoff = 0
    vi.yoff = 2400
    vi.bits_per_pixel = 32
    fi.line_length = 2560
    main() – 4
    vi.xres = 640
    vi.yres = 480
    vi.xresv = 640
    vi.yresv = 2880
    vi.xoff = 0
    vi.yoff = 2400
    vi.bits_per_pixel = 32
    fi.line_length = 2560
    main() – 5
    depth [32] – 6

    Of course the program exits in the switch case for bpp 32.

    Cheers!
    -Tom

  9. Hi,

    I want to write JNI for existing c code, I have c code and header, i
    created the .so file using cygwin, I wrote the Android.mk file and
    using cygwin created the shared library first it was showing
    “undefined reference to function name” error, then I removed “include $
    (CLEAR_VARS)” and again run the ndk-build command then shared library
    was created but while running it was showing UnsatisfiedLinker
    Error.my make file is
    LOCAL_PATH := $(call my-dir)

    include $(CLEAR_VARS)

    LOCAL_MODULE := test
    LOCAL_SRC_FILES := libtest.so

    include $(PREBUILT_SHARED_LIBRARY)

    #LOCAL_PATH := $(call my-dir)

    #include $(CLEAR_VARS)

    LOCAL_C_INCLUDES := $(LOCAL_PATH)

    LOCAL_MODULE := jni_anand
    LOCAL_CPP_EXTENSION := .cpp
    LOCAL_SRC_FILES := anand.cpp
    LOCAL_SHARED_LIBRARY := test

    include $(BUILD_SHARED_LIBRARY)

    Thanks…

  10. Hi,

    I cant find “build/prebuilt/($OS)/arm-eabi-4.2.1/bin/arm-eabi-gcc” on my copy of NDK (r6b). There is no prebuild directory inside of the build directory. Is the paths changed on my version of NDK?

    Thanks

  11. I didn’t use the new one yet, but Google has a habit now, of breaking everything with each new release.

  12. You really don’t need to even install cygwin for native android apps, just use your GCC from Android NDK:
    1. create setenv4.bat file (for android 1.6) in your NDK root, and copy the following content to it:

    @rem Change the two paths below to match your installation
    @set SDK_ROOT=C:\android
    @set NDK=%SDK_ROOT%\ndk-r6
    @set PLATFORM=%NDK%\platforms\android-4
    @set PATH=%NDK%\toolchains\arm-linux-androideabi-4.4.3\prebuilt\windows\arm-linux-androideabi\bin;%PATH%
    @set PATH=%NDK%\toolchains\arm-linux-androideabi-4.4.3\prebuilt\windows\libexec\gcc\arm-linux-androideabi\4.4.3;%PATH%
    @set PATH=%SDK_ROOT%\sdk\platform-tools;%PATH%
    @set lib=%NDK%\lib-swift;%PLATFORM%\arch-arm\usr\lib
    @set DEPS=-I%PLATFORM%\arch-arm\usr\include -Xlinker -s –sysroot=%PLATFORM%\arch-arm\usr -B %NDK%\toolchains\arm-linux-androideabi-4.4.3\prebuilt\windows\lib\gcc\arm-linux-androideabi\4.4.3
    @set GCC=gcc %DEPS%
    @echo 1. navigate: cd projects\native\NativeScreenCapture
    @echo 2. Compile: gcc %%DEPS%% -o file file.c
    @echo or: %%GCC%% -o file file.c
    @rem to use ADB, execute this: @set path=C:\android\sdk\platform-tools;%PATH%
    @echo 3. Copy: adb push file /folder/file
    @echo 4. Allow Run: adb shell chmod 555 /folder/file
    @echo 5. Execute: adb shell /folder/file
    @rem keep the console open
    @cmd /k

    2. Run that bat file and follow the instructions.
    cd
    %gcc% -o file1.c file2.c file3.c …

  13. Hi, thanks for the tutorial, it’s been really helpfull.
    Do you know how can i build an static or shared library?

    Thanks in advance.

  14. Veera:

    Very useful tutorial,thank you very much for taking time to write. I tried with Android-ndk-r7, it gave me some problems like can’t find /sys/ctypes.h. After setting up the tools as per the android-ndk native tools setup document everything started working. Script provided by NDK copied compiler tools and sysroot to one folder, that fixed all my issues.

    Thanks,
    Veera

Leave a Reply