Invoke".build/envsetup.sh"fromyourshelltoaddthefollowingfunctionstoyourenvironment:
-lunch:
lunch-
-tapas:
tapas[...][arm|x86|mips][eng|userdebug|user]
-croot:
Changesdirectorytothetopofthetree.
-m:
Makesfromthetopofthetree.
-mm:
Buildsallofthemodulesinthecurrentdirectory.
-mmm:
Buildsallofthemodulesinthesupplieddirectories.
-cgrep:
GrepsonalllocalC/C++files.
-jgrep:
GrepsonalllocalJavafiles.
-resgrep:
Grepsonalllocalres/*.xmlfiles.
-godir:
Gotothedirectorycontainingafile.
Lookatthesourcetoviewmorefunctions.Thecompletelistis:
EOF
T=$(gettop)
localA
A=""
foriin`cat$T/build/envsetup.sh|sed-n"/^function/s/function[a-z_]*.*/\1/p"|sort`;do
A="$A$i"
done
echo$A
}
我们在当前终端中执行hmm命令即可以看到函数hmm的完整输出。
函数hmm主要完成三个工作:
1.调用另外一个函数gettop获得Android源码的根目录T。
2.通过cat命令显示一个HereDocument,说明$T/build/envsetup.sh文件加载到当前终端后所提供的主要命令。
3.通过sed命令解析$T/build/envsetup.sh文件,并且获得在里面定义的所有函数的名称,这些函数名称就是$T/build/envsetup.sh文件加载到当前终端后提供的所有命令。
注意,sed命令是一个强大的文本分析工具,它以行为单位为执行文本替换、删除、新增和选取等操作。
函数hmm通过执行以下的sed命令来获得在$T/build/envsetup.sh文件定义的函数的名称:
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
sed-n"/^function/s/function[a-z_]*.*/\1/p"
它表示对所有以“function”开头的行,如果紧接在“function”后面的字符串仅由字母a-z和下横线(_)组成,那么就将这个字符串提取出来。
这正好就对应于shell脚本里面函数的定义。
文件build/envsetup.sh除了定义一堆函数之外,还有一个重要的代码段,如下所示:
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
#Executethecontentsofanyvendorsetup.shfileswecanfind.
forfin`/bin/lsvendor/*/vendorsetup.shvendor/*/*/vendorsetup.shdevice/*/*/vendorsetup.sh2>/dev/null`
do
echo"including$f"
.$f
done
unsetf
这个for循环遍历vendor目录下的一级子目录和二级子目录以及device目录下的二级子目录中的vendorsetup.sh文件,并且通过source命令(.)将它们加载当前终端来。
vendor和device相应子目录下的vendorsetup.sh文件的实现很简单,它们主要就是添加相应的设备型号及其编译类型支持到Lunch菜单中去。
例如,device/samsung/maguro目录下的vendorsetup.sh文件的实现如下所示:
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
add_lunch_combofull_maguro-userdebug
它调用函数add_lunch_combo添加一个名称为“full_maguro-userdebug”的菜单项到Lunch菜单去。
函数add_lunch_combo定义在build/envsetup.sh文件中,它的实现如下所示:
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
functionadd_lunch_combo()
{
localnew_combo=$1
localc
forcin${LUNCH_MENU_CHOICES[@]};do
if["$new_combo"="$c"];then
return
fi
done
LUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]}$new_combo)
}
传递给函数add_lunch_combo的参数保存在位置参数$1中,接着又保存在一个本地变量new_combo中,用来表示一个要即将要添加的Lunch菜单项。
函数首先是在数组LUNCH_MENU_CHOICES中检查要添加的菜单项是否已经存在。
只有在不存在的情况下,才会将它添加到数组LUNCH_MENU_CHOICES中去。
注意,${LUNCH_MENU_CHOICES[@]}表示数组LUNCH_MENU_CHOICES的所有元素。
数组LUNCH_MENU_CHOICES是定义在文件build/envsetup.sh的一个全局变量,当文件build/envsetup.sh被加载的时候,这个数组会被初始化为化full-eng、full_x86-eng、vbox_x86-eng和full_mips-eng,如下所示:
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
#addthedefaultonehere
add_lunch_combofull-eng
add_lunch_combofull_x86-eng
add_lunch_combovbox_x86-eng
add_lunch_combofull_mips-eng
这样当文件build/envsetup.sh加载完成之后,数组LUNCH_MENU_CHOICES就包含了当前源码支持的所有设备型号及其编译类型,于是当接下来我们执行lunch命令的时候,就可以通过数组LUNCH_MENU_CHOICES看到一个完整的Lunch藤蔓。
二.lunch命令的执行过程
lunch命令实际上是定义在文件build/envsetup.sh的一个函数,它的实现如下所示:
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
functionlunch()
{
localanswer
if["$1"];then
answer=$1
else
print_lunch_menu
echo-n"Whichwouldyoulike?
[full-eng]"
readanswer
fi
localselection=
if[-z"$answer"]
then
selection=full-eng
elif(echo-n$answer|grep-q-e"^[0-9][0-9]*$")
then
if[$answer-le${#LUNCH_MENU_CHOICES[@]}]
then
selection=${LUNCH_MENU_CHOICES[$(($answer-1))]}
fi
elif(echo-n$answer|grep-q-e"^[^\-][^\-]*-[^\-][^\-]*$")
then
selection=$answer
fi
if[-z"$selection"]
then
echo
echo"Invalidlunchcombo:
$answer"
return1
fi
exportTARGET_BUILD_APPS=
localproduct=$(echo-n$selection|sed-e"s/-.*$//")
check_product$product
if[$?
-ne0]
then
echo
echo"**Don'thaveaproductspecfor:
'$product'"
echo"**Doyouhavetherightrepomanifest?
"
product=
fi
localvariant=$(echo-n$selection|sed-e"s/^[^\-]*-//")
check_variant$variant
if[$?
-ne0]
then
echo
echo"**Invalidvariant:
'$variant'"
echo"**Mustbeoneof${VARIANT_CHOICES[@]}"
variant=
fi
if[-z"$product"-o-z"$variant"]
then
echo
return1
fi
exportTARGET_PRODUCT=$product
exportTARGET_BUILD_VARIANT=$variant
exportTARGET_BUILD_TYPE=release
echo
set_stuff_for_environment
printconfig
}
函数lunch的执行逻辑如下所示:
1.检查是否带有参数,即位置参数$1是否等于空。
如果不等于空的话,就表明带有参数,并且该参数是用来指定要编译的设备型号及其编译类型的。
如果等于空的话,那么就调用另外一个函数print_lunch_menu来显示Lunch菜单项,并且通过调用read函数来等待用户输入。
无论通过何种方式,最终变量answer的值就保存了用户所指定的备型号及其编译类型。
2.对变量answer的值的合法性进行检查。
如果等于空的话,就将它设置为默认值“full-eng”。
如果不等于空的话,就分为三种情况考虑。
第一种情况是值为数字,那么就需要确保该数字的大小不能超过Lunch菜单项的个数。
在这种情况下,会将输入的数字索引到数组LUNCH_MENU_CHOICES中去,以便获得一个用来表示设备型号及其编译类型的文本。
第二种情况是非数字文本,那么就需要确保该文本符合-的形式,其中表示设备型号,而表示编译类型。
第三种情况是除了前面两种情况之外的所有情况,这是非法的。
经过合法性检查后,变量selection代表了用户所指定的备型号及其编译类型,如果它的值是非法的,即它的值等于空,那么函数lunch就不往下执行了。
3.接下来是解析变量selection的值,也就是通过sed命令将它的和值提取出来,并且分别保存在变量product和variant中。
提取出来的product和variant值有可能是不合法的,因此需要进一步通过调用函数check_product和check_variant来检查。
一旦检查失败,也就是函数check_product和check_variant的返回值$?
等于非0,那么函数lunch就不往下执行了。
4.通过以上合法性检查之后,就将变量product和variant的值保存在环境变量TARGET_PRODUCT和TARGET_BUILD_VARIANT中。
此外,另外一个环境变量TARGET_BUILD_TYPE的值会被设置为"release",表示此次编译是一个release版本的编译。
另外,前面还有一个环境变量TARGET_BUILD_APPS,它的值被函数lunch设置为空,用来表示此次编译是对整个系统进行编译。
如果环境变量TARGET_BUILD_APPS的值不等于空,那么就表示此次编译是只对某些APP模块进行编译,而这些APP模块就是由环境变量TARGET_BUILD_APPS来指定的。
5.调用函数set_stuff_for_environment来配置环境,例如设置JavaSDK路径和交叉编译工具路径等。
6.调用函数printfconfig来显示已经配置好的编译环境参数。
在上述执行过程中,函数check_product、check_variant和printconfig是比较关键的,因此接下来我们就继续分析它们的实现。
函数check_product定义在文件build/envsetup.sh中,它的实现如下所示:
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
#checktoseeifthesuppliedproductisonewecanbuild
functioncheck_product()
{
T=$(gettop)
if[!
"$T"];then
echo"Couldn'tlocatethetopofthetree.TrysettingTOP.">&2
return
fi
CALLED_FROM_SETUP=trueBUILD_SYSTEM=build/core\
TARGET_PRODUCT=$1\
TARGET_BUILD_VARIANT=\
TARGET_BUILD_TYPE=\
TARGET_BUILD_APPS=\
get_build_varTARGET_DEVICE>/dev/null
#hidesuccessfulanswers,butallowtheerrorstoshow
}
函数gettop用来返回Android源代码工程的根目录。
函数check_product需要在Android源代码工程根目录或者子目录下调用。
否则的话,函数check_product就出错返回。
接下来函数check_product设置几个环境变量,其中最重要的是前面三个CALLED_FROM_SETUP、BUILD_SYSTEM和TARGET_PRODUCT。
环境变量CALLED_FROM_SETUP的值等于true表示接下来执行的make命令是用来初始化Android编译环境的。
环境变量BUILD_SYSTEM用来指定Android编译系统的核心目录,它的值被设置为build/core。
环境变量TARGET_PRODU