原文网址:http://www.cloudchou.com/android/post-276.html
1.概述
编译Android的第三步是使用mka命令进行编译,当然我们也可以使用make –j4,但是推荐使用mka命令。因为mka将自动计算-j选项的数字,让我们不用纠结这个数字到底是多少(这个数字其实就是所有cpu的核心数)。在编译时我们可以带上我们需要编译的目标,假设你想生成recovery,那么使用mka recoveryimage,如果想生成ota包,那么需要使用mka otapackage,后续会介绍所有可以使用的目标。另外注意有一些目标只是起到修饰的作用,也就是说需要和其它目标一起使用,共有4个用于修饰的伪目标:
- 1) showcommands 显示编译过程中使用的命令
- 2) incrementaljavac用于增量编译java代码
- 3) checkbuild用于检验那些需要检验的模块
- 4) all如果使用all修饰编译目标,会编译所有模块
研究Android编译系统时最头疼的可能是变量,成百个变量我们无法记住其含义,也不知道这些变量会是什么值,为此我专门做了一个编译变量的参考网站android.cloudchou.com,你可以在该网站查找变量,它能告诉你变量的含义,也会给出你该变量的示例值,另外也详细解释了编译系统里每个Makefile的作用,这样你在看编译系统的代码时不至于一头雾水。
编译的核心文件是build/core/main.mk和build/core/makefile,main.mk主要作用是检查编译环境是否符合要求,确定产品配置,决定产品需要使用的模块,并定义了许多目标供开发者使用,比如droid,sdk等目标,但是生成这些目标的规则主要在Makefile里定义,而内核的编译规则放在build/core/task/kernel.mk
我们将先整体介绍main.mk的执行流程,然后再针对在Linux上编译默认目标时使用的关键代码进行分析。Makefile主要定义了各个目标的生成规则,因此不再详细介绍它的执行流程,若有兴趣看每个目标的生成规则,可查看http://android.cloudchou.com/build/core/Makefile.php
2. main.mk执行流程
2.1 检验编译环境并建立产品配置
- 1) 设置Shell变量为bash,不能使用其它shell
- 2) 关闭make的suffix规则,rcs/sccs规则,并设置一个规则: 当某个规则失败了,就删除所有目标
- 3) 检验make的版本,cygwin可使用任意版本make,但是linux或者mac只能使用3.81版本或者3.82版本
- 4) 设置PWD,TOP,TOPDIR,BUILD_SYSTEM等变量,定义了默认目标变量,但是暂时并未定义默认目标的生成规则
- 5) 包含build/core/help.mk,该makefile定义了两个目标help和out, help用于显示帮助,out用于检验编译系统是否正确
- 6) 包含build/core/config.mk,config.mk作了很多配置,包括产品配置,包含该makefile后,会建立输出目录系列的变量,还会建立PRODUCT系列变量,后续介绍产品配置时,对此会有更多详细介绍
- 7) 包含build/core/cleanbuild.mk,该makefile会包含所有工程的CleanSpec.mk,写了CleanSpec.mk的工程会定义每次编译前的特殊清理步骤,cleanbuild.mk会执行这些清除步骤
- 8) 检验编译环境,先检测上次编译结果,如果上次检验的版本和此次检验的版本一致,则不再检测,然后进行检测并将此次编译结果写入
2.2 包含其它makefile及编译目标检测
- 1) 如果目标里含有incrementaljavac, 那么编译目标时将用incremental javac进行增量编译
- 2) 设置EMMA_INSTRUMENT变量的值,emma是用于测试代码覆盖率的库
- 3) 包含build/core/definistions.mk,该makefile定义了许多辅助函数
- 4) 包含build/core/qcom_utils.mk,该makefile定义了高通板子的一些辅助函数及宏
- 5) 包含build/core/dex_preopt.mk,该makefile定义了优化dex代码的一些宏
- 6) 检测编译目标里是否有user,userdebug,eng,如果有则告诉用户放置在buildspec.mk或者使用lunch设置,检测TARGET_BUILD_VARIANT变量,看是否有效
- 7) 包含build/core/pdk_config.mk, PDK主要是能提高现有设备升级能力,帮助设备制造商能更快的适配新版本的android
2.3 根据TARGET_BUILD_VARIANT建立配置
- 1) 如果编译目标里有sdk,win_sdk或者sdk_addon,那么设置is_sdk_build为true
- 2) 如果定义了HAVE_SELINUX,那么编译时为build prop添加属性ro.build.selinux=1
- 3) 如果TARGET_BUILD_VARIANT是user或者userdebug,那么tags_to_install += debug 如果用户未定义DISABLE_DEXPREOPT为true,并且是user模式,那么将设置WITH_DEXPREOPT := true,该选项将开启apk的预优化,即将apk分成odex代码文件和apk资源文件
- 4) 判断enable_target_debugging变量,默认是true,当build_variant是user时,则它是false。如果该变量值为true,则设置Rom的编译属性ro.debuggable为1,否则设置ro.debuggable为0
- 5) 如果TARGET_BUILD_VARIANT是eng,那么tags_to_install为debug,eng, 并设置Rom的编译属性ro.setupwizard.mode为OPTIONAL,因为eng模式并不要安装向导
- 6) 如果TARGET_BUILD_VARIANT是tests,那么tags_to_install := debug eng tests
- 7) 设置sdk相关变量
- 8) 添加一些额外的编译属性
- 9) 定义should-install-to-system宏函数
- 10) 若除了修饰目标,没定义任何目标,那么将使用默认目标编译
2.4 包含所有要编译的模块的Makefile
如果编译目标是clean clobber installclean dataclean,那么设置dont_bother为true,若dont_bother为false,则将所有要编译的模块包含进来
1) 如果主机操作系统及体系结构为darwin-ppc(Mac电脑),那么提示不支持编译Sdk,并将SDK_ONLY设置为true
2) 如果主机操作系统是windows,那么设置SDK_ONLY为true
3) 根据SDK_ONLY是否为true,编译主机操作系统类型,BUILD_TINY_ANDROID的值,设置sudbidrs变量
4) 将所有PRODUCT_*相关变量存储至stash_product_vars变量,稍后将验证它是否被修改
5) 根据ONE_SHOT_MAKEFILE的值是否为空,包含不同的makefile
6) 执行post_clean步骤,并确保产品相关变量没有变化
7) 检测是否有文件加入ALL_PREBUILT
8) 包含其它必须在所有Android.mk包含之后需要包含的makefile
9) 将known_custom_modules转化成安装路径得到变量CUSTOM_MODULES
10) 定义模块之间的依赖关系,$(ALL_MODULES.$(m).REQUIRED))变量指明了模块之间的依赖关系
11) 计算下述变量的值:product_MODULES,debug_MODULES,eng_MODULES,tests_MODULES,modules_to_install,overridden_packages,target_gnu_MODULES,ALL_DEFAULT_INSTALLED_MODULES
12) 包含build/core/Makefile
13) 定义变量modules_to_check
2.5 定义多个目标
这一节定义了众多目标,prebuilt,all_copied_headers,files,checkbuild,ramdisk,factory_ramdisk,factory_bundle,systemtarball,boottarball,userdataimage,userdatatarball,cacheimage,bootimage,droidcore,dist_files,apps_only,all_modules,docs,sdk,lintall,samplecode,findbugs,clean,modules,showcommands,nothing。
后续文章将列出所有可用的目标
3 编译默认目标时的执行流程
在介绍编译默认目标时的执行流程之前,先介绍一下ALL_系列的变量,否则看代码时很难搞懂这些变量的出处,这些变量在包含所有模块后被建立,每个模块都有对应的用于编译的makefile,这些makefile会包含一个编译类型对应的makefile,比如package.mk,而这些makefile最终都会包含base_rules.mk,在base_rules.mk里会为ALL系列变量添加值。所有这些变量及其来源均可在android.cloudchou.com查看详细解释:
- 1) ALL_DOCS所有文档的全路径,ALL_DOCS的赋值在droiddoc.mk里, ALL_DOCS += $(full_target)
- 2) ALL_MODULES系统的所有模块的简单名字集合,编译系统还为每一个模块还定义了其它两个变量,ALL_MODULES.$(LOCAL_MODULE).BUILT 所有模块的生成路径ALL_MODULES.$(LOCAL_MODULE).INSTALLED 所有模块的各自安装路径,详情请见http://android.cloudchou.com/build/core/definitions.php#ALL_MODULES
- 3) ALL_DEFAULT_INSTALLED_MODULES 所有默认要安装的模块,在build/core/main.mk和build/core/makfile里设置
- 4) ALL_MODULE_TAGS 使用LOCAL_MODULE_TAGS定义的所有tag集合,每一个tag对应一个ALL_MODULE_TAGS.变量,详情请见http://android.cloudchou.com/build/core/definitions.php#ALL_MODULE_TAGS
- 5) ALL_MODULE_NAME_TAGS类似于ALL_MODULE_TAGS,但是它的值是 某个tag的所有模块的名称 详情请见http://android.cloudchou.com/build/core/definitions.php#ALL_MODULE_NAME_TAGS
- 6) ALL_HOST_INSTALLED_FILES 安装在pc上的程序集合
- 7) ALL_PREBUILT 将会被拷贝的预编译文件的安装全路径的集合
- 8) ALL_GENERATED_SOURCES 某些工具生成的源代码文件的集合,比如aidl会生成java源代码文件
- 9) ALL_C_CPP_ETC_OBJECTS 所有asm,c,c++,以及lex和yacc生成的c代码文件的全路径
- 10) ALL_ORIGINAL_DYNAMIC_BINARIES 没有被优化,也没有被压缩的动态链接库
- 11) ALL_SDK_FILES 将会放在sdk的文件
- 12) ALL_FINDBUGS_FILES 所有findbugs程序用的xml文件
- 13) ALL_GPL_MODULE_LICENSE_FILES GPL 模块的 许可文件
- 14) ANDROID_RESOURCE_GENERATED_CLASSES Android 资源文件生成的java代码编译后的类的类型
3.1 关键代码
定义默认目标的代码位于main.mk:
1 |
.PHONY: droid |
droid目标依赖的目标有:
1 |
ifneq ($(TARGET_BUILD_APPS),) |
dist_files目标依赖的目标主要是一些用于打包的工具,它们都是用dist-for-goals宏添加依赖关系的:
1 |
$(call dist-for-goals, dist_files, $(EMMA_META_ZIP)) |
我们再看droidcore目标依赖的目标有:
1 |
droidcore: files \ |
system.img, boot.img, recovery.img, data.img,cache.img,installed_files.txt的生成规则在Makefile里定义, 在http://android.cloudchou.com/build/core/Makefile.php里可以看到详细的生成规则 再看一下files目标所依赖的目标:
1 |
files: prebuilt \ |
prebuilt目标依赖$(ALL_PREBUILT),android-info.txt的生成规则在target/board/board.mk里定义,而$(modules_to_install)目标是所有要安装的模块的集合,计算比较复杂,现在以在linux下编译默认目标为例,将涉及到的代码组织如下:
1 |
…… |