web analytics

Cross-compile GLib for Android

Prepare cross-compile environment

Install Android NDK and standalone toolchain

First, you need to have Android NDK r10e installed, which can be downloaded from:

Pay attention to the NDK version we are using: r10e. This is the version that can successfully compile the GLib library for Android. As the time of writing, the newest NDK version is r12b. Yet using of this version is NOT RECOMMENDED, because some changes the GCC toolchain makes the compiling process fail (I will explain later).

Then, make a standalone toolchain using the script at ${NDK}/build/tools/make-standalone-toolchain.sh. For more details, refer to my previous blog on Three Ways to Use Android NDK Cross Compiler or Android NDK doc.

$ ${NDK}/build/tools/make-standalone-toolchain.sh \
--toolchain=arm-linux-androideabi-4.9 \
--stl=gnustl \
--arch=arm \
--ndk-dir=/home/midev/dev/android-ndk-r10e \
--system=linux-x86_64 \
--package-dir=/home/midev/dev \
--install-dir=/home/midev/dev/android-ndk-toolchain \
--platform=android-19

We have made a standalone NDK toolchain targeting Android arm architecture, API level 19, using gnustl C++ library, and GCC version 4.9, host system type is linux-86_64.

Install necessary packages in Ubuntu

Several packages needs to be installed because GLib and its dependencies use GNU Autotools.

$ sudo apt-get update && sudo apt-get upgrade
$ sudo apt-get install build-essential
$ sudo apt-get install pkg-config automake autoconf libtool
$ sudo apt-get install zlib1g-dev libffi-dev libtool-bin

Then we need to specify Android version binutils for cross compiling and set proper compiler flags. I have a script to do the environment configuration.

#!/bin/sh
# Android cross-compile environment setup script for Glib
# Author  : Zengwen Yuan
# Date    : 2016-07-16
# Version : 2.1

# Android NDK sources and standalone toolchain is put here
export DEV=${HOME}/dev

# All the built binaries, libs and their header will be installed here
export PREFIX=/opt/android

# Don't mix up .pc files from your host and build target
export PKG_CONFIG_PATH=${PREFIX}/lib/pkgconfig

# GCC for Android version to use
# 4.9 is the only available version since NDK r11!
export GCC_VER=4.9

# The building system we are using (Linux x86_64)
export BUILD_SYS=x86_64-linux-gnu

# Set Android target API level
export ANDROID_API=19

# Set Android target arch
export ANDROID_ARCH=arm

# Set Android target name, according to Table 2 in
# https://developer.android.com/ndk/guides/standalone_toolchain.html
export ANDROID_TARGET=armv5te-none-linux-androideabi

# The cross-compile toolchain we use
export TOOLCHAIN=arm-linux-androideabi

# This is a symlink pointing to the real Android NDK r10e
export NDK=${DEV}/android-ndk

# The path of standalone NDK toolchain
# Refer to https://developer.android.com/ndk/guides/standalone_toolchain.html
export NDK_TOOLCHAIN=${DEV}/android-ndk-toolchain

# Set Android Sysroot according to API and arch
export SYSROOT=${NDK_TOOLCHAIN}/sysroot
# this one is the absolute, prebuilt path
# export SYSROOT=${NDK}/platforms/android-${ANDROID_API}/arch-${ANDROID_ARCH}

# Binutils path
export CROSS_PREFIX=${NDK_TOOLCHAIN}/bin/${TOOLCHAIN}
# this one is the absolute, prebuilt path
# export CROSS_PREFIX=${NDK}/toolchains/${TOOLCHAIN}-${GCC_VER}/prebuilt/linux-x86_64/bin/${TOOLCHAIN}

