Android编译系统详解(二)——命令执行流程

2014/01/20

本文原创作者:Cloud Chou. 欢迎转载,请注明出处和本文链接

编译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) \t获取用户指定的产品或者让用户选择产品,并提取$product和$variant
  • 2) \t检查是否支持产品
  • 3) \t若不支持该产品,从网上下载该产品的相关配置到本地device目录,并再次检查是否支持该产品
  • 4) \t若支持该产品,下载产品的最新配置到本地device目录
  • 5) \t若还是不支持,告诉用户不支持并退出
  • 6) \t设置环境变量export TARGET_PRODUCT,TARGET_BUILD_VARIANT,TARGET_BUILD_TYPE
  • 7) \t建立$(OUT_DIR)/target/common目录
  • 8) \t设置PROMPT_COMMAND变量,java_home,PATH目录,set_sequence_number
  • 9) \t打印选择产品后对应的一些编译配置变量
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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

```<h3>1.3 check_product执行流程</h3>
<p>流程:</p>
<ul>
<li>1) \texport CM_BUILD CM_BUILD示例:若$1是cm_i9100,则CM_BUILD是i9100</li>
<li>2) \t调用get_build_var TARGET_DEVICE</li>
</ul>
```bash
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
```<h3>1.4 get_build_var执行流程</h3>
<p>调用流程:lunch->check_product->get_build_var TARGET_DEVICE</p>
<p>此时的环境变量有</p>
<p>  1)TARGET_PRODUCT:cm_i9100 </p>
<p>  2)CALLED_FROM_SETUP:true </p>
<p>  3)BUILD_SYSTEM:build/core</p>
<p>  4)export CM_BUILD=i9100</p>
<p>最终调用build/core/config.mk来完成检测是否支持产品$TARGET_PRODUCT</p>
```bash
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命令时,会觉得时间比较长,如果是本地产品,你可以注释那些检查的代码,这样会很快。

¥打赏5毛

取消

感谢您的支持,我会继续努力的!

扫码支持
赏个5毛,支持我把

打开支付宝扫一扫,即可进行扫码打赏哦

本篇目录