这篇文章接着前面的<<Android编译系统分析(一)>>继续, 主要分析Android OS以及module的编译行为. 首先先说明一下几个重要的makefile文件:
build/core/main.mk, 这是入口文件, 编译系统将从这里开始
build/core/Makefile, 系统内置目标的规则定义基本都在这个文件里面实现, 由main.mk导入.
build/core/base_rules.mk, 处理module的基本信息, 并提供给编译核心使用, 一般在Android.mk的导入链上的合适地方被导入进来. 其定义了丰富的变量可以供编译核心来处理当前这个module.
build/core/definitions.mk, 实现了一系列基本而重要的函数, 这样能够有效地把处理通用规则的代码抽取出来给各个模块重复使用.
1 主体流程描述
最开始是环境检查,在Android编译系统分析一当中已经分析过, 比较简单, 不再赘述.
TARGET_BUILD_VARIANT
该变量可选值(user,userdebug,eng),其各个值对编译后的系统的影响,可直接查看android官网描述:
eng |
This is the default flavor.
|
user |
This is the flavor intended to be the final release bits. Installs modules tagged with user. Installs modules according to the product definition files, in addition to tagged modules.
|
userdebug |
The same as user, except: Also installs modules tagged with debug.
|
意思已经说的比较清楚了,有两点特别说明一下:
ro.secure/ro.debuggable这两个property主要影响adb的行为,也就是adbd是否以root身份运行。
表格里面提到的tag, 就是各个模块Android.mk里面变量LOCAL_MODULE_TAG,其通常的取值是: eng/userdebug或者默认的optional. 如果模块没有定义这个变量,默认的optional表示OS仅装载PRODUCT_PACKAGES列表中声明的模块。这个变量不要定义成user,在5.0及以上系统中不再可用(5.0以下的没考察过,不清楚从哪个版本开始的),base_rules.mk里面会检查并报错。
在编译的时候如何选择某个VARIANT呢?看下图:
每个combo的最后一个字段就是VARIANT了,这个在<<Android编译系统分析(一)>>中已经分析过了。这里是aosp的源码,没有user的combo,实际上是有用的,一般是设备厂商最后发布的release版本。
回到make.mk文件,VARIANT最后会确定tags_to_install变量的内容,也就是最后将要被install的模块的tags(也就是模块可以声明成多个tag).
编译SDK
编译目标中含有: sdk win_sdk sdk_addon,则会编译SDK包(is_sdk_build变量被激活),如果HOST系统是windows的话,则只会编译SDK,也就是SDK_ONLY变量会被激活。
SDK_ONLY被激活,则对应的FULL_BUILD为空, 也就意味着product_FILES变量为空,这个变量可以认为就是PRODUCT_PACKAGES,所以也就是说大多数模块在这种模式下都不会被编译的,但是,被打上tags_to_install当中的tag的模块,还是会被编译的。
is_sdk_build会指示从目标编译模块中摘除GNU目标,可能是GPL授权的原因吧,gnu工具编译系统本身可以用用(作为host的目标制作出来),但是肯定是不能在sdk包中发布的。
除了在配置上被以上两个变量控制了以外, sdk的目标规则通过内部目标INTERNAL_SDK_TARGET定义在Makefile文件当中, 具体的这里不再赘述.
关于all_modules目标
这个目标一般是配合mm/mmm等shell函数使用(详见<<Android编译系统分析(一)>>), 它的定义如下
.PHONY: all_modules
ifndef BUILD_MODULES_IN_PATHS
all_modules: $(ALL_MODULES)
else
# BUILD_MODULES_IN_PATHS is a list of paths relative to the top of the tree
module_path_patterns := $(foreach p, $(BUILD_MODULES_IN_PATHS),\
$(if $(filter %/,$(p)),$(p)%,$(p)/%))
my_all_modules := $(sort $(foreach m, $(ALL_MODULES),$(if $(filter\
$(module_path_patterns), $(addsuffix /,$(ALL_MODULES.$(m).PATH))),$(m))))
all_modules: $(my_all_modules)
endif
在mm/mmm函数里没有定义BUILD_MODULES_IN_PATHS, 也就是上面的代码走第一个分支all_modules: $(ALL_MODULES), 那么ALL_MODULES在哪里? 这两个命令同时还定义了ONE_SHOT_MAKEFILE变量, 就是当前module的Android.mk, 当该变量非空, main.mk只会单独导入该Android.mk, 否则main.mk会扫描整个工程的Android.mk并都导入进来. 这条导入链最终会导入到base_rules.mk, 该文件里面定义了ALL_MODULES += $(my_register_name), 即ALL_MODULES += $(LOCAL_MODULE), 也即all_modules: $(LOCAL_MODULE). 所以这两个函数会借助all_modules编译当前module当中的所有LOCAL_MODULE.
在mma/mmma函数里少了ONE_SHOT_MAKEFILE, 多了BUILD_MODULES_IN_PATHS. 所以上面的代码走第二个分支. ONE_SHOT_MAKEFILE没有意味着会扫描所有的Android.mk,这个可以理解,毕竟是带着依赖模块一起编译,自然要扫描工程的。至于BUILD_MODULES_IN_PATHS变量,简单点说,mma里面就是PWD. 意义是:mm编译时, ALL_MODULES就是ONE_SHOT_MAKEFILE指向的Android.mk里面定义的LOCAL_MODULE,所以all_modules直接依赖就可以,而mma的ALL_MODULES则代表工程中所有Android.mk,因此不能直接使用,所以只能通过路径与module的对应关系找到需要编译的modules,然后制作成all_modules的依赖链.这个对应关系在base_rulse.mk里面处理,是为$(ALL_MODULES.$(module).PATH)与module的对应.
CUSTOM_MODULES
这个变量记录:
a.没有定义在PRODUCT_PACKAGES列表中的module;
b.没有对应到tags_to_install当中的tag的module.