http://www.ibm.com/developerworks/cn/opensource/os-cn-android-build/index.html#iratings
前言
AndroidBuild
系统是Android源码的一部分。关于如何获取Android源码,请参照AndroidSource
官方网站:
http://source.android.com/source/downloading.html。
AndroidBuild
系统用来编译Android系统,AndroidSDK
以及相关文档。该系统主要由Make文件,Shell脚本以及Python脚本组成,其中最主要的是Make文件。
众所周知,Android是一个开源的操作系统。Android的源码中包含了大量的开源项目以及许多的模块。不同产商的不同设备对于Android系统的定制都是不一样的。
如何将这些项目和模块的编译统一管理起来,如何能够在不同的操作系统上进行编译,如何在编译时能够支持面向不同的硬件设备,不同的编译类型,且还要提供面向各个产商的定制扩展,是非常有难度的。
但AndroidBuild
系统很好的解决了这些问题,这里面有很多值得我们开发人员学习的地方。
对于Android平台开发人员来说,本文可以帮助你熟悉你每天接触到的构建环境。
对于其他开发人员来说,本文可以作为一个GNUMake
的使用案例,学习这些成功案例,可以提升我们的开发经验。
Build系统中最主要的处理逻辑都在Make文件中,而其他的脚本文件只是起到一些辅助作用,由于篇幅所限,本文只探讨Make文件中的内容。
整个Build系统中的Make文件可以分为三类:
第一类是Build系统核心文件,此类文件定义了整个Build系统的框架,而其他所有Make文件都是在这个框架的基础上编写出来的。
图1是Android源码树的目录结构,Build系统核心文件全部位于/build/core(本文所提到的所有路径都是以Android源码树作为背景的,“/”指的是源码树的根目录,与文件系统无关)目录下。
第二类是针对某个产品(一个产品可能是某个型号的手机或者平板电脑)的Make文件,这些文件通常位于device目录下,该目录下又以公司名以及产品名分为两级目录,图2是device目录下子目录的结构。对于一个产品的定义通常需要一组文件,这些文件共同构成了对于这个产品的定义。例如,/device/sony/it26目录下的文件共同构成了对于SonyLT26
型号手机的定义。
第三类是针对某个模块(关于模块后文会详细讨论)的Make文件。整个系统中,包含了大量的模块,每个模块都有一个专门的Make文件,这类文件的名称统一为“Android.mk”,该文件中定义了如何编译当前模块。Build系统会在整个源码树中扫描名称为“Android.mk”的文件并根据其中的内容执行模块的编译。
Android系统的编译环境目前只支持Ubuntu以及MacOS两种操作系统。关于编译环境的构建方法请参见以下路径:http://source.android.com/source/initializing.html
在完成编译环境的准备工作以及获取到完整的Android源码之后,想要编译出整个Android系统非常的容易:
打开控制台之后转到Android源码的根目录,然后执行如清单1所示的三条命令即可("$"
是命令提示符,不是命令的一部分。):
完整的编译时间依赖于编译主机的配置,在笔者的MacbookPro(OSX
10.8.2, i7 2G CPU,8GRAM, 120G
SSD)上使用8个Job同时编译共需要一个半小时左右的时间。
$ source build/envsetup.sh $ lunch full-eng $ make -j8 |
这三行命令的说明如下:
第一行命令“sourcebuild/envsetup.sh”引入了 build/envsetup.sh
脚本。该脚本的作用是初始化编译环境,并引入一些辅助的Shell函数,这其中就包括第二步使用lunch函数。
除此之外,该文件中还定义了其他一些常用的函数,它们如表1所示:
名称 |
说明 |
---|---|
croot |
切换到源码树的根目录 |
m |
在源码树的根目录执行make |
mm |
Build当前目录下的模块 |
mmm |
Build指定目录下的模块 |
cgrep |
在所有 C/C++文件上执行grep |
jgrep |
在所有 Java文件上执行grep |
resgrep |
在所有 res/*.xml文件上执行grep |
godir |
转到包含某个文件的目录路径 |
printconfig |
显示当前 Build的配置信息 |
add_lunch_combo |
在 lunch函数的菜单中添加一个条目 |
第二行命令“lunchfull-eng”是调用lunch函数,并指定参数为“full-eng”。lunch函数的参数用来指定此次编译的目标设备以及编译类型。在这里,这两个值分别是“full”和“eng”。“full”是Android源码中已经定义好的一种产品,是为模拟器而设置的。而编译类型会影响最终系统中包含的模块,关于编译类型将在表7中详细讲解。
如果调用lunch函数的时候没有指定参数,那么该函数将输出列表以供选择,该列表类似图3中的内容(列表的内容会根据当前Build系统中包含的产品配置而不同,具体参见后文“添加新的产品”),此时可以通过输入编号或者名称进行选择。
第三行命令“make-j8”才真正开始执行编译。make的参数“-j”指定了同时编译的Job数量,这是个整数,该值通常是编译主机CPU支持的并发线程总数的1倍或2倍(例如:在一个4核,每个核支持两个线程的CPU上,可以使用make-j8
或make-j16)。在调用make命令时,如果没有指定任何目标,则将使用默认的名称为“droid”目标,该目标会编译出完整的Android系统镜像。
所有的编译产物都将位于/out目录下,该目录下主要有以下几个子目录:
-
/out/host/:该目录下包含了针对主机的Android开发工具的产物。即SDK中的各种工具,例如:emulator,adb,aapt等。
-
/out/target/common/:该目录下包含了针对设备的共通的编译产物,主要是Java应用代码和Java库。
-
/out/target/product/<product_name>/:包含了针对特定设备的编译结果以及平台相关的C/C++库和二进制文件。其中,<product_name>是具体目标设备的名称。
-
/out/dist/:包含了为多种分发而准备的包,通过“
makedist
target”将文件拷贝到该目录,默认的编译目标不会产生该目录。
Build的产物中最重要的是三个镜像文件,它们都位于/out/target/product/<product_name>/目录下。
这三个文件是:
-
system.img:包含了AndroidOS
的系统文件,库,可执行文件以及预置的应用程序,将被挂载为根分区。 -
ramdisk.img:在启动时将被Linux内核挂载为只读分区,它包含了/init文件和一些配置文件。它用来挂载其他系统镜像并启动init进程。
-
userdata.img:将被挂载为/data,包含了应用程序相关的数据以及和用户相关的数据。
整个Build系统的入口文件是源码树根目录下名称为“Makefile”的文件,当在源代码根目录上调用make命令时,make命令首先将读取该文件。
Makefile文件的内容只有一行:“includebuild/core/main.mk
”。该行代码的作用很明显:包含build/core/main.mk文件。在main.mk文件中又会包含其他的文件,其他文件中又会包含更多的文件,这样就引入了整个Build系统。
这些Make文件间的包含关系是相当复杂的,图3描述了这种关系,该图中黄色标记的文件(且除了 $
开头的文件)都位于build/core/目录下。
表2总结了图4中提到的这些文件的作用:
文件名 |
|
---|