Monday, September 15, 2014

Porting VIM to android [ARM]

Last week I tried porting VIM to android. I felt that I could give you guys a complete walkthrough. That way you could get the idea of the process and try porting something else.

So I started and very soon realized that I would need to port ncurses also. So first I had to download ncurses. Took the latest version. Which was ncurses-5.9 at the time i did the port. Always try to compile things for your system first. So i did that. It's a straight forward procedure. Had to fun configure and then make.
That did the job. I was not interested in installing anyways. So i directly ran './configure --help' this lists out all the options available. 
The most important ones were displayed in the end. Those were the set of flags that we can pass on to the build system.
The configure script allowed me to configure both the compilers and their flags preprocessor and linker flags. This was great. So I set about trying to get it built. It is always a good thing to write a script to do your configure and make. so I wrote mine "build_for_android.sh". So basically I wrote whatever I could think of to make the build happen. The initial shell script looked like this.

NDK=/home/regnarts/android/android-ndk-r8e
CROSS_COMPILE=${NDK}/toolchains/arm-linux-androideabi-4.7/prebuilt/linux-x86_64/bin/arm-linux-androideabi-
PREFIX=`pwd`/../sysroot
COPTS=--sysroot=${NDK}/platforms/android-9/arch-arm
./configure \
        CC=${CROSS_COMPILE}gcc \
        CFLAGS=${COPTS} \
        CPPFLAGS=${COPTS} \
        CPP=${CROSS_COMPILE}cpp \
        CXX=${CROSS_COMPILE}c++ \
        --host=arm \
        --prefix=${PREFIX}
make


After this I ran make, only to encounter an error 
Upon investigation and a bit of googling I found out that android's LOCALE was broken. So I had to uncomment the flag HAVE_LOCALE_H. I tried to see if there was a possibility of doing it as a flag passed through configure but I finally settled over sed. I quickly figured out that the flag was being defined in "include/ncurses_cfg.h". So I just added
sed -i 's/#define HAVE_LOCALE_H 1/#define HAVE_LOCALE_H 0/' include/ncurses_cfg.h
to the build script. Now upon running the script and make I got this



Observe that this only contains a few undefined references. I ran a search for the required symbols in the lib directory of ndk.
I couldn't find it anywhere in the android lib directory. But googled it that I need to include the library libgnustl_static.a. I knew just including -lgnustl_static wouldn't work. Because the library wasn't there in sysroot we specified. So merely including '-lgnustl_static' wouldn't help. So we need to find the library. And it was there in <ndk-root>/sources/cxx-stl/gnu-libstdc++/4.7/libs/armeabi-v7a. So I included a '-Lflag' to fix it. And finally it worked.

Running make install it put all the output in the sysroot directory as I had pointed out in the script. The directory now held all the binaries required to build VIM.

So now I went forward and downloaded VIM source. Unpacked it the same way. Then went on to directly run './configure --help'. I was presented with a similar set of options. So I wrote a 'build_for_android.sh' for this one too.

But I got a series of errors as such these were resolved by setting the required variables. So I added
export vim_cv_terminfo='yes'
export vim_cv_bcopy_handles_overlap='no'
export vim_cv_memcpy_handles_overlap='no'
export vim_cv_stat_ignores_slash='no'
export vim_cv_memmove_handles_overlap='no'
export vim_cv_getcwd_broken='no'
export vim_cv_tty_group=world
export vim_cv_toupper_broken='set' 

to 'build_for_android.sh'.
This took a long time as everytime I set one another would pop up. After fixing this up I found another
But this was easy. Observe that we haven't provided path for the linker to search for ncurses library that we have built. Including that to the build the configure ran smoothly. Then I ran make

Now these are all c library symbols. Why aren't they being found?, but upon closer inspection I saw this

The script was running gcc as a linker. And It wasn't including any flags that we had passed on. So I consulted the configures help menu again. And learned that I had not set the LDFLAGS. So I straightaway corrected the script. And now ran make again.
Now I did my check and couldn't find these symbols anywhere. They weren't in any library that was included in the ndk[r8e]. After spending some quality time over it I was frustrated. It simply wasn't available in android. I had to implement it myself. So I googled out source for the function.

#include <stdlib.h>
#include <string.h>
#include <wchar.h>

int mblen(const char *s, size_t n)
{
        static mbstate_t mbs;
        size_t rval;

        if (s == NULL) {
                /* No support for state dependent encodings. */
                memset(&mbs, 0, sizeof(mbs));
                return (0);
        }
        rval = mbrtowc(NULL, s, n, &mbs);
        if (rval == (size_t)-1 || rval == (size_t)-2)
                return (-1);
        return ((int)rval);
}


And then added to the file from where the function was called

And now running make got it compiled completely. I did make install and
There it was. I wasted no time and pushed it to my device and tried running it over adb.
I wanted to check it up with terminal emulator. But unfortunately my touch screen is broken making it impossible to use my phone. I am not sure if it will completely support it. Let me know if you guys find out Download the source scripts and binaries from here.

I recently gave a talk about this at GDG BlrDroid. Here is the video