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 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 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 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 |
调用流程: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 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 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 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 |
在执行完breakfast或者lunch命令后,会在当前终端设置许多变量,这些变量有些只能在当前shell里使用,有些能继续在子shell里使用(用sh执行某个shell脚本即在子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命令时,会觉得时间比较长,如果是本地产品,你可以注释那些检查的代码,这样会很快。