开发Linux编程的程序员都清楚,Makefile是一个很重要的文件,该文件一个重要作用就是指定了编译规则。如此庞大的android系统源码当然也不例外了。
android源码中Android.mk就充当了Makefile的角色。android系统的编译系统太过复杂了,没办法,那些可都是谷歌里面的大牛们写的,对于我等程序员来说,首先最重要的就是会用啊,老板不关心你知识渊博与否,就关心你能不能搞定问题,至于如何搞定老板不关心。所以这里我只是简单的介绍下android的编译系统,目的很简单,如果要添加一个新平台的话,知道看哪些文件,修改哪些文件就可以了。
下图所示编译android系统的三大步骤。
source build/envsetup.sh
上面的命令引入了 build/envsetup.sh
脚本。该脚本的作用是初始化编译环境,并引入一些辅助的 Shell 函数,这其中就包括第二步中选择目标所用的lunch 函数。除此之外,该文件中还定义了其他一些常用的函数,如下所示:
名称 | 说明 |
---|---|
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 函数的菜单中添加一个条目 |
lunch full-eng
第二步是调用 lunch 函数,并指定参数为“full-eng”。lunch 函数的参数用来指定此次编译的目标设备以及编译类型。在这里,这两个值分别是“full”和“eng”。“full”是 Android 源码中已经定义好的一种产品,是为模拟器而设置的。而编译类型会影响最终系统中包含的模块,下图所示是三种编译类型,前面的文章只是稍微提了一下:
名称 | 说明 |
---|---|
eng | 默认类型,该编译类型适用于开发阶段。 当选择这种类型时,编译结果将:
|
user | 该编译类型适合用于最终发布阶段。 当选择这种类型时,编译结果将:
|
userdebug | 该编译类型适合用于 debug 阶段。 该类型和 user 一样,除了:
|
第三步执行命令
make -j2
才真正开始执行编译。make 的参数“-j”指定了同时编译的 Job 数量,这是个整数,该值通常是编译主机 CPU 支持的并发线程总数的 1 倍或 2 倍(例如:在一个 4 核,每个核支持两个线程的 CPU 上,可以使用 make -j8 或 make -j16,当然也要考虑机器的内存大小!!!)。
所有的编译产物都将位于 /out 目录下,该目录下主要有以下几个子目录:
前面我们编译的是模拟器版本的android系统,他的产品名称是generc.
首先在android源码的根目录下有一个makefile文件,如下图所示,可知其指向了build/core/中的main.mk。
好了,我们已经非常简单的介绍了一下android编译的三个步骤,现在我们考虑一个问题,大家都知道android是开源的操作系统,各个手机厂商都可以使用。android系统源码,大家也看到了,非常的庞大与复杂。好在谷歌工程师早就想好了对策,所有android编译都是模块化的,按照谷歌官方推荐的规则去添加自己的产品即可。
接下来我直接告诉大家添加一个新设备需要的几个重要的文件
vendorsetup.sh AndroidProduct.mk 产品定义文件 BoardConfig.mk
在说这几个文件的作用前,先以源码中自带的产品为例,宏观上介绍一下。谷歌公司推荐公司把自己的产品都添加到android源码目录中的device 文件夹中。
如下图所示是device文件夹中的内容,可以看到里面大多数是以公司名为名字的文件夹,比如飞思卡尔,三星,TI等。这个也很容易想嘛,就拿三星公司来说,人家旗下的android设备远远不止一种啊,
所以为了方便起见,device目录下都是公司名字,公司文件夹下存放的是该公司的具体产品设备。
以samsung公司的toro产品为例,说一下如何将这个产品添加到android系统源码中去的。
首先要做的肯定是要在执行lunch命令后,出现的产品列表中显示这个产品吧。。。
vendorsetup.sh 这个文件就是干这个事情的,那么他在哪里呢???肯定是在产品的目录里面了。由下图可以看出来,该文件在产品的根目录下,其内容也就是一行代码而已。add_lunch_combo是一个函数,当执行完source build/envsetup.sh后,该函数就可用了,他的作用就是添加一个产品到lunch显示列表中去,full_toro-userdebug格式为“产品名称+编译类型”。
光是这样还不够啊。以三星手机为例,同一款手机人家还分韩版,欧版,港版,大陆版。。。所以你要告诉android编译系统,你编译的是哪一个吧。
AndroidProduct.mk文件顾名思义啊,就是干这个事情的。该文文件中的内容很简单,其中只需要定义一个变量,名称为“PRODUCT_MAKEFILES”,该变量的值为产品版本定义文件名的列表,例如:
PRODUCT_MAKEFILES := $(LOCAL_DIR)/full_toro.mk
从中可以看出上述命令中的full_toro.mk才是产品真正的定义文件呢,其内容如下,可以看出该文件中定义了产品的名称,设备名称等,还指定了一些文件的拷贝路径。
PRODUCT_NAME:表示产品名字,它要和最终出现的编译项产品名一致。
PRODUCT_DEVICE:表示设备名字,它要和将来创建的设备目录名字一致。
BoardConfig.mk文件用来配置设备的硬件信息,例如:该设备的CPU架构,Wifi 相关信息,还有 bootloader,内核,大小位置等信息。
好,到这里我们已经知道如何添加一个新的产品到android系统中了。
下面列出了,在android目录中执行make 命令时,后面可以跟哪些目标。
Make 目标 | 说明 |
---|---|
make clean | 执行清理,等同于:rm -rf out/。 |
make sdk | 编译出 Android 的 SDK。 |
make clean-sdk | 清理 SDK 的编译产物。 |
make update-api | 更新 API。在 framework API 改动之后,需要首先执行该命令来更新 API,公开的 API 记录在 frameworks/base/api 目录下。 |
make dist | 执行 Build,并将 MAKECMDGOALS 变量定义的输出文件拷贝到 /out/dist 目录。 |
make all | 编译所有内容,不管当前产品的定义中是否会包含。 |
make help | 帮助信息,显示主要的 make 目标。 |
make snod | 从已经编译出的包快速重建系统镜像。 |
make libandroid_runtime | 编译所有 JNI framework 内容。 |
makeframework | 编译所有 Java framework 内容。 |
makeservices | 编译系统服务和相关内容。 |
make |
编译一个指定的模块,local_target 为模块的名称。 |
make clean- |
清理一个指定模块的编译结果。 |
makedump-products | 显示所有产品的编译配置信息,例如:产品名,产品支持的地区语言,产品中会包含的模块等信息。 |
makePRODUCT-xxx-yyy | 编译某个指定的产品。 |
makebootimage | 生成 boot.img |
makerecoveryimage | 生成 recovery.img |
makeuserdataimage | 生成 userdata.img |
makecacheimage | 生成 cache.img |