http://zwyuan.github.io/2016/07/17/cross-compile-glib-for-android/
17 Jul 2016
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
.
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
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.
$ 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.
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.
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
.
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.
I have tried NDK r12b
with both API level 23 and 19. But both of them cannot compile. When compiling the gettext
, API 23 will give error on “cannot find <stdio_ext.h>“ and API 19 will give error on “cannot find <search.h>“.
If you encountered an error complaining gets
not defined when compiling libiconv
, the following solutions may help you:
If you want to use with-pcre=system
when configuring GLib, you need to cross-compile PCRE 8.39 and install it:
./configure --build=${BUILD_SYS} --host=arm-eabi --disable-rpath --prefix=${PREFIX} --enable-unicode-properties
And good luck on fixing any error that may pop up.