source build/envsetup.sh 做了什么?

Android 编译系统解析系列文档

编译系统入口envsetup.sh解析

解析lunch的执行过程以及make执行过程中include文件的顺序

关注一些make执行过程中的几个关键点

对一些独特的语法结构进行解析


我们用几个问题来解释这篇文章要讨论的内容

为什么用\cd 而不用cd

在android原代码的提交中,我们发现了这个解释:

Use “\cd” to disable alias temporarily.

使用\cd 来临时屏蔽alias别名

具体作用如下示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
function mycd()
{
echo "in mycd"
cd $@
}
alias cd='mycd'

function cproj()
{
pwd
\cd .. 或者 cd ..
pwd
}

cproj()

我们执行这个脚本,前者将会打印:

1
2
/home/foree/bin
/home/foree

后者将会打印:

1
2
3
/home/foree/bin
in mycd
/home/foree

android 如何定位TOP目录

循环递归,查找build/core/envsetup.mk这个路径下文件是否存在

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function gettop
{
local TOPFILE=build/core/envsetup.mk
if [ -n "$TOP" -a -f "$TOP/$TOPFILE" ] ; then
# The following circumlocution ensures we remove symlinks from TOP.
(cd $TOP; PWD= /bin/pwd)
else
if [ -f $TOPFILE ] ; then
# The following circumlocution (repeated below as well) ensures
# that we record the true directory name and not one that is
# faked up with symlink names.
PWD= /bin/pwd
else
local HERE=$PWD
T=
while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do
\cd ..
T=`PWD= /bin/pwd -P`
done
\cd $HERE
if [ -f "$T/$TOPFILE" ]; then
echo $T
fi
fi
fi
}

关于shell脚本中命令太长的多行写法

1
2
3
adb shell ps \
| tr -d '\r' \
| sed -e 1d -e 's/^[^ ]* *\([0-9]*\).* \([^ ]*\)$/\1 \2/'

如何进入到指定关键字的目录,并选择符合条件中的一个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
function godir () {
if [[ -z "$1" ]]; then
echo "Usage: godir <regex>"
return
fi
T=$(gettop)
if [[ ! -f $T/filelist ]]; then
echo -n "Creating index..."
(\cd $T; find . -wholename ./out -prune -o -wholename ./.repo -prune -o -type f > filelist)
echo " Done"
echo ""
fi

local lines

lines=($(\grep "$1" $T/filelist | sed -e 's/\/[^/]*$//' | sort | uniq))
if [[ ${#lines[@]} = 0 ]]; then
echo "Not found"
return
fi

local pathname
local choice

if [[ ${#lines[@]} > 1 ]]; then
while [[ -z "$pathname" ]]; do
local index=1
local line
for line in ${lines[@]}; do
printf "%6s %s\n" "[$index]" $line
index=$(($index + 1))
done

echo
echo -n "Select one: "

unset choice
read choice

if [[ $choice -gt ${#lines[@]} || $choice -lt 1 ]]; then
echo "Invalid choice"
continue
fi

pathname=${lines[$(($choice-1))]}
done
else
pathname=${lines[0]}
fi

\cd $T/$pathname

}

envsetup.sh的作用

export 一些函数,像如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function lunch()

function _lunch()

function gettop

function getdriver()

function m()

function findmakefile()

function mm()

function mmm()

function mma()

function mmma()

function croot()

function cproj()

function pid()

以上这些就是咱们平时使用的croot,lunch,mm这些函数的来源,

然后在脚本的最后,需要判断一下当前环境的shell是否为bash,对于非bash来说,例如zsh,因为与bash的语法有微小差别,因此使用一些命令会出错,例如,lunch之后,如果直接选择数字,而不是类型,可能会导致lunch一个错误的机型

1
2
3
4
5
6
7
8
9
if [ "x$SHELL" != "x/bin/bash" ]; then
case `ps -o command -p $$` in
bash)
;;
*)
echo "WARNING: Only bash is supported, use of other shell would lead to erroneous results"
;;
esac
fi

然后最后,再查找device和vendor目录下vendorsetup.sh文件,目录层级最深为4层,我们可以以vendor_device-TAG的方式加入变量,通过add_lunch_combo加到Lunch的选择项中。

1
2
3
4
5
6
7
# Execute the contents of any vendorsetup.sh files we can find.
for f in `test -d device && find -L device -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null` \
`test -d vendor && find -L vendor -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null`
do
echo "including $f"
. $f
done

我们来看看通过lunch启动都干了些什么(可以通过cat build/envsetup.sh|sed -n ‘/function lunch/,/^}/p’ 打印函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
function lunch()
{
local answer

if [ "$1" ] ; then
answer=$1
else
print_lunch_menu
echo -n "Which would you like? [aosp_arm-eng] "
read answer
fi

local selection=
if [ -z "$answer" ]
then
selection=aosp_arm-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 "Invalid lunch combo: $answer"
return 1
fi

export TARGET_BUILD_APPS=
local product=$(echo -n $selection | sed -e "s/-.*$//")
check_product $product

if [ $? -ne 0 ]
then
echo
echo "** Don't have a product spec for: '$product'"
echo "** Do you have the right repo manifest?"
product=
fi

local variant=$(echo -n $selection | sed -e "s/^[^\-]*-//")
check_variant $variant

if [ $? -ne 0 ]
then
echo
echo "** Invalid variant: '$variant'"
echo "** Must be one of ${VARIANT_CHOICES[@]}"
variant=
fi

if [ -z "$product" -o -z "$variant" ]
then
echo
return 1
fi

export TARGET_PRODUCT=$product
export TARGET_BUILD_VARIANT =$variant
export TARGET_BUILD_TYPE=release

echo
set_stuff_for_environment
printconfig
}

如上:主要关注点在于4个正则表达式

  1. elif (echo -n $answer | grep -q -e “^[0-9][0-9]*$”)
  2. elif (echo -n $answer | grep -q -e “^[^-][^-]-[^-][^-]$”)
  3. local product=$(echo -n $selection | sed -e “s/-.*$//“)
  4. local variant=$(echo -n $selection | sed -e “s/^[^-]*-//“)

第一个是查找1-2位数的正则表达式
第二个是匹配vendor_product-eng这样格式的选择项
第三个用来提取-前边的关键字,例如meizu_m76-eng中的meizu_m76
第四个用来提取variant变量,就是eng,userdebug,user这几个变量

我们从这里可以看出,vendorsetup.sh中写出符合标准格式的combo,然后在lunch的时候导出

接下来我们进入lunch的世界

Android编译系统之lunch分析

作者

0xforee

发布于

2015-11-05

更新于

2015-12-02

许可协议


欢迎关注我的公众号 0xforee,第一时间获取更多有价值的思考

评论