首页 > android > Port glib to android

Port glib to android

glib]] 是 linux 下非常基础的库, 大部分 linux 下的软件 都依赖于它, 比如 gstreamer, gtk 等等. 由于最近我在准备 hack spice, 准备把它 port 到 android 上, 而 libspice 又依赖于 glib, 所以需要把 glib 移植到 android 上.

所幸几年前就 hack 过大量程序到 ARM 和 blackfin 平台上, 所以过程还算顺利.

下载相应的文件

需要用到的源代码有以下几个:

libiconv
1.14, 从 这里 下载
gettext
0.18.2, 从 这里 下载
libffi
3.0.12, 从 这里 下载
glib
2.34.3, 从 这里 下载

由于有的版本(比如 libiconv-1.14, 旧的 glib)暂时不支持自动探测 android host, 所以需要对探测的脚本做一些修改, 这里使用最简单的方法, 使用 gnu 网站上最新的 探测脚本替换不支持的脚本, 先把脚本下载下来, 需要是替换

$  wget -O /tmp/config.sub "git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD"
$  wget -O /tmp/config.guess "git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD"

编译环境

这里 根据操作系统的版本下载相应的 toolchain版本, 我下载的是 android-ndk-r8d-linux-x86.tar.bz2, 解压到合适的目录, 然后进入该目录, 安装 toolchain, (在这里我将 toolchain 安装在 ${HOME}/Develop/android-toolchain, 使用 gcc-4.7, android-14 的 platform

$ tar xf android-ndk-r8d-linux-x86.tar.bz2
$ cd android-ndk-r8d
$ build/tools/make-standalone-toolchain.sh --platform=android-14 \
--toolchain=arm-linux-androideabi-4.7 \
--install-dir=${HOME}/Develop/android-toolchain

在 bash 的配置文件里面配置 toolchain 的相关路径

$ echo "export SYSROOT=${HOME}/Develop/android-toolchain/sysroot" >> ${HOME}/.bashrc
$ echo 'export PATH=${PATH}:${HOME}/Develop/android-toolchain/bin' >> ${HOME}/.bashrc
$ source ${HOME}/.bashrc

compile libiconv

libiconv 目前的最新版本是 1.14, 还不支持 android, 需要做一些 hack

将上面下载的 config.sub 和 config.guest 文件替换原有的文件, 因为目前还不支持 自动探测 android host.

$ cp /tmp/config.sub build-aux/config.sub
$ cp /tmp/config.sub libcharset/build-aux/config.sub
$ cp /tmp/config.guess build-aux/config.guess
$ cp /tmp/config.guess libcharset/build-aux/config.guess

另外, 由于 libiconv 自带的 stdint.h 和 android toolchain 的冲突, 导致使用 time_t 等几个结构体的时候会出现类型未申明的情况, 比如:

In file included from /root/Develop/android-toolchain/sysroot/usr/include/sys/time.h:33:0,
                 from /root/Develop/android-toolchain/sysroot/usr/include/time.h:32,
                 from ./time.h:40,
                 from ./stdint.h:518,
                 from /root/Develop/android-toolchain/bin/../lib/gcc/arm-linux-androideabi/4.7/include-fixed/sys/types.h:43,
                 from ./fcntl.h:46,
                 from careadlinkat.h:23,
                 from areadlink.c:27:
/root/Develop/android-toolchain/sysroot/usr/include/linux/time.h:20:2: error: unknown type name 'time_t'
/root/Develop/android-toolchain/sysroot/usr/include/linux/time.h:26:2: error: unknown type name 'time_t'
/root/Develop/android-toolchain/sysroot/usr/include/linux/time.h:27:2: error: unknown type name 'suseconds_t'

在 configure 中预定义 gl_cv_header_working_stdint_h=yes 可以避开这个问题.

以下是编译的具体指令:

$ gl_cv_header_working_stdint_h=yes ./configure --prefix="${SYSROOT}/usr" --host=arm-linux-androideabi CFLAGS="--sysroot $SYSROOT" --enable-static
$ make -j5
$ make install

现在, 应该可以在 ${SYSROOT}/usr/lib/ 下看到相关的库已经安装了

$ ls ${SYSROOT}/usr/lib/libiconv*

compile gettext

android toolchai 的 passwd 结构体没有 pw_gecos 成员. 会报以下错误:

msginit.c: In function 'get_user_fullname':
msginit.c:1084:21: error: 'struct passwd' has no member named 'pw_gecos'

以下的小 patch 简单的避免这个问题(其实就是简单粗暴地给 fullname 赋值, 避免访问 pwd->pw_gecos):

--- msginit.c   2012-12-04 14:28:58.000000000 +0800
+++ msginit.c.new   2013-04-01 11:59:54.054980294 +0800
@@ -1081,7 +1081,11 @@
       char *result;

       /* Return the pw_gecos field, up to the first comma (if any).  */
+#ifndef __ANDROID__
       fullname = pwd->pw_gecos;
+#else
+fullname = "android";
+#endif
       fullname_end = strchr (fullname, ',');
       if (fullname_end == NULL)
         fullname_end = fullname + strlen (fullname);

以下是编译的具体指令:

$ ./configure --prefix="${SYSROOT}/usr" --host=arm-linux-androideabi CFLAGS="--sysroot $SYSROOT" --enable-static --disable-java --disable-native-java
$ make -j5
$ make install

libffi

编译没什么问题, 一次通过

$ ./configure --prefix="${SYSROOT}/usr" --host=arm-linux-androideabi CFLAGS="--sysroot $SYSROOT" --enable-static
$ make -j5
$ make install

compile glib

glib 的编译稍微麻烦一点.

这里我选择相对新一点的 2.34.3 版本, 这个版本的探测脚本已经支持 android host 了.

另外, 在编译前的配置时, ARM 上的编译器会在检查一些特性时失败, 采用下面的方法 避免这类失败: 详情请参考源码目录下的 docs/reference/glib/html/glib-cross-compiling.html

新建一个文件 android.cache, 写入以下内容.

# file android.cache
ac_cv_type_long_long=yes
glib_cv_stack_grows=no
glib_cv_uscore=no
ac_cv_func_posix_getpwuid_r=no
ac_cv_func_posix_getgrgid_r=no

配置编译的指令(test 模块编译不过去, 没关系, 我永不到, disable 之):

$ ./configure --prefix="${SYSROOT}/usr" --host=arm-linux-androideabi CFLAGS="--sysroot $SYSROOT" --enable-static --cache-file=android.cache --disable-modular-tests

编译的时候会出现各种各样的错误:

gstrfuncs.c: In function 'g_ascii_strtod':
gstrfuncs.c:718:30: error: 'struct lconv' has no member named 'decimal_point'
gstrfuncs.c: In function 'g_ascii_formatd':
gstrfuncs.c:942:30: error: 'struct lconv' has no member named 'decimal_point'

gutils.c:840:8: error: 'struct passwd' has no member named 'pw_gecos'
gutils.c:840:25: error: 'struct passwd' has no member named 'pw_gecos'
gutils.c:846:35: error: 'struct passwd' has no member named 'pw_gecos'
gutils.c:749:12: warning: unused variable 'logname' [-Wunused-variable]
gutils.c:748:10: warning: unused variable 'error' [-Wunused-variable]

glocalfileinfo.c:1097:21: error: 'struct passwd' has no member named 'pw_gecos'

gresolver.c:1133:14: error: 'T_TXT' undeclared (first use in this function)
gresolver.c:1133:14: note: each undeclared identifier is reported only once for each function it appears in
gresolver.c:1135:14: error: 'T_SOA' undeclared (first use in this function)
gresolver.c:1137:14: error: 'T_NS' undeclared (first use in this function)
gresolver.c:1139:14: error: 'T_MX' undeclared (first use in this function)

同样, 简单粗暴的 hack.(由于 patch 比较打, 我放在后面的 gist 上).

不同版本可能会遇到各种各样不同的编译错误, 这里需要具体问题具体分析, 比如 某些结构体, 宏没有申明的, 可以手动添加, 一些测试模块编译不过去, 可以把测试 模块去掉, 例如上面的 configure 时的–disable-modular-tests 参数.

测试

使用 glib 获取外网 IP 的 test 代码, JNI 和 NDK 的使用请看我其它的 android NDK 开发指南(抱歉还没有).

// file GetIPAddr.java
package org.mathslinux.glib_demo;
public class GetIPAddr {
    static {
        System.loadLibrary("ipaddr");
    }
 
     public static native String getAddr();
}
 
//  MainActivity.java
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
public class MainActivity extends Activity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setContentView(R.layout.activity_main);
    }
 
    public void onClick(View v) {
        EditText text = (EditText) this.findViewById(R.id.editText1);
        text.setText(GetIPAddr.getAddr());
    }
 
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        this.getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
 
}
/* file ip.c */
#include 
#include 
#include 
#include "ip.h"
static gchar *get_addr()
{
    char url[128] = {0};
 
    g_type_init();
    GSocketClient *client = g_socket_client_new();
    snprintf(url, sizeof(url), "%s:%s%s", "http", "//", "cip.cc");
    GSocketConnection *c = g_socket_client_connect_to_uri(
        client, url, 80, NULL, NULL);
    if (c) {
        GSocket *socket = NULL;
        g_object_get(c, "socket", &socket, NULL);
        if (socket) {
            char buf[2048] = {0};
            char *str = g_malloc0(1024);
            char *p, *q;
            char *send = "GET / HTTP/1.1\r\nUser-Agent: curl/7.29.0\r\nHost: cip.cc\r\nAccept: */*\r\n\r\n";
            g_socket_send(socket, send, strlen(send) + 1, NULL, NULL);
            g_socket_receive(socket, buf, sizeof(buf), NULL, NULL);
            p = strstr(buf, "IP");
            q = strstr(p, "\n");
            if (p && q) {
                char ip[32] = {0};
                snprintf(ip, q - p + 2, "%s", p);
                strcat(str, ip);
            }
 
            p = strstr(buf, "地址");
            q = strstr(p, "\n");
            if (p && q) {
                char addr[1024] = {0};
                snprintf(addr, q - p + 2, "%s", p);
                strcat(str, addr);
            }
            g_socket_close(socket, NULL);
            return str;
        }
    }
    g_object_unref(client);
    return NULL;
}
 
