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

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命令时,会觉得时间比较长,如果是本地产品,你可以注释那些检查的代码,这样会很快。


你可能感兴趣的:(android,编译)