# Non-exhaustive lists of compiler + binutils
export AR=${CROSS_PREFIX}-ar
export AS=${CROSS_PREFIX}-as
export LD=${CROSS_PREFIX}-ld
export NM=${CROSS_PREFIX}-nm
export CC=${CROSS_PREFIX}-gcc
export CXX=${CROSS_PREFIX}-g++
export CPP=${CROSS_PREFIX}-cpp
export CXXCPP=${CROSS_PREFIX}-cpp
export STRIP=${CROSS_PREFIX}-strip
export RANLIB=${CROSS_PREFIX}-ranlib
export STRINGS=${CROSS_PREFIX}-strings

# Set build flags
# Refer to https://developer.android.com/ndk/guides/standalone_toolchain.html
export PATH=$PATH:${PREFIX}/bin:${PREFIX}/lib
export CFLAGS="--sysroot=${SYSROOT} -I${SYSROOT}/usr/include -I${PREFIX}/include -fPIE -DANDROID -Wno-multichar"
export CXXFLAGS=${CFLAGS}
export CPPFLAGS="--sysroot=${SYSROOT} -I${SYSROOT}/usr/include -I${NDK_TOOLCHAIN}/include/c++/ -DANDROID -DNO_XMALLOC -mandroid"
export LIBS="-lc"
export LDFLAGS="-Wl,-rpath-link=-I${SYSROOT}/usr/lib -L${SYSROOT}/usr/lib -L${PREFIX}/lib -L${NDK_TOOLCHAIN}/lib"

Save it, e.g. glib-env-prep.sh, and source it to make changes effective in current shell immediately:

$ emacs glib-env-prep.sh
$ source ./glib-env-prep.sh

Compile and install dependency libraries

The most haunting experience in compiling sources yourself is probably handling dependencies. We are out of no exception this time. To make it worse, we are cross-compiling GLib for Android, and that means we need to cross-compile not only GLib itself, but also three other dependencies: libiconv, libffi and gettext. You may also cross-compile PCRE but that’s not a mandatory dependency.

Compile libiconv 1.14

$ wget http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.14.tar.gz
$ tar zxvf libiconv-1.14.tar.gz
$ cd libiconv-1.14
$ ./configure --build=${BUILD_SYS} --host=arm-eabi --prefix=${PREFIX} --disable-rpath
$ make
$ make install

Then static library libiconv.a will be installed to /opt/android/lib.

Notice here that for host arch we used arm-eabi instead of arm-linux-androideabi, because arm-eabi is meant for building Android kernel. In general these two are slightly different. Though arm-linux-androideabi may work, I have not verified it so I cannot give any gurantees. Also, no need to run autogen.sh as it may confuse the build system and host system architectures.

Compile libffi 3.2.1

libffi is the Portable Foreign Function Interface Library and is a prerequisite for building GLib. It is an interface that allows code written in one language to call code written in another language.

$ wget ftp://sourceware.org/pub/libffi/libffi-3.2.1.tar.gz
$ tar zxvf libffi-3.2.1.tar.gz
$ cd libffi-3.2.1
$ sed -e '/^includesdir/ s/$(libdir).*$/$(includedir)/' -i include/Makefile.in
$ sed -e '/^includedir/ s/=.*$/=@includedir@/' -e 's/^Cflags: -I${includedir}/Cflags:/' -i libffi.pc.in
$ ./configure --build=${BUILD_SYS} --host=arm-eabi --prefix=${PREFIX} --enable-static
$ make
$ make install

The two lines beginning with sed is to make package install headers into the conventional ${PREFIX}/include instead of ${PREFIX}/lib/libffi-3.2.1/include. Thanks these instructions from LFS [1], otherwise we will be adding the -I and -L flags for libffi in our cross compile environment setting script.

Compile gettext 0.18.3

First get the source code for gettext 0.18.3

$ wget http://ftp.gnu.org/pub/gnu/gettext/gettext-0.18.3.tar.gz
$ tar zxvf gettext-0.18.3.tar.gz

To compile gettext-0.18.3, a patch must be applied. In gettext-tools/src/msginit.c, change line 1088 from

