http://www.cloudchou.com/android/post-261.html
编译Rom的第一步是source build/envsetup.sh,该步骤将envsetup.sh里的函数声明为当前终端可用的命令,并将所有产品添加至变量LUNCH_MENU_CHOICES里。
编译Rom的第二步是让用户选择他想编译的产品,用户可以使用在source build/envsetup.sh后设置的breakfast或者lunch命令进行选择,接下来我们将详细分析这些命令的执行流程以及执行完breakfast命令或者lunch命令后在会话终端设置的变量
1. 命令执行流程
1.1 breakfast执行流程
流程:
-
1) 从github上下载cm支持的产品,并添加至产品列表
-
2) 如果命令参数为空,那么调用lunch函数,让用户选择产品
-
3) 如果命令参数为1个且$target格式为$product-$build_variant,那么调用lunch $target,这样不需要用户选择产品
-
4) 如果命令参数为1个且$target格式为$product,那么将其扩展为带build_variant格式的产品,然后调用lunch cm_$target-userdebug,这样不需要用户选择产品
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
|
target=$1
CM_DEVICES_ONLY="true" #只编译CM支持的设备
unset LUNCH_MENU_CHOICES
add_lunch_combo full-eng
#vendor/cm/vendorsetup.sh 该脚本会从github上下载cm支持的产品,
#并添加至LUNCH_MENU_CHOICES变量 ,该变量表示产品列表
for f in `/bin/ls vendor/cm/vendorsetup.sh 2> /dev/null`
do
echo "including $f"
. $f
done
unset f
#如果没有带任何参数,那么调用lunch函数,让用户选择产品
if [ $# -eq 0 ]; then
lunch
else
#target格式:$product-$build_variant 或者 $product
# 示例 cm_i9100-userdebug 或 i9100
echo "z$target" | grep -q "-"
#如果用户输入的产品格式是$product-$build_variant 那么直接调用lunch
if [ $? -eq 0 ]; then
# A buildtype was specified, assume a full device name
lunch $target
else
#如果用户输入的产品格式是$product,
#那么扩展该变量为cm_$target-userdebug格式
# This is probably just the CM model name
lunch cm_$target-userdebug
fi
fi
return $?
|
1.2 lunch执行流程
流程:
-
1) 获取用户指定的产品或者让用户选择产品,并提取$product和$variant
-
2) 检查是否支持产品
-
3) 若不支持该产品,从网上下载该产品的相关配置到本地device目录,并再次检查是否支持该产品
-
4) 若支持该产品,下载产品的最新配置到本地device目录
-
5) 若还是不支持,告诉用户不支持并退出
-
6) 设置环境变量export TARGET_PRODUCT,TARGET_BUILD_VARIANT,TARGET_BUILD_TYPE
-
7) 建立$(OUT_DIR)/target/common目录
-
8) 设置PROMPT_COMMAND变量,java_home,PATH目录,set_sequence_number
-
9) 打印选择产品后对应的一些编译配置变量
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
72
73
74
75
76
77
78
79
80
81
82
83
84
|
local answer
if [ "$1" ] ; then
answer=$1
else
#若调用者没有指定产品,那么打印产品列表,让用户选择
print_lunch_menu
echo -n "Which would you like? [full-eng] "
read answer
fi
local selection=
if [ -z "$answer" ] #默认产品为full-eng
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
#选择的产品为$product-$build_variant格式
elif (echo -n $answer | grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$")
then
selection=$answer
fi
if [ -z "$selection" ]#selection格式为$product-$build_variant
then
echo
echo "Invalid lunch combo: $answer"
return 1
fi
export TARGET_BUILD_APPS=
#提取product变量 product示例cm_i9100
local product=$(echo -n $selection | sed -e "s/-.*$//")
check_product $product #检查产品是否支持
if [ $? -ne 0 ]#若产品不支持
then
#if we can't find a product, try to grab it off the CM github
T=$(gettop)
pushd $T > /dev/null
#下载prouct的配置 放在device/$vendor/$product目录
build/tools/roomservice.py $product
popd > /dev/null
check_product $product #再次检查产品是否支持
else
#获取最新配置 更新device/$vendor/$product
build/tools/roomservice.py $product true
fi
if [ $? -ne 0 ]
then
echo
echo "** Don't have a product spec for: '$product'"
echo "** Do you have the right repo manifest?"
product=
fi
#从$product-$build_variant里提取$variant
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
fixup_common_out_dir #建立$(OUT_DIR)/target/common目录
#设置PROMPT_COMMAND变量,java_home,PATH目录,set_sequence_number
set_stuff_for_environment
# 打印选择产品后的重要环境变量
printconfig
|
1.3 check_product执行流程
流程:
-
1) export CM_BUILD CM_BUILD示例:若$1是cm_i9100,则CM_BUILD是i9100
-
2) 调用get_build_var TARGET_DEVICE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
T=$(gettop)
if [ ! "$T" ]; then
echo "Couldn't locate the top of the tree. Try setting TOP." >&2
return
fi
if (echo -n $1 | grep -q -e "^cm_") ; then
CM_BUILD=$(echo -n $1 | sed -e 's/^cm_//g')
else
CM_BUILD=
fi
export CM_BUILD
CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \
TARGET_PRODUCT=$1 \
TARGET_BUILD_VARIANT= \
TARGET_BUILD_TYPE= \
TARGET_BUILD_APPS= \
get_build_var TARGET_DEVICE > /dev/null
|
1.4 get_build_var执行流程
调用流程:lunch->check_product->get_build_var TARGET_DEVICE
此时的环境变量有
1)TARGET_PRODUCT:cm_i9100
2)CALLED_FROM_SETUP:true
3)BUILD_SYSTEM:build/core
4)export CM_BUILD=i9100
最终调用build/core/config.mk来完成检测是否支持产品$TARGET_PRODUCT
1
2
3
4
5
6
7
8
|
T=$(gettop)
if [ ! "$T" ]; then
echo "Couldn't locate the top of the tree. Try setting TOP." >&2
return
fi
CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \
#$1的值可能为TARGET_DEVICE
make --no-print-directory -C "$T" -f build/core/config.mk dumpvar-$1
|
选择好产品后,可用get_build_var查看产品对应的编译变量,它依赖于以下环境变量
export TARGET_PRODUCT=cm_i9100
export TARGET_BUILD_VARIANT=userdebug
export TARGET_BUILD_TYPE=release
export CM_BUILD=i9100
因此makefile里定义的变量并未添加至环境变量,每次调用get_build_var时,其实是调用config.mk依赖的dumpvar.mk实时计算出编译变量的值
比如说LEX变量 HOST_ARCH变量
1.5 printconfig执行流程
1
2
3
4
5
6
|
T=$(gettop)
if [ ! "$T" ]; then
echo "Couldn't locate the top of the tree. Try setting TOP." >&2
return
fi
get_build_var report_config
|
最终调用build/core/dumpvar.mk来完成变量的打印
示例:
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=4.2.2
CM_VERSION=10.1-20130822-UNOFFICIAL-i9100
TARGET_PRODUCT=cm_i9100
TARGET_BUILD_VARIANT=userdebug
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=arm
TARGET_ARCH_VARIANT=armv7-a-neon
HOST_ARCH=x86
HOST_OS=linux
HOST_OS_EXTRA=Linux-2.6.32-33-generic-x86_64-with-Ubuntu-10.04-lucid
HOST_BUILD_TYPE=release
BUILD_ID=JDQ39E
OUT_DIR=/home/android/tmp/android_out/CyanogenMod
1.5 mm执行流程
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
|
local MM_MAKE=make
local ARG=
for ARG in $@ ; do #如果参数中有mka,那么利用mka进行编译
if [ "$ARG" = mka ]; then
MM_MAKE=mka
fi
done
#如果处在根目录 利用Android根目录的makefile编译选中目标
if [ -f build/core/envsetup.mk -a -f Makefile ]; then
$MM_MAKE $@
else
T=$(gettop)
#找到最近的makfile,即当前目录所在工程的makefile
local M=$(findmakefile)
# Remove the path to top as the makefilepath needs to be relative
local M=`echo $M|sed 's:'$T'/::'`
if [ ! "$T" ]; then
echo "Couldn't locate the top of the tree. Try setting TOP."
elif [ ! "$M" ]; then
echo "Couldn't locate a makefile from the current directory."
else
#使用ONE_SHOT_MAKEFILE关键字确定工程所用的makefile,
#并利用Android根目录的makefile进行编译
ONE_SHOT_MAKEFILE=$M $MM_MAKE -C $T all_modules $@
fi
fi
|
1.5 mmm执行流程
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
|
local MMM_MAKE=make
T=$(gettop)
if [ "$T" ]; then
local MAKEFILE=
local MODULES=
local ARGS=
local DIR TO_CHOP
#提取编译选项(用-指定编译参数)
local DASH_ARGS=$(echo "$@" | awk -v RS=" " -v ORS=" " '/^-.*$/')
#提取编译目录
local DIRS=$(echo "$@" | awk -v RS=" " -v ORS=" " '/^[^-].*$/')
for DIR in $DIRS ; do
MODULES=`echo $DIR | sed -n -e 's/.*:\(.*$\)/\1/p' | sed 's/,/ /'`
#提取模块 dir格式:dirname:modulename
if [ "$MODULES" = "" ]; then
MODULES=all_modules
fi
DIR=`echo $DIR | sed -e 's/:.*//' -e 's:/$::'`
#如果指定目录有Android.mk,计算出MAKEFILE变量的值
if [ -f $DIR/Android.mk ]; then
TO_CHOP=`(cd -P -- $T && pwd -P) | wc -c | tr -d ' '`
TO_CHOP=`expr $TO_CHOP + 1`
START=`PWD= /bin/pwd`
MFILE=`echo $START | cut -c${TO_CHOP}-`
if [ "$MFILE" = "" ] ; then
MFILE=$DIR/Android.mk
else
MFILE=$MFILE/$DIR/Android.mk
fi
MAKEFILE="$MAKEFILE $MFILE"
else #特殊目标 其实是做编译参数
if [ "$DIR" = snod ]; then
ARGS="$ARGS snod"
elif [ "$DIR" = showcommands ]; then
ARGS="$ARGS showcommands"
elif [ "$DIR" = dist ]; then
ARGS="$ARGS dist"
elif [ "$DIR" = incrementaljavac ]; then
ARGS="$ARGS incrementaljavac"
elif [ "$DIR" = mka ]; then
MMM_MAKE=mka
else
echo "No Android.mk in $DIR."
return 1
fi
fi
done
#使用ONE_SHOT_MAKEFILE关键字确定工程所用的makefile,
#并利用Android根目录的makefile进行编译
ONE_SHOT_MAKEFILE="$MAKEFILE" $MMM_MAKE -C $T $DASH_ARGS $MODULES $ARGS
else
echo "Couldn't locate the top of the tree. Try setting TOP."
fi
|
2. breakfast或者lunch命令执行后在会话终端定义的变量
在执行完breakfast或者lunch命令后,会在当前终端设置许多变量,这些变量有些只能在当前shell里使用,有些能继续在子shell里使用(用sh执行某个shell脚本即在子shell里)。根据变量定义位置,将变量分为3类:
-
1) 函数:是在函数定义的变量,但并未用export显示指出,在子shell里不可用,当前shell可用
-
2) export:导出的环境变量,子Shell也可用
-
3) 文件:文件里定义的变量
变量 |
类型 |
说明 |
---|
T |
函数 |
根目录 |
TARGET_BUILD_TYPE |
export |
release或者debug |
TARGET_PRODUCT |
export |
示例:cm_find5 |
TARGET_BUILD_VARIANT |
export |
可能的值为user,userdebug,eng |
TARGET_BUILD_APPS |
export |
需要编译的App集合 |
CM_BUILD |
export |
示例find5 |
VARIANT_CHOICES |
文件 |
(user userdebug eng) |
LUNCH_MENU_CHOICES |
函数 |
产品列表 |
prebuiltdir |
函数 |
$(getprebuilt) |
gccprebuiltdir |
函数 |
$(get_abs_build_var ANDROID_GCC_PREBUILTS) |
ANDROID_EABI_TOOLCHAIN |
export |
工具链所在目录:以下选项之一
$ gccprebuiltdir /x86/i686-linux-android-4.6/bin
$ gccprebuiltdir /arm/arm-linux-androideabi-4.6/bin
$ gccprebuiltdir /mips/mipsel-linux-android-4.6/bin |
ANDROID_TOOLCHAIN |
export |
$ANDROID_EABI_TOOLCHAIN |
ANDROID_QTOOLS |
export |
$T/development/emulator/qtools |
ANDROID_DEV_SCRIPTS |
export |
$T/development/scripts |
ANDROID_BUILD_PATHS |
export |
$(get_build_var ANDROID_BUILD_PATHS):
$ANDROID_QTOOLS:
$ANDROID_TOOLCHAIN:
$ARM_EABI_TOOLCHAIN_PATH:
$CODE_REVIEWS:$ANDROID_DEV_SCRIPTS: |
ARM_EABI_TOOLCHAIN |
export |
$gccprebuiltdir/ arm/arm-eabi-4.6/bin |
ARM_EABI_TOOLCHAIN_PATH |
函数 |
$gccprebuiltdir/ arm/arm-eabi-4.6/bin |
toolchaindir |
函数 |
以下三个选项之一
x86/i686-linux-android-4.6/bin
arm/arm-linux-androideabi-4.6/bin
mips/mipsel-linux-android-4.6/bin |
ANDROID_JAVA_TOOLCHAIN |
export |
$JAVA_HOME/bin |
ANDROID_PRE_BUILD_PATHS |
export |
$ANDROID_JAVA_TOOLCHAIN |
ANDROID_PRODUCT_OUT |
export |
$(get_abs_build_var PRODUCT_OUT) |
OUT |
export |
$ANDROID_PRODUCT_OUT |
ANDROID_HOST_OUT |
export |
$(get_abs_build_var HOST_OUT) |
OPROFILE_EVENTS_DIR |
export |
$T/external/oprofile/events |
BUILD_ENV_SEQUENCE_NUMBER |
export |
export BUILD_ENV_SEQUENCE_NUMBER=10 |
PROMPT_COMMAND |
export |
命令提示符 |
CM_DEVICES_ONLY |
函数 |
true 表示只支持CM的设备,如果使用breakfast命令会将改变量设置为true |
MODVERSION |
函数 |
$(get_build_var CM_VERSION) |
ZIPFILE |
函数 |
cm-$MODVERSION.zip |
ZIPPATH |
函数 |
$OUT/$ZIPFILE |
TOPFILE |
函数 |
build/core/envsetup.mk |
用户下一步将使用mka进行编译,会利用这前两步设置的变量和函数命令,虽然breakfast命令和lunch命令有利用到一些makefile检查选择的产品是否符合要求,但是makefile里的变量都引入到当前shell,仅仅用于检查产品是否符合要求而已。
我们现在了解到breakfast命令会从网上下载产品列表,而lunch命令会下载产品的最新配置,所以我们在使用breakfast命令或者lunch命令时,会觉得时间比较长,如果是本地产品,你可以注释那些检查的代码,这样会很快。