Cross-compile GLib for Android¶
Contents
Install Android NDK and standalone toolchain¶
First, you need to have Android NDK r10e installed, which can be downloaded from:
- Darwin: https://dl.google.com/android/repository/android-ndk-r10e-darwin-x86_64.zip
- Linux: https://dl.google.com/android/repository/android-ndk-r10e-linux-x86_64.zip
- Windows: https://dl.google.com/android/repository/android-ndk-r10e-windows-x86_64.zip
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
# Email : zyuan@cs.ucla.edu
# 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¶
- [1] http://www.linuxfromscratch.org/blfs/view/svn/general/libffi.html
- [2] https://groups.google.com/forum/#!topic/lcm-users/Ly-xvHphpcY
- [3] http://mathslinux.org/?p=268
- [4] https://www.gnu.org/software/libiconv/
- [5] http://www.linuxfromscratch.org/lfs/view/development/chapter06/gettext.html
- [6] https://mssun.me/blog/build-android-toolchain.html
- [7] https://gist.github.com/mssun/19070cc35d7a38c9ea21
- [8] https://android.googlesource.com/platform/prebuilts/android-emulator-build/qemu-android-deps/+archive/master.tar.gz
- [9] http://forum.xda-developers.com/showthread.php?t=2723240
- [10] https://dl.dropboxusercontent.com/u/23869279/Files/cc.sh
- [11] https://mail.gnome.org/archives/gtk-devel-list/2011-March/msg00096.html
- [12] https://gist.github.com/nddrylliog/4688209
Appendix¶
- I have tried NDK
r12b
with both API level 23 and 19. But both of them cannot compile. When compiling thegettext
, 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 compilinglibiconv
, 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.