fullname = pwd->pw_gecos;

to

#ifndef __ANDROID__
	fullname = pwd->pw_gecos;
#else
	fullname = "android";
#endif

I have made a patch for it:

diff -Naur gettext-0.19.8/gettext-tools/src/msginit.c gettext-0.19.8-patched/gettext-tools/src/msginit.c
--- gettext-0.19.8/gettext-tools/src/msginit.c	2016-05-30 05:12:33.000000000 -0400
+++ gettext-0.19.8-patched/gettext-tools/src/msginit.c	2016-07-17 17:35:43.732076347 -0400
@@ -1081,7 +1081,12 @@
       char *result;
 
       /* Return the pw_gecos field, up to the first comma (if any).  */
-      fullname = pwd->pw_gecos;
+      // fullname = pwd->pw_gecos;
+      #ifndef __ANDROID__
+        fullname = pwd->pw_gecos;
+      #else
+        fullname = "android";
+      #endif
       fullname_end = strchr (fullname, ',');
       if (fullname_end == NULL)
         fullname_end = fullname + strlen (fullname);

Save it as gettext-0.18.3-android.patch and apply it:

$ emacs gettext-0.18.3-android.patch
$ patch -p0 < gettext-0.18.3-android.patch

Then configure and compile it.

$ cd gettext-0.18.3
$ ./configure --build=${BUILD_SYS} --host=arm-eabi  --prefix=${PREFIX} --disable-rpath --disable-libasprintf --disable-java --disable-native-java --disable-openmp --disable-curses
$ make
$ make install

We disabled some non-necessary functions. Again, no need to run autogen.sh.

Compile GLib

Finally, let’s compile the latest glib-2.48.1.

$ wget http://ftp.gnome.org/pub/gnome/sources/glib/2.48/glib-2.48.1.tar.xz
$ tar xvf glib-2.48.1.tar.xz .
$ cd glib-glib-2.48.1

Cross-compiling GLib requires a “cache file”, which tells GLib how to set some of the variables manually. For more details see GLib’s official doc at https://developer.gnome.org/glib/stable/glib-cross-compiling.html.

The cache file we will be using is called android.cache:

glib_cv_long_long_format=ll
glib_cv_stack_grows=no
glib_cv_sane_realloc=yes
glib_cv_have_strlcpy=no
glib_cv_va_val_copy=yes
glib_cv_rtldglobal_broken=no
glib_cv_uscore=no
glib_cv_monotonic_clock=no
ac_cv_func_nonposix_getpwuid_r=no
ac_cv_func_posix_getpwuid_r=no
ac_cv_func_posix_getgrgid_r=no
glib_cv_use_pid_surrogate=yes
ac_cv_func_printf_unix98=no
ac_cv_func_vsnprintf_c99=yes
ac_cv_func_realloc_0_nonnull=yes
ac_cv_func_realloc_works=yes

We will also make it read-only to prevent it from being overwritten by the automatic configure:

$ emacs android.cache
$ chmod a-x android.cache
$ ./autogen.sh
$ ./configure --build=${BUILD_SYS} --host=${TOOLCHAIN} --prefix=${PREFIX} --disable-dependency-tracking --cache-file=android.cache --enable-included-printf --enable-static --with-pcre=no
$ make
$ make install

Should you see any errors from executing autogen.sh, fix them. For example, it could tell you that no package ‘zlib’ or ‘libffi’ found, and that’s why we have installed it at the very beginning.

If you don’t want waste your time compiling an additional PCRE library like I do, then the last argument --with-pcre=no will be your life saver.

So, finally, we are done!! The ${PREFIX}/lib folder (/opt/android/lib in our case) will contain libglib-2.0.so, libgio-2.0.so, libgmodule-2.0.so, libgthread-2.0.so and other built libraries/binaries.

References

Appendix

And good luck on fixing any error that may pop up.

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