对于Android研发的同学来说,编译Android系统的过程并不陌生。
首先:source build/envsetup.sh - 执行build路径下的envsetup.sh脚本。
其次:lunch - 选择一个特定的编译产品类型。
最后:make - 执行编译命令,产生编译结果,生成在out/target/product/xxx/下。
相关代码参考使用android-13.0.0_r3版本:http://aospxref.com/android-13.0.0_r3/
更多技术文章,全网首发公众号 “极客钛” 锁定 -上午11点 - ,感谢大家关注、转发、点赞!
1.source build/envsetup.sh原理
执行的envsetup.sh,我们只需要看懂这个脚本既可理解。
envsetup.sh这个脚本主要是提供了一些函数和命令,提供给编译使用。其中就包含了我们后面会使用到的lunch和make命令。
http://aospxref.com/android-13.0.0_r3/xref/build/envsetup.sh
第一个命令 - hmm
执行hmm可以看到这个脚本支持的命令,第一行cat <<EOP,意思就是把EOF后面到下一个EOF前面的内容当做一个文件,然后cat 会接收这个文件的内容,而cat默认的输出是标准输出,也就是这个文件的内容会被打印到屏幕上来。
hmm命令函数最后部分的原理,如下。
1.gettop就是获取Android源码的根目录。
2.for循环获取根目录下的build/envsetup.sh脚本中所有的function功能,并进行显示。
执行hmm命令最后的结果:
在这里,罗列了一些主要的函数命令用法:
function help() # 显示帮助信息
function get_abs_build_var() # 获取绝对变量
function get_build_var() # 获取绝对变量
function check_product() # 检查product
function check_variant() # 检查变量
function setpaths() # 设置文件路径
function printconfig() # 打印配置
function set_stuff_for_environment() # 设置环境变量
function set_sequence_number() # 设置序号
function settitle() # 设置标题
function choosetype() # 设置type
function chooseproduct() # 设置product
function choosevariant() # 设置variant
function tapas() # 功能同choosecombo
function choosecombo() # 设置编译参数
function add_lunch_combo() # 添加lunch项目
function print_lunch_menu() # 打印lunch列表
function lunch() # 配置lunch
function m() # make from top
function findmakefile() # 查找makefile
function mm() # make from current directory
function mmm() # make the supplied directories
function croot() # 回到根目录
function cproj()
function pid()
function systemstack()
function gdbclient()
function jgrep() # 查找java文件
function cgrep() # 查找c/cpp文件
function resgrep()
function tracedmdump()
envsetup.sh脚本最后会执行三个命令:
1.validate_current_shell - 验证当前的shell环境
2.source_vendorsetup - 执行vendorsetup脚本
3.addcompletions -确保bash环境
其中第二步是芯片厂中自行定制化的一步,qcom和mtk等都会使用自家的vendorsetup脚本,我们来看下如何执行vendorsetup脚本。
在获取到Android源码的根目录下,使用shell命令find查找,设定了查找深度为4层目录,4级目录下的是否存在allowed-vendorsetup_sh-files,-n 是判断是否不为空,然后再排序,依次执行。
同理,查找vendorsetup.sh后再依次执行。
一般vendorsetup.sh都在device下的,对应厂商名字的文件夹下,例如mtk平台的Android代码在执行 source build/envsetup.sh时,会引用mtk平台的vendorsetup。
通过基本的流程,可以看出envsetup.sh作用如下:
1.加载了编译时使用到的函数命令,如:help,lunch,m,mm,mmm等。
2.添加了默认编译选项:aosp_arm-eng等系统默认选项(lunch操作在下一个章节介绍)。
3.查找build/<厂商目录>/ 目录下的vendorsetup.sh,如果存在的话,加载执行它,添加厂商自己定义产品的编译选项。
其他的函数命令在编译的使用中再对照envsetup.sh脚本的函数进行分析,不会一一介绍。
2.lunch原理
当执行完souce build/envsetup.sh之后,可以执行lunch进入到菜单进行选择,也可以lunch + 版本名称,直接完成lunch。
例如:
lunch aosp_arm64-eng
lunch函数命令的具体流程:
读取lunch参数,获取用户编译目标到answer变量,如果没有输入参数,调用print_lunch_menu函数显示可选编译项菜单,提示用户继续输入。
在print_lunch_menu中通过COMMON_LUNCH_CHOICES来获取所有的版本名称。
在早期的Android版本(9之前)中是通过add_lunch_combo函数添加进来的。
而新版本的Android(10以后)已经将这个部分弃用,使用COMMON_LUNCH_CHOICES。
根据COMMON_LUNCH_CHOICES的版本名称,引用对应的mk文件。
然后再对输入的数字或者字符进行检查。
-
grep -q -e "^[0-9][0-9]*$",-q表示quiet模式,也就是不打印信息,-e表示后面跟的是一个用于匹配的模式。这条命令从answer变量中搜寻以两位数字开头的字符串,如果找到,就认为是输入的是数字。然后进一步对这个数字做有没有越界的检查。
2.不是数字的话,尝试匹配这样的字符串 ${variant_and_version%%-*}
这里匹配的是后面跟任意字符,然后必须有个-,最后又不以--结束的字符串。就是期望得到product-varient的形式。
也就是我们之前使用COMMON_LUNCH_CHOICES获取的那些字符串的格式。
比如:aosp_arm-eng,product就是aosp_arm,varient就是eng。
中间的-是必须的。如果发现符合要求的格式的话selection变量就会被$answer赋值,也就是说,selection其实就是一个product-varient模式的字符串。
3.最后将 product赋值给TARGET_PRODUCT ,variant赋值给TARGET_BUILD_VARIANT,做export设置环境变量。
3.make原理
make命令中会调用get_make_command,最后到soong_ui.bash,以及m,mm,mmm的命令也会调用soong_ui.bash。
从Android10以后,soong_ui.bash替代了make命令,作为编译开始的入口。
在soong_ui.bash中做了三个事,如下:
1.导入microfactory.bash,设置 GOROOT 环境变量,指向 prebuild 的 go 编译工具链,根据参数编译 go 源码生成相应的程序soong_ui。
2.调用 soong_build_go 函数。两个参数的含义,第一参数是编译生成的可执行程序的名字,第二参数是执行程序的源码路径。
3.通过 exec 命令创建进程执行上一步生成的 soong_ui程序,并使用添加的参数。这一步就等同于之前的make命令。
例如,m cameraserver,最终等价于调用 out/soong_ui --make-mode cameraserver,注意这里的 --make-mode 是在 m 函数里引入的。
总而言之,build/soong/soong_ui.bash 的最终效果就是帮助我们制作出了一个叫做 soong_ui 的应用程序,放在 out 下。
通过以上的三步,就可以完成Android特定的project的编译。
通过对Android编译源码的分析,有利于我们新建一个自定义的Android project,这对于一个vendor厂商有着重要意义,通过不同的project可以区分不同的产品类型,并且不需要更多的代码仓库。