标签:
原文网址:http://blog.sina.com.cn/s/blog_533074eb0101ez5q.html
Android 编译环境 本身比较复杂,且不像普通的编译环境:只有顶层目录下才有 Makefile 文件,而其他的每个component 都使用统一标准的 Android.mk . Android.mk 文件本身是比较简单的,不过它并不是我们熟悉的Makefile ,而是经过了 Android 自身编译系统的很多处理,因此要真正理清楚其中的联系还比较复杂,不过这种方式的好处在于,编写一个新的 Android.mk 来给 Android 增加一个新的 Component 会比较简单。
编译 Java 程序可以直接采用 Eclipse 的集成环境来完成,这里就不重复了。我们主要针对 C/C++ 来说明,下面通过一个小例子来说明,如何在 Android 中增加一个 C 程序的 Hello World :
1. 在 $(YOUR_ANDROID)/ development 目录下创建 hello 目录,其中 $(YOUR_ANDROID) 指 Android 源代码所在的目录。
- # mkdir $(YOUR_ANDROID)/development/hello
2. 在 $(YOUR_ANDROID)/development /hello/ 目录编写 hello.c 文件, hello.c 的内容当然就是经典的 HelloWorld 程序:
#include
int main()
{
printf("Hello World!/n");
return 0;
}
3. 在 $(YOUR_ANDROID)/development /hello/ 目录编写 Android.mk 文件。这是 Android Makefile 的标准命名,不要更改。Android.mk 文件的格式和内容可以参考其他已有的 Android.mk 文件的写法,针对 helloworld 程序的 Android.mk 文件内容如下:
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS)
LOCAL_SRC_FILES:= / hello.c
LOCAL_MODULE := helloworld include $(BUILD_EXECUTABLE) |
注意上面 LOCAL_SRC_FILES 用来指定源文件;, LOCAL_MODULE 指定要编译的模块的名字,下一步骤编译时就要用到; include $(BUILD_EXECUTABLE) 表示要编译成一个可执行文件,如果想编译成动态库则可用BUILD_SHARED_LIBRARY ,这些可以在 $(YOUR_ANDROID)/build/core/config.mk 查到。
4. 回到 Android 源代码顶层目录进行编译:
# cd $(YOUR_ANDROID) && make helloworld |
注意 make helloworld 中的目标名 helloworld 就是上面 Android.mk 文件中由 LOCAL_MODULE 指定的模块名。编译结果如下:
target thumb C: helloworld <= development/hello/hello.c target Executable: helloworld (out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworld) target Non-prelinked: helloworld (out/target/product/generic/symbols/system/bin/helloworld) target Strip: helloworld (out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/helloworld) Install: out/target/product/generic/system/bin/helloworld |
5 .如上面的编译结果所示,编译后的可执行文件存放在 out/target/product/generic/system/bin/helloworld ,通过 ”adb push”将它传送到模拟器上,再通过 ”adb shell” 登录到模拟器终端,就可以执行了
============================================
上一篇中(即《 Android编译环境 (1) - 编译 Native C的 helloworld模块 》),我们试用了通过标准的 Android.mk 文件来编译 C 模块,下面我们来试试如何直接运用 gcc 命令行来编译,从而了解 Android 编译环境的细节。
Android 编译环境提供了 ”showcommands” 选项来显示编译命令行, 我们可以通过打开这个选项来查看一些编译时的细节。当然,在这之前要把上一篇中的 helloworld 模块 clean:
# make clean-helloworld |
上面的 “make clean-$(LOCAL_MODULE)” 是 Android 编译环境提供的 make clean 的方式。
接下来使用 showcommands 选项 重新编译 helloworld:
# make helloworld showcommands build/core/product_config.mk:229: WARNING: adding test OTA key target thumb C: helloworld <= development/hello/hello.c prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-gcc -I system/core/include -I hardware/libhardware/include -I hardware/ril/include -I dalvik/libnativehelper/include -I frameworks/base/include -I external/skia/include -I out/target/product/generic/obj/include -I bionic/libc/arch-arm/include -I bionic/libc/include -I bionic/libstdc++/include -I bionic/libc/kernel/common -I bionic/libc/kernel/arch-arm -I bionic/libm/include -I bionic/libm/include/arch/arm -I bionic/libthread_db/include -I development/hello -I out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates -c -fno-exceptions -Wno-multichar -march=armv5te -mtune=xscale -msoft-float -fpic -mthumb-interwork -ffunction-sections -funwind-tables -fstack-protector -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -include system/core/include/arch/linux-arm/AndroidConfig.h -DANDROID -fmessage-length=0 -W -Wall -Wno-unused -DSK_RELEASE -DNDEBUG -O2 -g -Wstrict-aliasing=2 -finline-functions -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers -DNDEBUG -UDEBUG -mthumb -Os -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64 -MD -o out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/hello.o development/hello/hello.c target Executable: helloworld (out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworld) prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-g++ -nostdlib -Bdynamic -Wl,-T,build/core/armelf.x -Wl,-dynamic-linker,/system/bin/linker -Wl,--gc-sections -Wl,-z,nocopyreloc -o out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworld -Lout/target/product/generic/obj/lib -Wl,-rpath-link=out/target/product/generic/obj/lib -lc -lstdc++ -lm out/target/product/generic/obj/lib/crtbegin_dynamic.o out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/hello.o -Wl,--no-undefined prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/../lib/gcc/arm-eabi/4.2.1/interwork/libgcc.a out/target/product/generic/obj/lib/crtend_android.o target Non-prelinked: helloworld (out/target/product/generic/symbols/system/bin/helloworld) out/host/linux-x86/bin/acp -fpt out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworld out/target/product/generic/symbols/system/bin/helloworld target Strip: helloworld (out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/helloworld) out/host/linux-x86/bin/soslim --strip --shady --quiet out/target/product/generic/symbols/system/bin/helloworld --outfile out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/helloworld Install: out/target/product/generic/system/bin/helloworld out/host/linux-x86/bin/acp -fpt out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/helloworld out/target/product/generic/system/bin/helloworld |
从上面的命令行可以看到, Android 编译环境所用的交叉编译工具链是 prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-gcc , -I 和 -L 参数指定了所用的 C 库头文件和动态库文件路径分别是 bionic/libc/include 和out/target/product/generic/obj/lib ,其他还包括很多编译选项以及 -D 所定义的预编译宏。
我们可以利用上面的编译命令,稍加简化来手工编译 helloworld 程序。先手工删除上次编译得到的 helloworld 程序:
# rm out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/hello.o # rm out/target/product/generic/system/bin/helloworld |
再用 gcc 编译,生成目标文件:
# prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-gcc -I bionic/libc/arch-arm/include -I bionic/libc/include -I bionic/libc/kernel/common -I bionic/libc/kernel/arch-arm -c -fno-exceptions -Wno-multichar -march=armv5te -mtune=xscale -msoft-float -fpic -mthumb-interwork -ffunction-sections -funwind-tables -fstack-protector -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -include system/core/include/arch/linux-arm/AndroidConfig.h -DANDROID -fmessage-length=0 -W -Wall -Wno-unused -DSK_RELEASE -DNDEBUG -O2 -g -Wstrict-aliasing=2 -finline-functions -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers -DNDEBUG -UDEBUG -mthumb -Os -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64 -MD -o out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/hello.o development/hello/hello.c |
与 Android.mk 编译参数比较,上面主要减少了不必要的 -I 参数。
接下来生成可执行文件:
# prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-gcc -nostdlib -Bdynamic -Wl,-T,build/core/armelf.x -Wl,-dynamic-linker,/system/bin/linker -Wl,--gc-sections -Wl,-z,nocopyreloc -o out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworld -Lout/target/product/generic/obj/lib -Wl,-rpath-link=out/target/product/generic/obj/lib -lc -lm out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/hello.o out/target/product/generic/obj/lib/crtbegin_dynamic.o -Wl,--no-undefined ./prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/../lib/gcc/arm-eabi/4.2.1/interwork/libgcc.a out/target/product/generic/obj/lib/crtend_android.o |
这里值得留意的是参数“ -Wl,-dynamic-linker,/system/bin/linker ”,它指定了 Android 专用的动态链接器 /system/bin/linker ,而不是通常所用的 ld.so 。
生成的可执行程序可用 file 和 readelf 命令来查看一下:
# file out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworld out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworld: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), not stripped # readelf -d out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworld |grep NEEDED 0x00000001 (NEEDED) Shared library: [libc.so] 0x00000001 (NEEDED) Shared library: [libm.so] |
这是 ARM 格式的动态链接可执行文件,运行时需要 libc.so 和 libm.so 。“ not stripped ”表示它还没被 STRIP 。嵌入式系统中为节省空间通常将编译完成的可执行文件或动态库进行 STRIP ,即去掉其中多余的符号表信息。在前面“ make helloworld showcommands ”命令的最后我们也可以看到, Android 编译环境中使用了 out/host/linux-x86/bin/soslim 工具进行STRIP 。
======================================
Android 所用的 Toolchain (即交叉编译工具链)可从下面的网址下载:
http://android.kernel.org/pub/android-toolchain-20081019.tar.bz2 。如果下载了完整的 Android 项目的源代码,则可以在“/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin ”目录下找到交叉编译工具,比如 Android 所用的 arm-eabi-gcc-4.2.1 。
Android 并没有采用 glibc 作为 C 库,而是采用了 Google 自己开发的 Bionic Libc ,它的官方 Toolchain 也是基于Bionic Libc 而并非 glibc 的。这使得使用或移植其他 Toolchain 来用于 Android 要比较麻烦:在 Google 公布用于 Android的官方 Toolchain 之前,多数的 Android 爱好者使用的 Toolchain 是在http://www.codesourcery.com/gnu_toolchains/arm/download.html 下载的一个通用的 Toolchain ,它用来编译和移植 Android的 Linux 内核是可行的,因为内核并不需要 C 库,但是开发 Android 的应用程序时,直接采用或者移植其他的 Toolchain都比较麻烦,其他 Toolchain 编译的应用程序只能采用静态编译的方式才能运行于 Android 模拟器中,这显然是实际开发中所不能接受的方式。目前尚没有看到说明成功移植其他交叉编译器来编译 Android 应用程序的资料。
与 glibc 相比, Bionic Libc 有如下一些特点:
- 采用 BSD License ,而不是 glibc 的 GPL License ;
- 大小只有大约 200k ,比 glibc 差不多小一半,且比 glibc 更快;
- 实现了一个更小、更快的 pthread ;
- 提供了一些 Android 所需要的重要函数,如 ”getprop”, “LOGI” 等;
- 不完全支持 POSIX 标准,比如 C++ exceptions , wide chars 等;
- 不提供 libthread_db 和 libm 的实现
另外, Android 中所用的其他一些二进制工具也比较特殊:
- 加载动态库时使用的是 /system/bin/linker 而不是常用的 /lib/ld.so;
- prelink 工具不是常用的 prelink 而是 apriori ,其源代码位于 ” /build/tools/apriori”
- strip 工具也没有采用常用的 strip ,即“ /prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin ”目录下的 arm-eabi-strip,而是位于 / out/host/linux-x86/bin/
的soslim 工具。
【转】Android C程序也可自己手动用交叉编译器编译 (
标签:
原文地址:http://www.cnblogs.com/wi100sh/p/4558220.html