Android编译环境

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)/external/hello/ 目录编写 hello.c 文件, hello.c 的内容当然就是经典的 HelloWorld 程序:

#include <stdio.h>

int main()
{
printf("Hello World!/n");

return 0;
}

3. $(YOUR_ANDROID)/external/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” 登录到模拟器终端,就可以执行了

手工编译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与Bionic Libc

Android 所用的 Toolchain (即交叉编译工具链)可 从下面的网址下载:

http://android.kernel.org/pub/android-toolchain-20081019.tar.bz2 。如果下载了完整的 Android 项目的源代码,则可以在“ <your_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 ,其源代码位于 ” <your_android>/build/tools/apriori”

- strip 工具也没有采用常用的 strip ,即“ <your_android>/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin ”目录下的 arm-eabi-strip ,而是位于 <your_android>/ out/host/linux-x86/bin/ soslim 工具。

更多相关文章

  1. Android(安卓)AOSP基础(三)Android系统源码的整编和单编
  2. Android工程中R.java文件的重新生成——注意资源文件的错误
  3. Android使用adb指令在虚拟机中安装、卸载apk程序
  4. 关于Android(安卓)动态加载 jar 文件
  5. Android安装的时候系统都做了些什么
  6. FFmpeg和android播放器
  7. Android内核开发:系统分区与镜像文件的烧写
  8. Android(安卓)Studio下载安装教程及开发环境搭建
  9. android动态加载(二)

随机推荐

  1. 详解uni-app中的样式
  2. 如何用vue实现网页截图你知道吗
  3. JavaScript中二维数组的创建技巧
  4. PHP基础 -(二)分支与循环、php模板语法
  5. 彻底学会快速部署vue框架,一篇就够了
  6. 变量与函数
  7. 原生购物车案例使用vue改写
  8. 解密跨境服装电商Shein,看千亿美元估值“
  9. vue中jsonp的使用方法
  10. ffmpeg / libx264 build for ANDROID