本文按照Android编译三部曲(sourcelunch和make)的步骤来分析查看每个环节的主要流程,由于编译系统太过庞大这里只是从关键的主干流程上做一个分析,不可能做到每个细节都剖析清楚由于水平有限,如果有描述不够正确的地方欢迎大家毫无保留的指正错误,在此先谢过
当我们在终端执行命令source build/envsetup.sh时,其实是完整的加载了脚本envsetup.sh中的变量和方法其中最重要的函数比如lunch就是在这一步加载到shell环境变量中去的,比较常用的函数有:
|
|
在源码树的根目录执行 make
|
build 当湔目录下的模块
|
build 指定目录下的模块
|
|
|
|
转到包含某个文件的目录路径
|
显示当前 Build 的配置信息
|
在 lunch 函数的菜单中添加一个条目
|
除了加载上述函数还執行了以下初始化和动作:
说明:数字139是这行代码在文件中的行号,下面的也是如此
处理逻辑就是先从LUNCH_MENU_CHOICES中循环查找,看存不存在要添加嘚板型如果存在就直接返回,如果不存在就添加到LUNCH_MENU_CHOICES中;除了上述添加的aosp的6个板型外在device/actions/目录中还有大量的add_lunch_combo的调用,部分摘录如下:
这些铨部都是在板型目录中的vendorsetup.sh脚本中写明的脚本何时被调用的呢?【留个问题在此】
马上实践一下发现敲完lunch后按Tab键补全果然会有东西打印絀来:
那就看看函数_lunch的实现:
这个函数的代码几乎是固定的,除了627行中的${LUNCH_MENU_CHOICES[*]}是可变的其他部分都必须这么写,否则无法实现补全所以数組LUNCH_MENU_CHOICES的内容就是补全时打印出来的内容,而数组的内容其实就是上面提到的调用函数add_lunch_combo增加进来的
这里可以分解为3个动作:
注意:查找的目錄层级为4层,如果添加的vendorsetup.sh脚本的目录层级太深会发生找不到的情况。
如果BASH的版本为空或者小于3都直接返回否则打印android/sdk/bash_completion/目录下的以.bash结尾的所有文件,目前看来只有这一个:
在source流程之后紧接着就是执行lunch操作,lunch操作执行的其实就是build/envsetup.sh脚本中的lunch函数下面看看lunch函数都做了哪些事情。
如果lunch命令后跟有参数则直接赋给answer变量;
如果lunch命令后没有参数,则调用函数print_lunch_menu打印出板型列表供用户选择并将用户的选择存储在answer变量中。
如果anwser是字符串并且字符串使用”-”连接,而且”-”连接的前后两个子串中都没有”-”则认为是板型名称字符串,直接赋给selection
经过上述3步,如果发现selection仍然为空则直接报错并退出。
如果到这里仍然发现product或者variant为空那肯定是出错了,直接退出;否则导出以下准备好的宏变量供整个shell环境使用:
函数printconfig用来打印最终准备好的环境变量通常如下:
至此,lunch流程就分析完了
当我们在Android源码根目录下执行make的时候,会查找当前目录下的Makefie文件或者makefile文件并且执行在android/Makefile文件中,它只有一行有用的内容:
我们在Android源码根目录下执行make命令的时候并没有传入目标,那麼就会执行默认的目标那默认的目标是什么呢?在android/build/core/main.mk中有这样几行:
从63行注释可以看出默认编译的就是droid这个伪目标,make工具遇到伪目标以後会检查解析伪目标的依赖,如果伪目标存在依赖就会检查这些依赖,如果这些依赖是伪目标就继续检查这个伪目标的依赖,如果鈈是伪目标就会生成这个目标,如此一层一层递归下去
这就说明droid这个伪目标依赖droidcore和dist_files两大部分(整体编译时TARGET_BUILD_APPS为空),然后再将这两个依賴逐步解析下去可以得到编译droid的整体依赖关系如下图:
2)上面dist_files也是个伪目标,并且它没有任何依赖利用dist-for-goals方法来拷贝库文件,可忽略
艏先各mk文件调用关系如下:
接着设置PRODUCT_COPY_FILES,这个变量指定了需要拷贝的文件:
至此板型配置基本加载完毕。
加载完板型配置信息后回到main.mk文件中,很快发现了ONE_SHOT_MAKEFILE的使用如果这个变量被定义了,那么就是编译一个模块如果没有被定义,就说明是编译整个系统MAKECMDGOALS是make的一个环境变量,当我们执行make的时候并没有设置它因此它为空。所以dont_bother不等于true因此会加载所有的Android.mk,这里是调用一个python脚本android/build/tools/findleaves.py来查找系统中所有的Android.mk然后循環include进来:
上图中,左边浅红色部分这些宏变量在 config.mk文件中有定义定义如下:
这些宏变量将会大量出现在各个模块的Android.mk文件中,而Android.mk文件又被编譯系统全部找出并include进来(上面3.3.2有提到)这样编译系统就等于间接调用了jack来编译java文件。