JNIEXPORT jstring JNICALL Java_org_mathslinux_glib_1demo_GetIPAddr_getAddr
(JNIEnv *je, jclass jc)
{
    char *addr = get_addr();
    return (*je)->NewStringUTF(je, addr);
}

秀一张截图:
glib_on_android

Others

glib 的 patch 我贴在 gist 上 请访问: 我的 gist

分类: android 标签:
  1. Robin
    2013年5月29日05:37 | #1

    谢谢大师分享!!O(∩_∩)O哈哈~

  2. Robin
    2013年5月31日05:18 | #2

    大神,遇到下面的问题没有阿,
    CCLD libgobject-2.0.la
    CC gobject-query.o
    CCLD gobject-query
    ./.libs/libgobject-2.0.so: undefined reference to `ffi_type_pointer’
    ./.libs/libgobject-2.0.so: undefined reference to `ffi_type_sint32′
    ./.libs/libgobject-2.0.so: undefined reference to `ffi_type_uint64′
    ./.libs/libgobject-2.0.so: undefined reference to `ffi_call’
    ./.libs/libgobject-2.0.so: undefined reference to `ffi_type_void’
    ./.libs/libgobject-2.0.so: undefined reference to `ffi_type_double’
    ./.libs/libgobject-2.0.so: undefined reference to `ffi_type_sint64′
    ./.libs/libgobject-2.0.so: undefined reference to `ffi_type_float’
    ./.libs/libgobject-2.0.so: undefined reference to `ffi_prep_cif’
    ./.libs/libgobject-2.0.so: undefined reference to `ffi_type_uint32′
    collect2: error: ld returned 1 exit status
    make[4]: *** [gobject-query] Error 1

  3. mathslinux
    2013年5月31日05:40 | #3

    你没有把 libffi 编译好(参考前面 lbiffi 的编译), glib 依赖于 libffi, 你看看你的 sysroot 里面有没有 ilbffi
    例如: ls /android/sysroot/usr/lib/libffi*

    @Robin

  4. mathslinux
    2013年5月31日05:41 | #4

    @Robin 回复见上面的评论

  5. Robin
    2013年6月1日05:42 | #5

    大神,这些都在阿,
    不知如何出现这种问题:

    $ ls sysroot/usr/lib/libffi*
    sysroot/usr/lib/libffi.a sysroot/usr/lib/libffi.so sysroot/usr/lib/libffi.so.6.0.1
    sysroot/usr/lib/libffi.la sysroot/usr/lib/libffi.so.6

    sysroot/usr/lib/libffi-3.0.12:
    include

  6. mathslinux
    2013年6月1日06:15 | #6

    @Robin 应该是你链接的时候libffi没有找到,

    看看你编译的具体指令, make V=1 在链接时候的输出结果

    有没有 -lffi 和 -LYOUR_SYSROOTPATH/sysroot/usr/lib, 有没有YOUR_SYSROOTPATH/sysroot/usr/lib/libffi.so* 存在, 没有的化, 就是在 configure 那里你配置错了.

  7. rongpmcu
    2013年6月5日04:12 | #7

    您好,按照您的操作编译 发现报错:
    echo glib-unix.h glib-object.h glib.h galloca.h garray.h gasyncqueue.h gatomic.h gbacktrace.h gbase64.h gbitlock.h gbookmarkfile.h gbytes.h gcharset.h gchecksum.h gconvert.h gdataset.h gdate.h gdatetime.h gdir.h genviron.h gerror.h gfileutils.h ggettext.h ghash.h ghmac.h ghook.h ghostutils.h gi18n.h gi18n-lib.h giochannel.h gkeyfile.h glist.h gmacros.h gmain.h gmappedfile.h gmarkup.h gmem.h gmessages.h gnode.h goption.h gpattern.h gpoll.h gprimes.h gqsort.h gquark.h gqueue.h grand.h gregex.h gscanner.h gsequence.h gshell.h gslice.h gslist.h gspawn.h gstdio.h gstrfuncs.h gtestutils.h gstring.h gstringchunk.h gthread.h gthreadpool.h gtimer.h gtimezone.h gtrashstack.h gtree.h gtypes.h gunicode.h gurifuncs.h gutils.h gvarianttype.h gvariant.h gversion.h gversionmacros.h gwin32.h gprintf.h > glib-public-headers.txt.tmp && mv glib-public-headers.txt.tmp glib-public-headers.txt
    CCLD libglib-2.0.la
    /home/rongpeng/android_env/android-toolchain/bin/../lib/gcc/arm-linux-androideabi/4.7/../../../../arm-linux-androideabi/bin/ld: error: cannot find -lpthread
    collect2: error: ld returned 1 exit status

  8. rongpmcu
    2013年6月7日02:11 | #8

    我自己解决了,将每个Makefile里面的pthread注释掉就可以了,测试暂时没发现问题

    • mathslinux
      2013年6月7日02:13 | #9

      你说的是 glib 源码里面的 Makefile? 还是你自己项目里面的 Makefile(Android.mk), 应该不用改到 glib 的 Makefile, 这是自动生成的.

  9. rongpmcu
    2013年6月7日03:54 | #10

    @mathslinux
    改的是glib生成的Makefile, 我知道是自动生成的,但是会报我上面提到的错误,实在没办法,就先把gio、gmodule 以及 gobject等里面Makefile里的pthread相关的注释了,就编译通过了 经过测试 发现都正常 特别是线程那块 我编译的源码版本和您的是一样的 还有,我看到了这个网站:http://gitorious.org/glib?page=1 那里好像有人已经做过glib for android移植了 是不是可以直接用啊 呵呵

  10. rongpmcu
    2013年6月7日03:58 | #11

    还有一个问题想问您(我入门android不久,呵呵), 我通过ndk编译生成本地so的时候,用readelf看so是依赖glib-2.0 gobject-2.0等等这些so的 但是发现没有自动帮我将这些so打包到apk里面去 我实在找不到解决办法,就在android.mk里面都静态链接那些库了, 您有动态库的方式没? 

  11. rongpmcu
    2013年6月7日04:40 | #12

    @Robin
    在configure前面加上PKG_CONFIG_PATH=ffi pkgconfig的路径 就可以了

  12. rongpmcu
    2013年6月7日04:42 | #13

    @mathslinux
    比如我的:
    PKG_CONFIG_PATH=/home/rongpeng/android_env/android-toolchain/sysroot/usr/lib/pkgconfig/ ./configure –prefix=”${SYSROOT}/usr” –host=arm-linux-androideabi CFLAGS=”–sysroot $SYSROOT” –enable-static –cache-file=android.cache –disable-modular-tests

  13. rongpmcu
    2013年6月7日04:44 | #14

    @rongpmcu
    @错了 呵呵  本想@Robin的

  14. mathslinux
    2013年6月7日05:40 | #15

    @rongpmcu 在你工程目录 libs/armeabi 下的 *.so 就会被自动打到 apk 里,
    glib 的话, 我是静态编译的, 因为我就只有一个 app 需要他, 没必要动态加载.

  15. rongpmcu
    2013年6月7日06:28 | #16

    @mathslinux
    您没有注释掉Makefile里面和phtread相关的东西吗? 我所有的编译源码都和您一样啊 怎么会有差别呢?
    其实我也试过自己将so拷贝到libs/armeabi里 后来看结果还是失败(可能是我误判,呵呵 因为我在虚拟机里面开发android,速度实在不敢恭维), 我就没有去验证是否已经打到apk里面去了

  16. mathslinux
    2013年6月7日06:52 | #17

    @rongpmcu 我完全是按照上面的步骤编译的, 这个 post 只是作为一个记录,
    另外是不是你用的 ndk-build 的版本不一致导致的, “编译环境” 那里和我的版本一致吗?

  17. mathslinux
    2013年6月7日06:56 | #18

    @rongpmcu 你简单的写一个线程的函数用你的 ndk 编译看看会不会出错, 类似:
    #include int main(int argc, char *argv[])
    {
    pthread_create(NULL, NULL, NULL, NULL);
    return 0;
    }
    然后 # ~/android/bin/arm-linux-androideabi-gcc -o test test.c # 看看能正确编译过去吗, 我这里是正常的

  18. rongpmcu
    2013年6月7日07:37 | #19

    @mathslinux
    可以编译过, 我连线程池什么的都试过了 都正常

  19. 适兕
    2013年6月7日07:55 | #20

    @rongpmcu wow, good job.

  20. 2013年6月28日05:18 | #21

    /* file ip.c */
    #include
    #include
    #include
    #include “ip.h”

    没显示清楚。

  21. mathslinux
    2013年6月28日05:42 | #22

    @Richard 我使用 org-mode 写得, 然后转 html 直接 xml-rpc 到 wordpress 上的, 可能哪里出了 bug, 完整版我传到 gist 上了: https://gist.github.com/mathslinux/5882699

  22. kinglon
    2013年7月5日06:06 | #23

    谢谢大牛分享,对我帮助非常大。
    但是我按照那个步骤编译glib时,提示错误:
    arm-linux-androideabi/bin/ld: error: cannot find -lpthread
    因为android没有libpthread.so,所以找不到吧。
    我将Makefile文件中的_lpthread替换成_lc,不知会不会有问题。

  23. vx13
    2013年10月30日03:08 | #24

    @mathslinux
    静态编译 glib 的话,恐怕版权问题比较麻烦吧?要求代码必须开源之类的,对商业 app 来说比较麻烦。

  24. mathslinux
    2013年10月30日03:18 | #25

    @vx13 glib 是 LGPL 的, 静态编译应该也是不需要open 你的source的, 我没有仔细研究过, 是在不行的话, 动态编译也没有什么, 因为现在android里几乎没有用glib的程序, 所以静态和动态的差别不大.

  1. 本文目前尚无任何 trackbacks 和 pingbacks.