Android编译系统集中于build/core下,几个很重要的*.mk文件如下:
main.mk(主控
Makefile)
base_rules.mk(对一些
Makefile的变量规则化)
config.mk(关于编译参数、编译命令的一些配置)
definations.mk(定义了很多编译系统中用到的宏,相当于函数库)
Makefile(这个
Makefile特指build/core下的Makefile,此文件主要控制生成system.img,ramdisk.img,userdata.img,以及recorvery image,sdk等)
Binary.mk(控制如何生成目标文件
)
Clear_vars.mk(清除编译系统中用到的临时变量)
Combo/linux-arm.mk(控制如何生成
linux-arm二进制文件,包括ARM相关的编译器,编译参数等的设置)
Copy_headers.mk(将头文件拷贝到指定目录)
分散于各个目录下的
Android.mk(控制生成局部模块的源码,名称所需头文件路径,依赖库等特殊选项)
Build/envsetup.mk(编译环境初始化,定义一些实用的
shell函数,方便编译使用)
以上几个主要的文件,可以按照社会分工打一个比方:
Main.mk是总统,是老大,承担了很多工作。
Makefile是副总统,辅佐老大
Main.mk
Base_rules.mk是交警,让不规则的东西,变得规则。
Config.mk是省长,规定了各个人民群众该如何行事
Definations.mk是图书馆管理员
Binary.mk应该属于村长了,规定每个人该如何行事
Clear_vars.mk应该属于保洁公司的工人吧
Combo/linux-arm.mk应该属于社会公民了,他决定自己该如何去做
Main.mk分析
Main.mk主要包含如下几个部分的内容
1.
SHELL设置
2.
编译环境配置
3.
编译环境检查
4.
包含必要的宏
5.
根据
make参数设置编译时的变量
6.
包含需要编译的
Android.mk
7.
设置编译系统
Target:prerequisites 控制整个编译流程
下面对上面几点进行必要解释:
Main.mk的第一句就根据
ANDROID_BUILD_SHELL
来包裹编译系统用到的Shell,如果我们不想使用bash,而想使用sh,那么就可以在它前面写上ANDROID_BUILD_SHELL := /bin/sh,或者在build/envsetup.sh中添加相关定义。(内容如下图)
定义完
SHELL之后,就是对MAKE_VERSION的检查,然后定义了默认的编译目标droid!如果我们敲入
make之后,不加任何参数,默认的目标就是droid。注意虽然后面的include $(BUILD_SYSTEM)/config.mk写在默认目标droid依赖之后,但其和之后的语句都是要执行的,这是Makefile的语法决定的。(内容如下图)
后面会include config.mk cleanbuild.mk对编译系统进行必要的配置。后面就是对编译环境的检查,包括是否大小写敏感、路径检查、java版本检查、javac版本检查。Android对编译环境的检查如果符合条件,在下次编译的时候,不会再次进行检查。(内容如下图)
检查完版本之后,会包含进definitions.mk,如前所述,definitions.mk中定义了很多编译系统中用到的宏,这些宏在编译时需要经常调用,因此在编译的很靠前的阶段,就将之包含了进来。(内容如下图)Definitions.mk中的内容自己有时间在回顾一下,里面定义了很多宏.
然后就是针对make时传入的编译类型(eng user userdebug showcommands等)进行编译配置,这些配置会影响到最终编译目标所包括的模块。对于eng user userdebug sdk win_sdk tests等编译目标的区别,可以通过查看main.mk的代码找出其中到底有什么不同。(内容如下图)
(
在编译完整个系统之后,再运行make sdk,就可以进行sdk 的编译了。make sdk 将各种工具和image 打包,供开发和调试使用。
注: Make 命令
make droid:等同于make 命令。droid 是默认的目标名称。
make all: make all 将make 所有make droid 会编译的项目。同时,将编译
LOCAL_MODULE_TAGS 定义的不包括android tag 的模块。这将确保所有的在代码树里面同时有Android.mk 文件的模块。
clean-$(LOCAL_MODULE)和clean-$(LOCAL_PACKAGE_NAME):
删除某个模块的目标文件。例如:
clean-libutils 将删除所有的libutils.so 以及和它相关的中间文件;
clean-Home 将删除Home 应用。
make clean:删除本次配置所编译输出的结果文件。类似于:rm –rf ./out/ <configuration>
make clobber:删除所有配置所编译输出的结果文件。类似于:rm –rf ./out/
make dataclean:make dataclean deletes contents of the data directory inside the
current combo directory. This is especially useful on the simulator and emulator, where
the persistent data remains present between builds.
make showcommands在编译的时候显示脚本的命令而不是显示编译的简报。用于调试脚本。
make LOCAL_MODULE:编译一个单独得模块(需要有Android.mk 文件存在)。
make targets:将输出所有拟可以编译的模块名称列表。还有一些命令,从make 文件里面应该可以找到
。)
Ifeq($(SDK_ONLY),true)处,大概
400行上下,这个判断语句一直到这个语句块结束,都是对subdirs变量的设置,subdirs变量决定了哪些子文件夹最终被编译。在后面的subdir_makefiles变量的设置,决定了哪些Android.mk被编译。紧接着include $(subdir_makefiles)就会添加所有这些Android.mk文件的依赖。这其中包含了droid的依赖,后面我们会发现。(内容如下图)
本地模块的
Makefile文件就是我们在Android 里面几乎上随处可见的Android.mk。Android 进行编译的时候会通过下面的函数来遍历所有子目录中的Android.mk,一旦找到就不会再往层子目录继续寻找(所有你的模块定义的顶层Android.mk 必须包含自己定义的子目录中的Android.mk)。subdir_makefiles += \$(shellbuild/tools/findleaves.sh --prune="./out" $(subdirs) Android.mk)不同类型的本地模块具有不同的语法,但基本上是相通的,只有个别变量的不同,如何添加模块在前面,如上图就是公司自己添加的模块。
然后就会根据这些
Makefile,找出所有需要编译的模块(module),以及进行必要的分类(eng_MODULES/debug_MODULES/tests_MODULES等、modules_to_check/modules_to_install等),用以区别对待。紧接着是定义了一系列的隐含目标:
prebuilt、all_copied_headers、files、checkbuild、ramdisk、systemtallball、userdataimage、userdatatarball、bootimg、droidcore等。最重要的一点,是会发现droid依赖于droidcore,而droidcore依赖于
droidcore: files \
systemimage \
$(INSTALLED_BOOTIMAGE_TARGET) \
$(INSTALLED_RECOVERYIMAGE_TARGET) \
$(INSTALLED_USERDATAIMAGE_TARGET) \
$(INSTALLED_FILES_FILE)
正是这几个依赖项,控制着整个
android的编译。
当我们敲
Make实际上就等同于我们执行make droid。当Make include所有的文件,完成对所有make我文件的解析以后就会寻找生成droid的规则,依次生成它的依赖,直到所有满足的模块被编译好,然后使用相应的工具打包成相应的img。
config.mk分析
build/core/config.mk该文件被
main.mk包含。config.mk 文件的主要内容如下:头文件的定义;(各种
include 文件夹的设定)在定义头文件的部分,还include 了pathmap.mk,如下:include $(BUILD_SYSTEM)/pathmap.mk
该文件设置
include 目录和frameworks/base 下子目录等的信息。编译系统内部
mk 文件的定义; <Build system internal files>。(内容如下图)
设定通用的名称;
<Set common values>
Include 必要的子配置文件;
<Include sub-configuration files>
buildspec.mk
envsetup.mk
BoardConfig.mk
/combo/select.mk
/combo/javac.mk
。(内容如下图)
检查
BUILD_ENV_SEQUENCE_NUMBER 版本号;
In order to make easier for people when the build system changes, when it is necessary
to make changes to buildspec.mk or to rerun the environment setup scripts, they contain
a version number in the variable BUILD_ENV_SEQUENCE_NUMBER. If this variable does
not match what the build system expects, it fails printing an error message explaining
what happened. If you make a change that requires an update, you need to update two
places so this message will be printed.
· In config/envsetup.make, increment the
CORRECT_BUILD_ENV_SEQUENCE_NUMBER definition.
· In buildspec.mk.default, update the BUILD_ENV_SEQUENCE_DUMBER definition
to match the one in config/envsetup.make
The scripts automatically get the value from the build system, so they will trigger the
warning as well.。(内容如下图)
设置常用工具的常量;
< Generic tools.>
设置目标选项;
< Set up final options.>
遍历并设置
SDK 版本;
envsetup.mk分析
125行又包含了另外一个重要的
mk文件envsetup.mk,我们来看一下。
上面的代码是指定了目标输出代码的位置和主机输出代码的位置,重要的几个如下:PRODUCT_OUT = 这个的结果要根据
product_config.mk文件内容来决定。envsetup.mk文件主要包含了
product_config.mk文件,然后指定了编译时要输出的所有文件的OUT目录。
envsetup.mk又包含了product_config.mk文件
在
build/core/product_config.mk又包含了product.mk文件。
因此
android编译系统通过各种依赖关系、确保某个模块的修改引起相依赖的文件的重新编译链接,甚至还包括目标文件系统的生成,配置文件的生成等。