编译Android的第一步是执行source build/envsetup.sh设置编译相关的环境,里面到底都做了什么呢?我们来看一看。

envsetup.sh的代码较长,共有1632行,但其内容较简单,只做了两件事:
1. 函数定义

定义可以在命令行直接调用的函数,方便编译和调试操作。一共定义了四种类型共75个可以在命令行调用的函数;
2. 生成编译配置列表

脚本通过查找并执行{device, vendor, product}目录下的vendorsetup.sh文件,搜集所有可能的编译配置生成配置列表,供编译时选择;

接下来基于这两个主题对文件envsetup.sh进行详细分析。

1. 函数定义

文件95%以上的代码都是在定义各种各样的函数,并将这些函数导入到shell环境中,使其可以直接在命令行调用。
这些函数总共分为4类,包括:
- 辅助函数类
- 编译环境设置类
- 代码搜索类
- adb调试类

通过以下命令获得envsetup.sh中所定义的函数总数:

ygu@guyongqiangx:src$ sed -n "/^[[:blank:]]*function /s/function \([a-z_]\w*\).*/\1/p" < build/envsetup.sh | wc -l75

以下对所有定义的函数逐个进行分析。

1.1 辅助函数类

hmm

hmm函数输出envsetup.sh的帮助说明,提示执行. build/envsetup.sh后有哪些可以调用的操作。以前看到别人提过android编译的特殊命令,包括mmmmmm,这3个命令的差别我每次都要去问度娘,好了,现在直接运行hmm,什么都知道了。

function hmm() {cat <<EOFInvoke ". build/envsetup.sh" from your shell to add the following functions to your environment:- lunch:     lunch -- tapas:     tapas [  ...] [arm|x86|mips|armv5|arm64|x86_64|mips64] [eng|userdebug|user]- croot:     Changes directory to the top of the tree.- m:         Makes from the top of the tree.- mm:        Builds all of the modules in the current directory, but not their dependencies.- mmm:       Builds all of the modules in the supplied directories, but not their dependencies.             To limit the modules being built use the syntax: mmm dir/:target1,target2.- mma:       Builds all of the modules in the current directory, and their dependencies.- mmma:      Builds all of the modules in the supplied directories, and their dependencies.- provision: Flash device with all required partitions. Options will be passed on to fastboot.- cgrep:     Greps on all local C/C++ files.- ggrep:     Greps on all local Gradle files.- jgrep:     Greps on all local Java files.- resgrep:   Greps on all local res/*.xml files.- mangrep:   Greps on all local AndroidManifest.xml files.- mgrep:     Greps on all local Makefiles files.- sepgrep:   Greps on all local sepolicy files.- sgrep:     Greps on all local source files.- godir:     Go to the directory containing a file.Environment options:- SANITIZE_HOST: Set to 'true' to use ASAN for all host modules. Note that                 ASAN_OPTIONS=detect_leaks=0 will be set by default until the                 build is leak-check clean.Look at the source to view more functions. The complete list is:EOF    # 查找编译环境根目录    T=$(gettop)    local A    A=""    # 读取build/envsetup.sh文件,并通过sed操作获取其中定义的函数,并进行排序输出,存放到变量$A中    for i in `cat $T/build/envsetup.sh | sed -n "/^[[:blank:]]*function /s/function \([a-z_]*\).*/\1/p" | sort | uniq`; do      A="$A $i"    done    echo $A}

直接在命令行调用hmm,先显示一堆help信息,随后列举本文件中所有定义的函数。

>

函数中命令”sed -n "/^[[:blank:]]*function /s/function \([a-z_]*\).*/\1/p"“用于生成函数列表。

但其操作有一个bug,用于匹配函数的正则表达式”function \([a-z_]*\).*“会漏掉函数”is64bit“。

将匹配模式从”function \([a-z_]*\).*“修改为”function \([a-z_]\w*\).*“可以匹配文件中的所有函数。

gettop

gettop函数从指定的$TOP目录或当前目录开始查找build/core/envsetup.mk文件,并将能找到该文件的目录返回给调用函数作为操作的根目录,详细注释如下:

function gettop{    local TOPFILE=build/core/envsetup.mk    # 如果编译环境已经设置了$TOP,就检查$TOP/build/core/envsetup.mk文件是否存在    if [ -n "$TOP" -a -f "$TOP/$TOPFILE" ] ; then        # The following circumlocution ensures we remove symlinks from TOP.        # 转到$TOP目录,通过命令`/bin/pwd`将$TOP目录指向的真实路径存放到PWD中        (cd $TOP; PWD= /bin/pwd)    else        # 如果当前路径下能够找到build/core/envsetup.mk文件,        # 则将当前目录的真实路径存放到PWD中        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            # 如果当前目录下无法找到build/core/envsetup.mk文件,            # 则不断返回到外层目录查找,直到到达根目录/为止            # 保存查找操作前的路径            local HERE=$PWD            T=            while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do                # 转到外层目录                \cd ..                # 将当前路径保存到T中                T=`PWD= /bin/pwd -P`            done            # 查找完后恢复操作前的路径            \cd $HERE            # 如果目录T包含build/core/envsetup.mk,说明是T是编译的根目录            if [ -f "$T/$TOPFILE" ]; then                # 输出$T中保存的路径作为gettop的返回值                echo $T            fi        fi    fi}

croot

croot命令切换到当前编译环境的根目录。

function croot(){    # 查找当前编译树的根目录    T=$(gettop)    if [ "$T" ]; then        # 切换到编译环境的根目录        \cd $(gettop)    else        echo "Couldn't locate the top of the tree.  Try setting TOP."    fi}

cproj

cproj命令用于切换到当前模块的编译目录下(含有Android.mk)

function cproj(){    TOPFILE=build/core/envsetup.mk    # 保存操作前的路径    local HERE=$PWD    T=    # 当前目录下build/core/envsetup.mk不存在(即当前目录不是编译根目录),    # 并且当前目录不是系统根目录    while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do        T=$PWD        # 当前$T目录下存在文件Android.mk        if [ -f "$T/Android.mk" ]; then            # 转到$T目录            \cd $T            return        fi        # 转到外层目录        \cd ..    done    # 恢复操作前的路径    \cd $HERE    echo "can't find Android.mk"}

getprebuilt

getprebuilt返回ANDROID_PREBUILTS的路径

命令行直接调用getprebuilt

ygu@guyongqiangx:src$ getprebuilt/android/src/prebuilt/linux-x86

实际上src目录下并不存在路径prebuilt/linux-x86

function getprebuilt{    # 通过函数get_abs_build_var返回ANDROID_PREBUILTS设置    get_abs_build_var ANDROID_PREBUILTS}

setpaths

setpaths

function setpaths(){    # 如果找不到编译的根目录,则退出设置    T=$(gettop)    if [ ! "$T" ]; then        echo "Couldn't locate the top of the tree.  Try setting TOP."        return    fi    ##################################################################    #                                                                #    #              Read me before you modify this code               #    #                                                                #    #   This function sets ANDROID_BUILD_PATHS to what it is adding  #    #   to PATH, and the next time it is run, it removes that from   #    #   PATH.  This is required so lunch can be run more than once   #    #   and still have working paths.                                #    #                                                                #    ##################################################################    # Note: on windows/cygwin, ANDROID_BUILD_PATHS will contain spaces    # due to "C:\Program Files" being in the path.    # 将$ANDROID_BUILD_PATHS和$ANDROID_PRE_BUILD_PATHS指定的路径添加到$PATH中,并export    # out with the old    if [ -n "$ANDROID_BUILD_PATHS" ] ; then        export PATH=${PATH/$ANDROID_BUILD_PATHS/}    fi    if [ -n "$ANDROID_PRE_BUILD_PATHS" ] ; then        export PATH=${PATH/$ANDROID_PRE_BUILD_PATHS/}        # strip leading ':', if any        # 说实话,我没搞懂这个是什么意思,求大神指点下        export PATH=${PATH/:%/}    fi    # and in with the new    # 设置ANDROID_PREBUILT和ANDROID_GCC_PREBUILTS相应的路径    prebuiltdir=$(getprebuilt)    gccprebuiltdir=$(get_abs_build_var ANDROID_GCC_PREBUILTS)    # defined in core/config.mk    # 设置TARGET_GCC_VERSION和2ND_TARGET_GCC_VERSION    targetgccversion=$(get_build_var TARGET_GCC_VERSION)    targetgccversion2=$(get_build_var 2ND_TARGET_GCC_VERSION)    export TARGET_GCC_VERSION=$targetgccversion    # The gcc toolchain does not exists for windows/cygwin. In this case, do not reference it.    export ANDROID_TOOLCHAIN=    export ANDROID_TOOLCHAIN_2ND_ARCH=    # 根据get_build_var返回的TARGET_ARCH分别设置{x86, x86_64, arm, arm64,mips|mips64}体系结构对应的toolchaindir名称    local ARCH=$(get_build_var TARGET_ARCH)    case $ARCH in        x86) toolchaindir=x86/x86_64-linux-android-$targetgccversion/bin            ;;        x86_64) toolchaindir=x86/x86_64-linux-android-$targetgccversion/bin            ;;        arm) toolchaindir=arm/arm-linux-androideabi-$targetgccversion/bin            ;;        arm64) toolchaindir=aarch64/aarch64-linux-android-$targetgccversion/bin;               toolchaindir2=arm/arm-linux-androideabi-$targetgccversion2/bin            ;;        mips|mips64) toolchaindir=mips/mips64el-linux-android-$targetgccversion/bin            ;;        *)            echo "Can't find toolchain for unknown architecture: $ARCH"            toolchaindir=xxxxxxxxx            ;;    esac    # 设置ANDROID_TOOLCHAIN和ANDROID_TOOLCHAIN_2ND_ARCH环境变量,用于指示toolchain路径    if [ -d "$gccprebuiltdir/$toolchaindir" ]; then        export ANDROID_TOOLCHAIN=$gccprebuiltdir/$toolchaindir    fi    if [ -d "$gccprebuiltdir/$toolchaindir2" ]; then        export ANDROID_TOOLCHAIN_2ND_ARCH=$gccprebuiltdir/$toolchaindir2    fi    # 设置Android编译相关的路径变量ANDROID_BUILD_PATHS    export ANDROID_DEV_SCRIPTS=$T/development/scripts:$T/prebuilts/devtools/tools:$T/external/selinux/prebuilts/bin    export ANDROID_BUILD_PATHS=$(get_build_var ANDROID_BUILD_PATHS):$ANDROID_TOOLCHAIN:$ANDROID_TOOLCHAIN_2ND_ARCH:$ANDROID_DEV_SCRIPTS:    # If prebuilts/android-emulator// exists, prepend it to our PATH    # to ensure that the corresponding 'emulator' binaries are used.    # 基于不同的系统设置ANDROID_EMULATOR_PREBUILTS变量    case $(uname -s) in        Darwin)            ANDROID_EMULATOR_PREBUILTS=$T/prebuilts/android-emulator/darwin-x86_64            ;;        Linux)            ANDROID_EMULATOR_PREBUILTS=$T/prebuilts/android-emulator/linux-x86_64            ;;        *)            ANDROID_EMULATOR_PREBUILTS=            ;;    esac    # 如果ANDROID_EMULATOR_PREBUILTS变量指定的目录存在,则将其添加到Android编译相关的路径变量ANDROID_BUILD_PATHS中    if [ -n "$ANDROID_EMULATOR_PREBUILTS" -a -d "$ANDROID_EMULATOR_PREBUILTS" ]; then        ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS$ANDROID_EMULATOR_PREBUILTS:        export ANDROID_EMULATOR_PREBUILTS    fi    # 将Android编译相关的路径ANDROID_BUILD_PATHS添加到PATH中    export PATH=$ANDROID_BUILD_PATHS$PATH    # 将development/python-packages添加到python运行的查找路径中    export PYTHONPATH=$T/development/python-packages:$PYTHONPATH    unset ANDROID_JAVA_TOOLCHAIN    unset ANDROID_PRE_BUILD_PATHS    # 如果设置了$JAVA_HOME,则将其加入到PATH变量中    if [ -n "$JAVA_HOME" ]; then        export ANDROID_JAVA_TOOLCHAIN=$JAVA_HOME/bin        export ANDROID_PRE_BUILD_PATHS=$ANDROID_JAVA_TOOLCHAIN:        export PATH=$ANDROID_PRE_BUILD_PATHS$PATH    fi    # 设置ANDROID_PRODUCT_OUT和OUT环境变量    unset ANDROID_PRODUCT_OUT    export ANDROID_PRODUCT_OUT=$(get_abs_build_var PRODUCT_OUT)    export OUT=$ANDROID_PRODUCT_OUT    # 设置HOST_OUT环境变量    unset ANDROID_HOST_OUT    export ANDROID_HOST_OUT=$(get_abs_build_var HOST_OUT)    # needed for building linux on MacOS    # TODO: fix the path    #export HOST_EXTRACFLAGS="-I "$T/system/kernel_headers/host_include}

set_java_home

set_java_home

# Force JAVA_HOME to point to java 1.7/1.8 if it isn't already set.function set_java_home() {    # Clear the existing JAVA_HOME value if we set it ourselves, so that    # we can reset it later, depending on the version of java the build    # system needs.    #    # If we don't do this, the JAVA_HOME value set by the first call to    # build/envsetup.sh will persist forever.    #    # 如果已经设置$ANDROID_SET_JAVA_HOME变量,则清空JAVA_HOME    if [ -n "$ANDROID_SET_JAVA_HOME" ]; then      export JAVA_HOME=""    fi    # 如果JAVA_HOME没有设置    if [ ! "$JAVA_HOME" ]; then      # 如果已经设置了LEGACY_USE_JAVA7,说明强制指定使用JDK7      if [ -n "$LEGACY_USE_JAVA7" ]; then        echo Warning: Support for JDK 7 will be dropped. Switch to JDK 8.        # 根据编译系统设置JDK7的JAVA_HOME        case `uname -s` in            Darwin) # Mac                export JAVA_HOME=$(/usr/libexec/java_home -v 1.7)                ;;            *)                # 默认设置为java-7-openjdk-amd64的路径                export JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64                ;;        esac      # 如果没有设置LEGACY_USE_JAVA7,则使用JDK 8      else        # 根据系统设置JDK8的JAVA_HOME        case `uname -s` in            Darwin)                export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)                ;;            *)                export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64                ;;        esac      fi      # Keep track of the fact that we set JAVA_HOME ourselves, so that      # we can change it on the next envsetup.sh, if required.      #      # 设置ANDROID_SET_JAVA_HOME作为已经设置了JAVA_HOME的标识      export ANDROID_SET_JAVA_HOME=true    fi}

printconfig

printconfig输出当前的编译配置,如:

ygu@guyongqiangx:src$ printconfig============================================PLATFORM_VERSION_CODENAME=RELPLATFORM_VERSION=7.1.1TARGET_PRODUCT=bcm7252ssffdr4TARGET_BUILD_VARIANT=userdebugTARGET_BUILD_TYPE=releaseTARGET_BUILD_APPS=TARGET_ARCH=armTARGET_ARCH_VARIANT=armv7-a-neonTARGET_CPU_VARIANT=cortex-a15TARGET_2ND_ARCH=TARGET_2ND_ARCH_VARIANT=TARGET_2ND_CPU_VARIANT=HOST_ARCH=x86_64HOST_2ND_ARCH=x86HOST_OS=linuxHOST_OS_EXTRA=Linux-4.2.0-42-generic-x86_64-with-Ubuntu-14.04-trustyHOST_CROSS_OS=windowsHOST_CROSS_ARCH=x86HOST_CROSS_2ND_ARCH=x86_64HOST_BUILD_TYPE=releaseBUILD_ID=NMF27DOUT_DIR=out============================================
function printconfig(){    # 检查编译根目录    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来打印输出当前的编译配置    get_build_var report_config}

getdriver

getdriver在定义了 WITHSTATICANALYZER WITH_STATIC_ANALYZER没有定义,所以getdriver调用返回空

# Return driver for "make", if any (eg. static analyzer)function getdriver(){    local T="$1"    # 检查$WITH_STATIC_ANALYZER,如果设置为0,就将其内容清空    test "$WITH_STATIC_ANALYZER" = "0" && unset WITH_STATIC_ANALYZER    if [ -n "$WITH_STATIC_ANALYZER" ]; then        # 好吧,将这一堆字符串返回,我也不知道干什么用,求大神指导下        echo "\$T/prebuilts/misc/linux-x86/analyzer/tools/scan-build/scan-build \--use-analyzer $T/prebuilts/misc/linux-x86/analyzer/bin/analyzer \--status-bugs \--top=$T"    fi}

gettargetarch

gettargetarch函数返回编译目标系统的CPU架构,如arm

function gettargetarch{    # 通过get_build_var TARGET_ARCH返回cpu arch    get_build_var TARGET_ARCH}

godir

godir函数的用法为”Usage: godir ”,在编译路径下搜索匹配模式的目录,然后跳转到此目录。

ygu@guyongqiangx:src$ godir fugu   [1] ./device/asus/fugu   [2] ./device/asus/fugu-kernel   [3] ./device/asus/fugu/bluetooth   [4] ./device/asus/fugu/dumpstate   [5] ./device/asus/fugu/factory-images   [6] ./device/asus/fugu/kernel-headers/drm   [7] ./device/asus/fugu/kernel-headers/drm/ttm   [8] ./device/asus/fugu/kernel-headers/linux   [9] ./device/asus/fugu/kernel-headers/linux/sound   ...Select one:

在上面的提示后输入1,命令行跳转到./device/asus/fugu目录下,实现了跟函数名一致的”go dir”的操作。

function godir () {    # 如果godir的第一个参数$1为空,显示godir的用法    if [[ -z "$1" ]]; then        echo "Usage: godir "        return    fi    # 变量T存放编译根目录路径    T=$(gettop)    # 根据是否设置$OUT_DIR,设置$FILELIST    if [ ! "$OUT_DIR" = "" ]; then        # 创建$OUT_DIR目录        mkdir -p $OUT_DIR        FILELIST=$OUT_DIR/filelist    else        FILELIST=$T/filelist    fi    # 如果$FILELIST文件不存在,则将find命令结果输出到filelist中    if [[ ! -f $FILELIST ]]; then        echo -n "Creating index..."        #         # 使用find命令从编译的根目录下查找文件"-type f"(目录out和.repo除外),并将结果输出到$FILELIST文件中        # out目录和.repo目录的排除选项分别为:        # - "-wholename ./out -prune"        # - "-wholename ./.repo -prune"        # 关于find的"-wholename pattern"选项,其行为跟"-path pattern"基本一样,具体可以查看find的帮助信息        # 因此filelist文件保存了除out和.repo目录外其余目录的完整文件名        (\cd $T; find . -wholename ./out -prune -o -wholename ./.repo -prune -o -type f > $FILELIST)        echo " Done"        echo ""    fi    local lines    # 根据传入godir的参数,在filelist中搜索,并用sed处理后将结果存放在lines中    # 操作"sed -e 's/\/[^/]*$//'"仅保留完整文件名的路径部分    lines=($(\grep "$1" $FILELIST | sed -e 's/\/[^/]*$//' | sort | uniq))    # 检查lines中的结果,即filelist通过grep和sed操作后,是否还有匹配的目录    if [[ ${#lines[@]} = 0 ]]; then        echo "Not found"        return    fi    local pathname    local choice    # 如果lines的结果多于1行,则对各行进行编号并输出    if [[ ${#lines[@]} > 1 ]]; then        while [[ -z "$pathname" ]]; do            # 从1开始编号            local index=1            local line            for line in ${lines[@]}; do                # 对每行以类似以下的格式进行输出:                # $ godir fugu                #   [1] ./device/asus/fugu                #   [2] ./device/asus/fugu-kernel                #   [3] ./device/asus/fugu/bluetooth                #   [4] ./device/asus/fugu/dumpstate                #                 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中        pathname=${lines[0]}    fi    # 转到选择的目标路径    \cd $T/$pathname}

pez

pez函数的参数”$@”是一条可执行命令,通过执行结果来决定打印FAILUE和SUCCESS的颜色,失败打印红色的FAILURE,成功打印绿色的SUCCESS

# Print colored exit conditionfunction pez {    # 执行参数命令#@    "$@"    # 获取$@命令的返回值    local retval=$?    # 检查返回值是否为0    if [ $retval -ne 0 ]    then        # 输出红色的FAILURE        echo $'\E'"[0;31mFAILURE\e[00m"    else        # 输出绿色的SUCCESS        echo $'\E'"[0;32mSUCCESS\e[00m"    fi    # 将$@命令的执行结果返回给外层调用    return $retval}

findmakefile

findmakefile查找当前模块的Android.mk并输出文件的详细路径
以下是在目录cd device/asus/fugu/kernel-headers/linux/sound/内执行findmakefile的例子:

ygu@guyongqiangx:src$ cd device/asus/fugu/kernel-headers/linux/sound/ygu@guyongqiangx:src/device/asus/fugu/kernel-headers/linux/sound$ findmakefile/android/src/device/asus/fugu/Android.mkygu@guyongqiangx:src/device/asus/fugu/kernel-headers/linux/sound$ 

显然,会在当前目录下逐层往外查找Android.mk,找到后显示Android.mk的完整路径,显示完路径后仍然在当前目录下。

function findmakefile(){    TOPFILE=build/core/envsetup.mk    local HERE=$PWD    T=    # 检查是否已经在编译的根目录或系统根目录了    while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do        T=`PWD= /bin/pwd`        # 如果目录下存在Android.mk        if [ -f "$T/Android.mk" ]; then            # 输出Android.mk的完整路径            echo $T/Android.mk            # 切换回当前执行findmakefile的目录            \cd $HERE            return        fi        # 转到上级目录        \cd ..    done    # 切换回当前执行findmakefile的目录    \cd $HERE}

settitle

settitle根据板子设置,更细PROMPT_COMMAND设置
这里的PROMPT_COMMAND设置看起来好像没有什么用。
因此设置PROMPT_COMMAND有什么目的,我完全没弄清楚,求指点。
以下是关于PROMPT_COMMAND的两个链接:
http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x264.html

https://unix.stackexchange.com/questions/27692/in-bash-why-is-prompt-command-set-to-something-invisible

function settitle(){    # 如果STAY_OFF_MY_LAWN为"",则根据arch, product, variant, apps更新环境的PROMPT_COMMAND    if [ "$STAY_OFF_MY_LAWN" = "" ]; then        local arch=$(gettargetarch)        local product=$TARGET_PRODUCT        local variant=$TARGET_BUILD_VARIANT        local apps=$TARGET_BUILD_APPS        if [ -z "$apps" ]; then            export PROMPT_COMMAND="echo -ne \"\033]0;[${arch}-${product}-${variant}] ${USER}@${HOSTNAME}: ${PWD}\007\""        else            export PROMPT_COMMAND="echo -ne \"\033]0;[$arch $apps $variant] ${USER}@${HOSTNAME}: ${PWD}\007\""        fi    fi}

set_sequence_number

set_sequence_number将BUILD_ENV_SEQUENCE_NUMBER设置为10
说实话,我也不知道这个BUILD_ENV_SEQUENCE_NUMBER是做什么用的

function set_sequence_number(){    export BUILD_ENV_SEQUENCE_NUMBER=10}

set_stuff_for_environment

set_stuff_for_environment 调用前面定义的一系列函数设置title, java home, path, sequence number, android build top dir等环境变量

function set_stuff_for_environment(){    # 设置PROMPT_COMMAND    settitle    # 设置JAVA_HOME    set_java_home    # 将Android编译相关的toolchain和tools路径导入到PATH变量    setpaths    # 设置 BUILD_ENV_SEQUENCE_NUMBER    set_sequence_number    # 设置ANDROID_BUILD_TOP为编译根目录路径    export ANDROID_BUILD_TOP=$(gettop)    # With this environment variable new GCC can apply colors to warnings/errors    # 通过注释看起来是设置GCC工具在error, warning, note, caret, lucus, quote情形下的颜色    export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'    # 看起来这个选项是给ASAN使用的,参考链接:http://www.freebuf.com/news/83811.html    export ASAN_OPTIONS=detect_leaks=0}

addcompletions

addcompletions命令将sdk/bash_completion目录下所有的*.bash文件通过’.’操作导入到当前环境中来

function addcompletions(){    local T dir f    # Keep us from trying to run in something that isn't bash.    # 检测shell版本字符串BASH_VERSION长度为0时,返回    if [ -z "${BASH_VERSION}" ]; then        return    fi    # Keep us from trying to run in bash that's too old.    # 检测bash主版本低于3时返回    if [ ${BASH_VERSINFO[0]} -lt 3 ]; then        return    fi    # 指定dir目录并检查是否存在    dir="sdk/bash_completion"    if [ -d ${dir} ]; then        # 获取sdk/bash_completion下的*.bash文件列表,并将这些*.bash文件包含进来        for f in `/bin/ls ${dir}/[a-z]*.bash 2> /dev/null`; do            echo "including $f"            # 对*.bash文件执行'.'操作            . $f        done    fi}

1.2 编译环境设置类

build_build_var_cache

build_build_var_cache命令用于创建var_cache_xxx='yyy'abs_var_cache_xxx='yyy'的键值对,用于存储环境变量。
主要的键值对包括:
- var_cache_2ND_TARGET_GCC_VERSION
- var_cache_ANDROID_BUILD_PATHS
- var_cache_TARGET_ARCH
- var_cache_TARGET_DEVICE
- var_cache_TARGET_GCC_VERSION
- var_cache_print
- var_cache_report_config
- abs_var_cache_ANDROID_GCC_PREBUILTS
- abs_var_cache_ANDROID_PREBUILTS
- abs_var_cache_HOST_OUT
- abs_var_cache_PRODUCT_OUT
- abs_var_cache_print
键值对分析:
- var_cache_printabs_var_cache_print是因为提取脚本分析错误的原因,误以为’print’也是get_build_var/get_build_var需要提取的参数
- var_cache_report_config存放了编译配置完整的的report

# Get all the build variables needed by this script in a single call to the build system.function build_build_var_cache(){    # 获取编译根目录    T=$(gettop)    # Grep out the variable names from the script.    #     # 我读书少,不太了解awk的用法,看了以下操作,确实开了眼界,值得借鉴    # 命令直接分析脚本提取参数,确保刚好是需要的参数,没有多一个,也没有少一个,哈哈    #    # 搜索envsetup.sh文件中所有调用get_build_var的地方,提取其调用参数,并存放到cached_vars中    # 我尝试在命令行直接执行操作,其得到的结果如下:    # ygu@guyongqiangx:src$ cat build/envsetup.sh | tr '()' '  ' | awk '{for(i=1;i<=NF;i++) if($i~/get_build_var/) print $(i+1)}'    # print    #     # TARGET_DEVICE    # TARGET_GCC_VERSION    # 2ND_TARGET_GCC_VERSION    # TARGET_ARCH    # ANDROID_BUILD_PATHS    # report_config    # TARGET_ARCH    # 这里分析代码时把设置cached_vars的这行也包含在里面了,所以可以看到结果的第一行中有 print    #    cached_vars=`cat $T/build/envsetup.sh | tr '()' '  ' | awk '{for(i=1;i<=NF;i++) if($i~/get_build_var/) print $(i+1)}' | sort -u | tr '\n' ' '`    # 搜索envsetup.sh文件中所有调用get_abs_build_var的地方,提取其调用参数,并存放到cached_abs_vars中    # ygu@guyongqiangx:src$ cat build/envsetup.sh | tr '()' '  ' | awk '{for(i=1;i<=NF;i++) if($i~/get_abs_build_var/) print $(i+1)}'    # print    #     # ANDROID_GCC_PREBUILTS    # PRODUCT_OUT    # HOST_OUT    # ANDROID_PREBUILTS    # 这里跟提取get_build_var的参数一样,也包含了print一行    #     cached_abs_vars=`cat $T/build/envsetup.sh | tr '()' '  ' | awk '{for(i=1;i<=NF;i++) if($i~/get_abs_build_var/) print $(i+1)}' | sort -u | tr '\n' ' '`    # Call the build system to dump the "=" pairs as a shell script.    #    # 通过 command make --no-print-directory -f build/core/config.mk 命令来操作cached_vars和cached_abs_vars相关变量    # 后续打算对build/core/config.mk进行分析,看看到底发生了什么,这里暂且略过。    #    build_dicts_script=`\cd $T; CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \                        command make --no-print-directory -f build/core/config.mk \                        dump-many-vars \                        DUMP_MANY_VARS="$cached_vars" \                        DUMP_MANY_ABS_VARS="$cached_abs_vars" \                        DUMP_VAR_PREFIX="var_cache_" \                        DUMP_ABS_VAR_PREFIX="abs_var_cache_"`    # 检查上一步的返回值,即上一行命令中make操作的执行结果                          local ret=$?    if [ $ret -ne 0 ]    then        unset build_dicts_script        return $ret    fi    # Excute the script to store the "=" pairs as shell variables.    # 对$build_dicts_script内容进行求值处理,从注释看起来是建立一个"="的键值对    # 通过在eval操作前“echo $build_dicts_script”输出发现,其格式是这样的(为便于阅读,已经经过换行处理):    # ygu@guyongqiangx:src$ build_build_var_cache      # var_cache_2ND_TARGET_GCC_VERSION=''     # var_cache_ANDROID_BUILD_PATHS='/android/src/out/host/linux-x86/bin'     # var_cache_TARGET_ARCH='arm'     # var_cache_TARGET_DEVICE='bcm7252ssffdr4'     # var_cache_TARGET_GCC_VERSION='4.9'     # var_cache_print=''     # var_cache_report_config=` \    #           echo '============================================'; \    #           echo 'PLATFORM_VERSION_CODENAME=REL'; \    #           echo 'PLATFORM_VERSION=7.1.1'; \    #           echo 'TARGET_PRODUCT=bcm7252ssffdr4'; \    #           echo 'TARGET_BUILD_VARIANT=userdebug'; \    #           echo 'TARGET_BUILD_TYPE=release'; \    #           echo 'TARGET_BUILD_APPS='; \    #           echo 'TARGET_ARCH=arm'; \    #           echo 'TARGET_ARCH_VARIANT=armv7-a-neon'; \    #           echo 'TARGET_CPU_VARIANT=cortex-a15'; \    #           echo 'TARGET_2ND_ARCH='; \    #           echo 'TARGET_2ND_ARCH_VARIANT='; \    #           echo 'TARGET_2ND_CPU_VARIANT='; \    #           echo 'HOST_ARCH=x86_64'; \    #           echo 'HOST_2ND_ARCH=x86'; \    #           echo 'HOST_OS=linux'; \    #           echo 'HOST_OS_EXTRA=Linux-4.2.0-42-generic-x86_64-with-Ubuntu-14.04-trusty'; \    #           echo 'HOST_CROSS_OS=windows'; \    #           echo 'HOST_CROSS_ARCH=x86'; \    #           echo 'HOST_CROSS_2ND_ARCH=x86_64'; \    #           echo 'HOST_BUILD_TYPE=release'; \    #           echo 'BUILD_ID=NMF27D'; \    #           echo 'OUT_DIR=out'; \    #           echo '============================================';`     # abs_var_cache_ANDROID_GCC_PREBUILTS='/android/src/prebuilts/gcc/linux-x86'     # abs_var_cache_ANDROID_PREBUILTS='/android/src/prebuilt/linux-x86'     # abs_var_cache_HOST_OUT='/android/src/out/host/linux-x86'     # abs_var_cache_PRODUCT_OUT='/android/src/out/target/product/bcm7252ssffdr4'     # abs_var_cache_print=''    #    eval "$build_dicts_script"    # 显然,执行eval求值的结果就是建立如下类型的两种键值对:    # var_cache_xxx='yyy'    # abs_var_cache_xxx='yyy'    #    # 保存eval的求值结果    ret=$?    unset build_dicts_script    # 检查$build_dicts_scripts操作是否成功    if [ $ret -ne 0 ]    then        return $ret    fi    # 设置CACHE_READY标志    BUILD_VAR_CACHE_READY="true"}

destroy_build_var_cache

destroy_build_var_cache命令清空所有通过build_build_var_cache函数建立的环境变量。

# Delete the build var cache, so that we can still call into the build system# to get build variables not listed in this script.function destroy_build_var_cache(){    # 取消CACHE_READY标志    unset BUILD_VAR_CACHE_READY    # 根据cached_vars列表取消相应变量var_cache_xxx的设置    for v in $cached_vars; do      unset var_cache_$v    done    # 清空cached_vars列表    unset cached_vars    # 根据cached_abs_vars列表取消相应变量abs_var_cache_xxx的设置    for v in $cached_abs_vars; do      unset abs_var_cache_$v    done    # 清空cached_abs_vars列表    unset cached_abs_vars}

get_abs_build_var

get_abs_build_var命令查找通过build_build_var_cache函数建立的键值对列表,输出其参数对应的键值
如,get_abs_build_var xxx,则返回变量abs_var_cache_xxx的值。
get_abs_build_var函数跟get_build_var函数操作一样,唯一不同的地方是前者获取abs_var_cache_xxx变量的值,后者获取var_cache_xxx变量的值。

# Get the value of a build variable as an absolute path.function get_abs_build_var(){    # 如果$BUILD_VAR_CACHE_READY=true,直接返回前缀为abs_var_cache_的变量    # 函数 getprebuilt中,调用get_abs_build_var ANDROID_PREBUILTS,直接返回$abs_var_cache_ANDROID_PREBUILTS    if [ "$BUILD_VAR_CACHE_READY" = "true" ]    then        # 有意思,通过对echo输出的内容进行eval来设置        eval echo \"\${abs_var_cache_$1}\"    return    fi    # 查找编译根目录,如果没找到,则退出函数    T=$(gettop)    if [ ! "$T" ]; then        echo "Couldn't locate the top of the tree.  Try setting TOP." >&2        return    fi    # 切换到编译的根目录,执行命令"make --no-print-directory -f build/core/config.mk dumpvar-abs-$1"    # 函数 getprebuilt中,调用get_abs_build_var ANDROID_PREBUILTS,执行命令:"make --no-print-directory -f build/core/config.mk dumpvar-abs-ANDROID_PREBUILTS"    # 这里应该是通过make命令来重新生成`$abs_var_cache_ANDROID_PREBUILTS`变量    (\cd $T; CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \      command make --no-print-directory -f build/core/config.mk dumpvar-abs-$1)}

get_build_var

get_build_var命令超找通过build_build_var_cache函数建立的键值对列表,输出其参数对应的键值
如,get_build_var xxx,则返回变量var_cache_xxx的值。
get_build_var函数跟get_abs_build_var函数操作一样,唯一不同的地方是前者获取var_cache_xxx变量的值,后者获取abs_var_cache_xxx变量的值。

# Get the exact value of a build variable.function get_build_var(){    # 如果$BUILD_VAR_CACHE_READY=true,直接返回前缀为var_cache_的变量    if [ "$BUILD_VAR_CACHE_READY" = "true" ]    then        eval echo \"\${var_cache_$1}\"    return    fi    # 查找编译根目录,如果没找到,则退出函数    T=$(gettop)    if [ ! "$T" ]; then        echo "Couldn't locate the top of the tree.  Try setting TOP." >&2        return    fi    # 切换到编译的根目录,执行命令"make --no-print-directory -f build/core/config.mk dumpvar-$1"    # 来重新生成`$var_cache_ANDROID_PREBUILTS`变量    (\cd $T; CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \      command make --no-print-directory -f build/core/config.mk dumpvar-$1)}

check_product

check_product检查TARGET_DEVICE设置是否有效

# check to see if the supplied product is one we can buildfunction check_product(){    # 查找编译根目录,如果没找到,则退出函数    T=$(gettop)    if [ ! "$T" ]; then        echo "Couldn't locate the top of the tree.  Try setting TOP." >&2        return    fi        # 将参数1设置到TARGET_PRODUCT,其余相关参数置空        TARGET_PRODUCT=$1 \        TARGET_BUILD_VARIANT= \        TARGET_BUILD_TYPE= \        TARGET_BUILD_APPS= \        # 通过get_build_var获取var_cache_TARGET_DEVICE变量,并将标准输出的内容重定向到/dev/null        get_build_var TARGET_DEVICE > /dev/null    # hide successful answers, but allow the errors to show    # 这里根据注释说,错误信息会显示,并且check_product的返回值是get_build_var函数的调用结果}

check_variant

check_variant 检查输入的选项是否(user userdebug eng)这三者之一,找到返回0,找不到返回1

VARIANT_CHOICES=(user userdebug eng)# check to see if the supplied variant is validfunction check_variant(){    # 遍历数组VARIANT_CHOICES=(user userdebug eng)    for v in ${VARIANT_CHOICES[@]}    do        # 如果匹配,返回0        if [ "$v" = "$1" ]        then            return 0        fi    done    # 没有找到,返回1    return 1}

choosetype

choosetype 根据传入选项或读取用户输入设置编译版本是release还是debug版

function choosetype(){    # 显示提示信息    echo "Build type choices are:"    echo "     1. release"    echo "     2. debug"    echo    # 设置默认选项为"1. release"    local DEFAULT_NUM DEFAULT_VALUE    DEFAULT_NUM=1    DEFAULT_VALUE=release    export TARGET_BUILD_TYPE=    local ANSWER    # 检查是否已经设置TARGET_BUILD_TYPE,设置后其值不为空    while [ -z $TARGET_BUILD_TYPE ]    do        # 提示默认选项为"[1]"        echo -n "Which would you like? ["$DEFAULT_NUM"] "        # 如果choosetype不带参数,则读取参数到ANSWER        if [ -z "$1" ] ; then            read ANSWER        else            # choosetype带参数的情况下,直接将参数保存到ANSWER            echo $1            ANSWER=$1        fi        # 根据ANSWER,设置TARGET_BUILD_TYPE        case $ANSWER in        "")            export TARGET_BUILD_TYPE=$DEFAULT_VALUE            ;;        1)            export TARGET_BUILD_TYPE=release            ;;        release)            export TARGET_BUILD_TYPE=release            ;;        2)            export TARGET_BUILD_TYPE=debug            ;;        debug)            export TARGET_BUILD_TYPE=debug            ;;        *)            echo            echo "I didn't understand your response.  Please try again."            echo            ;;        esac        # 如果choosetype带有参数,则退出while循环,不用再提示设置        if [ -n "$1" ] ; then            break        fi    done    # 设置编译环境相关变量var_cache_xxx和abs_var_cache_xxx    build_build_var_cache    # 设置其他环境变量,如PROMPT_COMMAND,编译toolchain和tools相关的路径等    set_stuff_for_environment    # 清空环境变量,不懂为什么刚设置了这些变量,这里又要取消?    destroy_build_var_cache}

chooseproduct

chooseproduct 根据预先设置的变量或读取用户输入设置TARGET_PRODUCT

## This function isn't really right:  It chooses a TARGET_PRODUCT# based on the list of boards.  Usually, that gets you something# that kinda works with a generic product, but really, you should# pick a product by name.## 注释里面提到一些trick,说需要通过name来选择product#function chooseproduct(){    # 检查TARGET_PRODUCT是否为空    if [ "x$TARGET_PRODUCT" != x ] ; then        # 已经设置TARGET_PRODUCT,则保存到default_value        default_value=$TARGET_PRODUCT    else        # 没有设置TARGET_PRODUCT,则设置default_value为aosp_arm        default_value=aosp_arm    fi    export TARGET_BUILD_APPS=    export TARGET_PRODUCT=    local ANSWER    # 读取TARGET_PRODUCT设置    while [ -z "$TARGET_PRODUCT" ]    do        # 如果chooseproduct有带参数,则将参数保存到ANSWER        # 否则,在命令行读取用户输入数据,并保存到ANSWER        echo -n "Which product would you like? [$default_value] "        if [ -z "$1" ] ; then            read ANSWER        else            echo $1            ANSWER=$1        fi        # 如果 ANSWER 长度为0,即用户直接回车输入的情况        if [ -z "$ANSWER" ] ; then            export TARGET_PRODUCT=$default_value        else            # 调用check_product函数检查输入ANSWER            if check_product $ANSWER            then                export TARGET_PRODUCT=$ANSWER            else                echo "** Not a valid product: $ANSWER"            fi        fi        # 如果chooseproduct带了参数,则不再读取输入,跳出循环        if [ -n "$1" ] ; then            break        fi    done    # 设置编译环境相关变量var_cache_xxx和abs_var_cache_xxx    build_build_var_cache    # 设置其他环境变量,如PROMPT_COMMAND,编译toolchain和tools相关的路径等    set_stuff_for_environment    # 清空环境变量,不懂为什么刚设置了这些变量,这里又要取消?    destroy_build_var_cache}

choosevariant

choosevariant 读取用户输入设置TARGET_BUILD_VARIANTuser,userdebugeng

function choosevariant(){    echo "Variant choices are:"    local index=1    local v    # 从1开始,循环显示VARIANT_CHOICES数组的内容,效果如下:    # $ choosevariant    # Variant choices are:    #      1. user    #      2. userdebug    #      3. eng    # Which would you like? [eng]    #    for v in ${VARIANT_CHOICES[@]}    do        # The product name is the name of the directory containing        # the makefile we found, above.        echo "     $index. $v"        index=$(($index+1))    done    local default_value=eng    local ANSWER    export TARGET_BUILD_VARIANT=    # 交互读取TARGET_BUILD_VARIANT设置    while [ -z "$TARGET_BUILD_VARIANT" ]    do        # 读取用户输入,默认为[eng]        echo -n "Which would you like? [$default_value] "        if [ -z "$1" ] ; then            read ANSWER        else            # 如果choosevariant有带参数,则直接用保存参数            echo $1            ANSWER=$1        fi        # 如果 ANSWER 长度为0,即用户直接回车输入的情况        if [ -z "$ANSWER" ] ; then            export TARGET_BUILD_VARIANT=$default_value        # 将ANSWER的数值转换为VARIANT_CHOICES数组中的字符串        elif (echo -n $ANSWER | grep -q -e "^[0-9][0-9]*$") ; then            if [ "$ANSWER" -le "${#VARIANT_CHOICES[@]}" ] ; then                export TARGET_BUILD_VARIANT=${VARIANT_CHOICES[$(($ANSWER-1))]}            fi        else            # 调用check_variant是否为有效值            if check_variant $ANSWER            then                export TARGET_BUILD_VARIANT=$ANSWER            else                echo "** Not a valid variant: $ANSWER"            fi        fi        # 如果choosevariant带了参数,则不再读取输入,跳出循环        if [ -n "$1" ] ; then            break        fi    done}

choosecombo

choosecombo根据传入的3个参数,分别设置type(release, debug), product和variant(user, userdebug, eng)参数

function choosecombo(){    # 使用参数1设置type (release, debug)    choosetype $1    # 使用参数2设置product    echo    echo    chooseproduct $2    # 使用参数3设置variant (user, userdebug, eng)    echo    echo    choosevariant $3    echo    # 设置编译环境相关变量var_cache_xxx和abs_var_cache_xxx    build_build_var_cache    # 设置其他环境变量,如PROMPT_COMMAND,编译toolchain和tools相关的路径等    set_stuff_for_environment    # 显示当前选择的编译配置    printconfig    # 清空环境变量,不懂为什么刚设置了这些变量,这里又要取消?    destroy_build_var_cache}

add_lunch_combo

add_lunch_combo将提供的编译选项参数添加到LUNCH_MENU_CHOICES列表中

function add_lunch_combo(){    local new_combo=$1    local c    for c in ${LUNCH_MENU_CHOICES[@]} ; do        if [ "$new_combo" = "$c" ] ; then            return        fi    done    LUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]} $new_combo)}

print_lunch_menu

print_lunch_menu打印编译选项列表LUNCH_MENU_CHOICES的所有项

function print_lunch_menu(){    local uname=$(uname)    echo    echo "You're building on" $uname    echo    echo "Lunch menu... pick a combo:"    local i=1    local choice    # 遍历数组LUNCH_MENU_CHOICES,以类似"1. aosp_arm-eng"的格式输出    for choice in ${LUNCH_MENU_CHOICES[@]}    do        echo "     $i. $choice"        i=$(($i+1))    done    echo}

lunch

lunch操作根据传入参数选项设置TARGET_PRODUCT, TARGET_BUILD_VARIANTTARGET_BUILD_TYPE

function lunch(){    local answer    # 获取lunch操作的参数    if [ "$1" ] ; then        answer=$1    else        # lunch操作不带参数,则先显示lunch menu,然后读取用户输入        print_lunch_menu        echo -n "Which would you like? [aosp_arm-eng] "        read answer    fi    local selection=    # lunch操作得到的结果为空(例如用户直接在lunch要求输入时回车的情况)    # 则将选项默认为"aosp_arm-eng"    if [ -z "$answer" ]    then        selection=aosp_arm-eng    # lunch操作得到的输入是数字,则将数字转换为LUNCH_MENU_CHOICES中的字符串    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    # lunch操作得到的是字符串,直接将字符串保存到selection中          elif (echo -n $answer | grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$")    then        selection=$answer    fi    # 检查selection的值是否正常    # 例如选择了一个LUNCH_MENU_CHOICES中不存在的索引,则selection就为空    if [ -z "$selection" ]    then        echo        echo "Invalid lunch combo: $answer"        return 1    fi    export TARGET_BUILD_APPS=    #    # 分离selection字符串,例如:aosp_fugu-userdebug    #    # 获取第一个'-'后的部分,这里即userdebug,保存到variant    local variant=$(echo -n $selection | sed -e "s/^[^\-]*-//")    # 检查variant是否合法,即是否选项(user, userdebug, eng)之一,如果不是,输出提示    check_variant $variant    if [ $? -ne 0 ]    then        echo        echo "** Invalid variant: '$variant'"        echo "** Must be one of ${VARIANT_CHOICES[@]}"        variant=    fi    # 获取最后一个'-'前的部分,这里即aosp_fugu,保存到product    local product=$(echo -n $selection | sed -e "s/-.*$//")    # 设置TARGET_PRODUCT和TARGET_BUILD_VARIANT    TARGET_PRODUCT=$product \    TARGET_BUILD_VARIANT=$variant \    # 根据前面的设置,更新编译环境相关变量    build_build_var_cache    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或variant为空的情况下,退出函数    if [ -z "$product" -o -z "$variant" ]    then        echo        return 1    fi    # export 编译选项TARGET_PRODUCT, TARGET_BUILD_VARIANT和TARGET_BUILD_TYPE三元组    export TARGET_PRODUCT=$product    export TARGET_BUILD_VARIANT=$variant    export TARGET_BUILD_TYPE=release    echo    # 设置其他环境变量,如PROMPT_COMMAND,编译toolchain和tools相关的路径等    set_stuff_for_environment    # 输出当前的设置选项    printconfig    # 清空环境变量,不懂为什么刚设置了这些变量,这里又要取消?    destroy_build_var_cache}

_lunch

_lunch命令提供lunch命令的补全操作

# Tab completion for lunch.function _lunch(){    local cur prev opts    COMPREPLY=()    cur="${COMP_WORDS[COMP_CWORD]}"    prev="${COMP_WORDS[COMP_CWORD-1]}"    # 生成补全结果    COMPREPLY=( $(compgen -W "${LUNCH_MENU_CHOICES[*]}" -- ${cur}) )    return 0}# 指定遇到lunch命令时,用_lunch函数的输出的内容进行补全complete -F _lunch lunch

m

可以在代码的任何一个目录里面执行m指令编译所有模块
其实很简单,就是用make的-C选项指定到代码的根目录。

function m(){    # 获取代码的根目录    local T=$(gettop)    # 编译无关,可以忽略此选项    local DRV=$(getdriver $T)    if [ "$T" ]; then        # 直接转到代码的根目录进行编译        $DRV make -C $T -f build/core/main.mk $@    else        echo "Couldn't locate the top of the tree.  Try setting TOP."        return 1    fi}

mm

mm指令编译当前目录下的所有模块

function mm(){    # 获取代码根目录    local T=$(gettop)    local DRV=$(getdriver $T)    # If we're sitting in the root of the build tree, just do a    # normal make.    # 如果在代码根目录下执行mm指令,转换为直接运行make指令    if [ -f build/core/envsetup.mk -a -f Makefile ]; then        $DRV make $@    else        # 超找当前目录对应模块的Android.mk文件        # Find the closest Android.mk file.        local M=$(findmakefile)        local MODULES=        local GET_INSTALL_PATH=        local ARGS=        # Remove the path to top as the makefilepath needs to be relative        # 将查找到的Android.mk转换为针对代码根目录的相对路径        local M=`echo $M|sed 's:'$T'/::'`        # 好吧,没有找到代码的根目录        if [ ! "$T" ]; then            echo "Couldn't locate the top of the tree.  Try setting TOP."            return 1        # 好吧,竟然没有Android.mk文件        elif [ ! "$M" ]; then            echo "Couldn't locate a makefile from the current directory."            return 1        else            # 逐个处理命令行参数            for ARG in $@; do                case $ARG in                  GET-INSTALL-PATH) GET_INSTALL_PATH=$ARG;;                esac            done            # 如果GET_INSTALL_PATH不为空,则            if [ -n "$GET_INSTALL_PATH" ]; then              MODULES=              ARGS=GET-INSTALL-PATH            # 编译所有模块            else              MODULES=all_modules              ARGS=$@            fi            # 转到代码根目录开始编译指定的模块            ONE_SHOT_MAKEFILE=$M $DRV make -C $T -f build/core/main.mk $MODULES $ARGS        fi    fi}

mmm

mmm手动指定目录和,其下面编译的模块,格式如下:
mmm dir1,dir2,dir3,dir4/,...:[module1],[module2],[module3],[module4] -options

function mmm(){    # 获取代码根目录    local T=$(gettop)    local DRV=$(getdriver $T)    # 在代码根目录存在的情况下,开始进行命令参数解析和编译    if [ "$T" ]; then        local MAKEFILE=        local MODULES=        local ARGS=        local DIR TO_CHOP        local GET_INSTALL_PATH=        # 提取编译命令中的选项参数,即符号"-"后面接的参数        local DASH_ARGS=$(echo "$@" | awk -v RS=" " -v ORS=" " '/^-.*$/')        # 提取编译命令中目录和模块部分,即符号的"-"前的参数部分        # 例如 "mmm dir1 dir2 dir3 dir4/:module1,module2,module3,module4"        local DIRS=$(echo "$@" | awk -v RS=" " -v ORS=" " '/^[^-].*$/')        for DIR in $DIRS ; do            #            # 编译命令类似 mmm dir1 dir2 dir3 dir4/:module1,module2,module3,module4            #            # 第一个sed命令获取$DIR中":"后面的module1~module4的部分            # 第二个sed命令替换到各个目标间的','号,将其转换为一个包含编译目标的数组,即模块            # 处理后变成 MODULES=module1 module2 module3 module4            MODULES=`echo $DIR | sed -n -e 's/.*:\(.*$\)/\1/p' | sed 's/,/ /'`            # 没有指定模块的话,默认为all_modules            if [ "$MODULES" = "" ]; then                MODULES=all_modules            fi            # 第一个sed命令提取$DIR的":"前的目录部分            # 第二个sed命令忽略目录部分的"/"后缀            # 处理后变成DIR=dir1 dir2 dir3 dir4            DIR=`echo $DIR | sed -e 's/:.*//' -e 's:/$::'`            # 如果处理后的文件夹得到的DIR下有Android.mk            if [ -f $DIR/Android.mk ]; then                # 计算代码根目录路径包含的字符数                local TO_CHOP=`(\cd -P -- $T && pwd -P) | wc -c | tr -d ' '`                # 代码根目录路径的字符数+1                local TO_CHOP=`expr $TO_CHOP + 1`                # 获取当前目录的绝对路径START                local START=`PWD= /bin/pwd`                # 获取当前目录相对于根目录的相对路径,并保存在MFILE中                local MFILE=`echo $START | cut -c${TO_CHOP}-`                # 构建Android.mk的相对路径                if [ "$MFILE" = "" ] ; then                    MFILE=$DIR/Android.mk                else                    MFILE=$MFILE/$DIR/Android.mk                fi                MAKEFILE="$MAKEFILE $MFILE"            else                # 如果处理后的文件夹下面没有Android.mk,说明其可能不是目录,而是某个命令,如showcommands                case $DIR in                  # 如果是showcommands, snode, dist或*=*的情况,将其作为真正编译命令的参数传递                  showcommands | snod | dist | *=*) ARGS="$ARGS $DIR";;                  GET-INSTALL-PATH) GET_INSTALL_PATH=$DIR;;                  # 不是showcommands, snode, dist等的情况下,检查这个目录是否存在                  *) if [ -d $DIR ]; then                         # 目录存在,但Android.mk不存在,提示没有Android.mk文件                         echo "No Android.mk in $DIR.";                     else                         echo "Couldn't locate the directory $DIR";                     fi                     return 1;;                esac            fi        done        # 将GET_INSTALL_PATH作为参数传入编译        if [ -n "$GET_INSTALL_PATH" ]; then          ARGS=$GET_INSTALL_PATH          MODULES=        fi        # 将mmm命令“-”后的选项和模块参数以及其他参数传递给主makefile进行编译        ONE_SHOT_MAKEFILE="$MAKEFILE" $DRV make -C $T -f build/core/main.mk $DASH_ARGS $MODULES $ARGS    # 获取代码根目录是T返回空,说明没有找到代码根目录,有什么办法?那就显示错误信息并退出吧          else        echo "Couldn't locate the top of the tree.  Try setting TOP."        return 1    fi}

mma

mma 相当于mm执行相应目录下的all_modules参数

function mma(){  # 获取代码根目录  local T=$(gettop)  local DRV=$(getdriver $T)  # 如果在代码根目录下执行mm指令,转换为直接运行make指令  if [ -f build/core/envsetup.mk -a -f Makefile ]; then    $DRV make $@  else    # 取得的代码根目录路径为空,那就报错退出    if [ ! "$T" ]; then      echo "Couldn't locate the top of the tree.  Try setting TOP."      return 1    fi    # 将当前目录转换为相对于根目录的相对路径    local MY_PWD=`PWD= /bin/pwd|sed 's:'$T'/::'`    # 基于路径设置MODULES-IN-PATH参数    local MODULES_IN_PATHS=MODULES-IN-$MY_PWD    # Convert "/" to "-".    MODULES_IN_PATHS=${MODULES_IN_PATHS//\//-}    # 编译所有模块    $DRV make -C $T -f build/core/main.mk $@ $MODULES_IN_PATHS  fi}

mmma

mmma 相当于mmm执行相应目录下的all_modules参数

function mmma(){  # 获取代码根目录  local T=$(gettop)  local DRV=$(getdriver $T)  # 在代码根目录存在的情况下,开始进行命令参数解析和编译  if [ "$T" ]; then    # 提取编译命令中的选项参数,即符号"-"后面接的参数    local DASH_ARGS=$(echo "$@" | awk -v RS=" " -v ORS=" " '/^-.*$/')    # 提取编译命令中目录和模块部分,即符号的"-"前的参数部分    # 例如 "mmm dir1 dir2 dir3 dir4/:module1,module2,module3,module4"    local DIRS=$(echo "$@" | awk -v RS=" " -v ORS=" " '/^[^-].*$/')    # 获取当前目录    local MY_PWD=`PWD= /bin/pwd`    # 将当前目录到代码根目录的相对路径保存到MY_PWD中    if [ "$MY_PWD" = "$T" ]; then      MY_PWD=    else      MY_PWD=`echo $MY_PWD|sed 's:'$T'/::'`    fi    local DIR=    local MODULES_IN_PATHS=    local ARGS=    for DIR in $DIRS ; do      # 检查DIR指定的参数是否为目录      if [ -d $DIR ]; then        # Remove the leading ./ and trailing / if any exists.        DIR=${DIR#./}        DIR=${DIR%/}        # 生成DIR相对于代码根目录的完整相对路径        if [ "$MY_PWD" != "" ]; then          DIR=$MY_PWD/$DIR        fi        MODULES_IN_PATHS="$MODULES_IN_PATHS MODULES-IN-$DIR"      else        # 如果DIR对应的参数是showcommands, snod, dist等,将其转换为参数ARGS        case $DIR in          showcommands | snod | dist | *=*) ARGS="$ARGS $DIR";;          *) echo "Couldn't find directory $DIR"; return 1;;        esac      fi    done    # Convert "/" to "-".    MODULES_IN_PATHS=${MODULES_IN_PATHS//\//-}    # 将mmm命令“-”后的选项和模块参数以及其他参数传递给主makefile进行编译    $DRV make -C $T -f build/core/main.mk $DASH_ARGS $ARGS $MODULES_IN_PATHS  else    # 获取代码根目录是T返回空,说明没有找到代码根目录,有什么办法?那就显示错误信息并退出吧        echo "Couldn't locate the top of the tree.  Try setting TOP."    return 1  fi}

get_make_command

get_make_commandmake命令转换为command make调用。
执行souce build/envsetup.sh后环境中有两个make

  • envsetup.sh脚本中定义的make
  • make系统的可执行文件make
    命令行运行make时,先执行shell环境中内置的make函数,然后make函数内部通过command命令调用可执行文件make
function get_make_command(){  echo command make}

make

make

function make(){    # 获取开始时间    local start_time=$(date +"%s")    #     # 命令行运行`make`时,先执行`shell`环境中内置的`make`函数(本函数),    # 然后通过函数的`command`命令调用可执行文件`make`进行真正的make操作    # 这里相当于是重新定义shell命令行的函数将命令`make`拦截了    #    # 将make xxx转换为命令command make xxx执行    $(get_make_command) "$@"    # 获取可执行文件make的返回值    local ret=$?    # 获取执行make命令完成的时间    local end_time=$(date +"%s")    # 计算时间差,并转换为HH:MM:SS的格式    local tdiff=$(($end_time-$start_time))    local hours=$(($tdiff / 3600 ))    local mins=$((($tdiff % 3600) / 60))    local secs=$(($tdiff % 60))    # 设置各种字体颜色    local ncolors=$(tput colors 2>/dev/null)    if [ -n "$ncolors" ] && [ $ncolors -ge 8 ]; then        color_failed=$'\E'"[0;31m"        color_success=$'\E'"[0;32m"        color_reset=$'\E'"[00m"    else        color_failed=""        color_success=""        color_reset=""    fi    echo    # 显示编译成功信息    if [ $ret -eq 0 ] ; then        echo -n "${color_success}#### make completed successfully "    # 显示编译失败信息    else        echo -n "${color_failed}#### make failed to build some targets "    fi    # 显示make操作执行的时间    if [ $hours -gt 0 ] ; then        printf "(%02g:%02g:%02g (hh:mm:ss))" $hours $mins $secs    elif [ $mins -gt 0 ] ; then        printf "(%02g:%02g (mm:ss))" $mins $secs    elif [ $secs -gt 0 ] ; then        printf "(%s seconds)" $secs    fi    echo " ####${color_reset}"    echo    return $ret}

tapas

tapas 以交互方式设置单个app编译的build环境变量,调用格式为:

tapas [ ...] [arm|x86|mips] [eng|userdebug|user]

话说tapas长这样,其实做底层的我一次都没有用过这个命令。

# Configures the build to build unbundled apps.# Run tapas with one or more app names (from LOCAL_PACKAGE_NAME)function tapas(){    # 获取参数里的arch相关变量(arm|x86|mips|armv5|arm64|x86_64|mips64)    local arch="$(echo $* | xargs -n 1 echo | \grep -E '^(arm|x86|mips|armv5|arm64|x86_64|mips64)$' | xargs)"    # 获取参数里variant相关变量(user|userdebug|eng)    local variant="$(echo $* | xargs -n 1 echo | \grep -E '^(user|userdebug|eng)$' | xargs)"    # 获取参数里分辨率DPI(`Dot Per Inch`,每英寸像素数)相关的参数    local density="$(echo $* | xargs -n 1 echo | \grep -E '^(ldpi|mdpi|tvdpi|hdpi|xhdpi|xxhdpi|xxxhdpi|alldpi)$' | xargs)"    # 使用`grep -v`过滤参数中arch, variant,density相关参数,然后将剩余参数指定为apps    local apps="$(echo $* | xargs -n 1 echo | \grep -E -v '^(user|userdebug|eng|arm|x86|mips|armv5|arm64|x86_64|mips64|ldpi|mdpi|tvdpi|hdpi|xhdpi|xxhdpi|xxxhdpi|alldpi)$' | xargs)"    #    # 检查命令行是否设置了多个arch, variant和density参数,显然这类相关参数变量只能有唯一值    #    # 检查是否设置了多个arch参数,显示错误消息    if [ $(echo $arch | wc -w) -gt 1 ]; then        echo "tapas: Error: Multiple build archs supplied: $arch"        return    fi    # 检查是否设置了多个variant参数,显示错误消息    if [ $(echo $variant | wc -w) -gt 1 ]; then        echo "tapas: Error: Multiple build variants supplied: $variant"        return    fi    # 检查是否设置了多个density参数,显示错误消息    if [ $(echo $density | wc -w) -gt 1 ]; then        echo "tapas: Error: Multiple densities supplied: $density"        return    fi    # 根据arch设置针对相应平台的默认的product    local product=aosp_arm    case $arch in      x86)    product=aosp_x86;;      mips)   product=aosp_mips;;      armv5)  product=generic_armv5;;      arm64)  product=aosp_arm64;;      x86_64) product=aosp_x86_64;;      mips64)  product=aosp_mips64;;    esac    # 没有指定variant,则默认设置为eng    if [ -z "$variant" ]; then        variant=eng    fi    # 没有指定app,则默认为all    if [ -z "$apps" ]; then        apps=all    fi    # 没有指定density参数,则默认设置为alldpi    if [ -z "$density" ]; then        density=alldpi    fi    # 根据以上的各参数设置编译环境变量TARGET_PRODUCT, TARGET_BUILD_{VARIANT, DENSITY, TYPE, APPS}    export TARGET_PRODUCT=$product    export TARGET_BUILD_VARIANT=$variant    export TARGET_BUILD_DENSITY=$density    export TARGET_BUILD_TYPE=release    export TARGET_BUILD_APPS=$apps    # 设置编译环境相关变量var_cache_xxx和abs_var_cache_xxx    build_build_var_cache    # 设置其他环境变量,如PROMPT_COMMAND,编译toolchain和tools相关的路径等    set_stuff_for_environment    # 显示当前选择的编译配置    printconfig    # 清空环境变量,不懂为什么刚设置了这些变量,这里又要取消?    destroy_build_var_cache}

1.3 代码搜索类

代码搜索类函数定义了各种xxxgrep函数,并导入到当前环境中,使其可以直接在命令行调用。
这些函数先用find命令在除.repo/.git/out的目录外搜索相应后名称的目录或文件,然后基于搜索结果调用grep进行模式查找。

  • sgrep,基于(c|h|cc|cpp|S|java|xml|sh|mk|aidl|vts)文件查找
  • ggrep,基于(.gradle)的文件查找
  • jgrep,基于(.java)文件查找
  • cgrep,基于(c|cc|cpp|h|hpp)文件查找
  • resgrep,基于res目录下(xml)文件查找
  • mangrep,基于AndroidManifest.xml文件查找
  • sepgrep,基于sepolicy目录下查找
  • rcgrep,基于*.rc*文件查找
  • mgrep,基于(Makefile|Makefile\..*|.*\.make|.*\.mak|.*\.mk)的Makefile文件查找
  • treegrep,基于代码的文件(c|h|cpp|S|java|xml)进行查找
# 针对MAC和非MAC环境定义sgrep函数case `uname -s` in    Darwin)        function sgrep()        {            # 排除 .repo, .git目录            # 并对后缀(c|h|cc|cpp|S|java|xml|sh|mk|aidl|vts)的文件执行grep模式搜索            find -E . -name .repo -prune -o -name .git -prune -o  -type f -iregex '.*\.(c|h|cc|cpp|S|java|xml|sh|mk|aidl|vts)' \                -exec grep --color -n "$@" {} +        }        ;;    *)        function sgrep()        {            find . -name .repo -prune -o -name .git -prune -o  -type f -iregex '.*\.\(c\|h\|cc\|cpp\|S\|java\|xml\|sh\|mk\|aidl\|vts\)' \                -exec grep --color -n "$@" {} +        }        ;;esacfunction ggrep(){    # 排除 .repo, .git, out目录    # 并对后缀.gradle的文件执行grep模式搜索    find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.gradle" \        -exec grep --color -n "$@" {} +}function jgrep(){    # 排除 .repo, .git, out目录    # 并对后缀为.java的文件执行grep模式搜索    find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.java" \        -exec grep --color -n "$@" {} +}function cgrep(){    # 排除 .repo, .git, out目录    # 并对后缀为(.c|.cc|.cpp|.h|.hpp)的文件执行grep模式搜索    find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f \( -name '*.c' -o -name '*.cc' -o -name '*.cpp' -o -name '*.h' -o -name '*.hpp' \) \        -exec grep --color -n "$@" {} +}function resgrep(){    # 排除 .repo, .git, out目录    # 并对名为res目录下的*.xml文件执行grep模式搜索    for dir in `find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -name res -type d`; do        find $dir -type f -name '*\.xml' -exec grep --color -n "$@" {} +    done}function mangrep(){    # 排除 .repo, .git, out目录    # 并对名为AndroidManifest.xml的文件执行grep模式搜索    find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -type f -name 'AndroidManifest.xml' \        -exec grep --color -n "$@" {} +}function sepgrep(){    # 排除 .repo, .git, out目录    # 并对名为sepolicy目录中的文件执行grep模式搜索    find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -name sepolicy -type d \        -exec grep --color -n -r --exclude-dir=\.git "$@" {} +}function rcgrep(){    # 排除 .repo, .git, out目录    # 并对名为*.rc*的文件执行grep模式搜索    find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.rc*" \        -exec grep --color -n "$@" {} +}case `uname -s` in    Darwin)        function mgrep()        {            # 排除 .repo, .git, ./out目录            # 并对Makefile,后缀为(.make|.mak|.mk)的文件或Makefile目录下的文件执行grep模式搜索            find -E . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -type f -iregex '.*/(Makefile|Makefile\..*|.*\.make|.*\.mak|.*\.mk)' \                -exec grep --color -n "$@" {} +        }        function treegrep()        {            # 排除 .repo, .git目录            # 并对后缀为(.c|.h|.cpp|.S|.java|.xml)的文件执行grep模式搜索            find -E . -name .repo -prune -o -name .git -prune -o -type f -iregex '.*\.(c|h|cpp|S|java|xml)' \                -exec grep --color -n -i "$@" {} +        }        ;;    *)        function mgrep()        {            find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -regextype posix-egrep -iregex '(.*\/Makefile|.*\/Makefile\..*|.*\.make|.*\.mak|.*\.mk)' -type f \                -exec grep --color -n "$@" {} +        }        function treegrep()        {            find . -name .repo -prune -o -name .git -prune -o -regextype posix-egrep -iregex '.*\.(c|h|cpp|S|java|xml)' -type f \                -exec grep --color -n -i "$@" {} +        }        ;;esac

1.4 adb调试类

qpid

qpid

# simplified version of ps; output in the form#  function qpid() {    local prepend=''    local append=''    if [ "$1" = "--exact" ]; then        prepend=' '        append='$'        shift    elif [ "$1" = "--help" -o "$1" = "-h" ]; then        echo "usage: qpid [[--exact] "        return 255    fi    local EXE="$1"    if [ "$EXE" ] ; then        qpid | \grep "$prepend$EXE$append"    else        adb shell ps \            | tr -d '\r' \            | sed -e 1d -e 's/^[^ ]* *\([0-9]*\).* \([^ ]*\)$/\1 \2/'    fi}

pid

pid

function pid(){    local prepend=''    local append=''    if [ "$1" = "--exact" ]; then        prepend=' '        append='$'        shift    fi    local EXE="$1"    if [ "$EXE" ] ; then        local PID=`adb shell ps \            | tr -d '\r' \            | \grep "$prepend$EXE$append" \            | sed -e 's/^[^ ]* *\([0-9]*\).*$/\1/'`        echo "$PID"    else        echo "usage: pid [--exact] "        return 255    fi}

coredump_setup

coredump_enable

core

systemstack

stacks

is64bit

tracedmdump

runhat

getbugreports

getsdcardpath

getscreenshotpath

getlastscreenshot

startviewserver

stopviewserver

isviewserverstarted

key_home

key_back

key_menu

smoketest

runtest

provision

provision

function provision(){    # 如果ANDROID_PRODUCT_OUT没有设置,则退出函数    if [ ! "$ANDROID_PRODUCT_OUT" ]; then        echo "Couldn't locate output files.  Try running 'lunch' first." >&2        return 1    fi    # 如果$ANDROID_PRODUCT_OUT/provision-device文件不存在,则退出函数    if [ ! -e "$ANDROID_PRODUCT_OUT/provision-device" ]; then        echo "There is no provisioning script for the device." >&2        return 1    fi    # Check if user really wants to do this.    # 如果第一个参数是--no-confirmation,表明不需要交互执行脚本    if [ "$1" = "--no-confirmation" ]; then        shift 1    else        # 交互执行脚本,弹出确认信息        echo "This action will reflash your device."        echo ""        echo "ALL DATA ON THE DEVICE WILL BE IRREVOCABLY ERASED."        echo ""        echo -n "Are you sure you want to do this (yes/no)? "        read        # 用户选择no,则退出程序        if [[ "${REPLY}" != "yes" ]] ; then            echo "Not taking any action. Exiting." >&2            return 1        fi    fi    # 开始执行provision-device脚本    "$ANDROID_PRODUCT_OUT/provision-device" "$@"}

2. 生成编译配置列表

envsetup.sh脚本中除去函数的定义外,剩下的就是自身的逻辑应用,为了方便起见,以下是删除函数定义后的脚本内容:

...# 编译的可能选项后缀VARIANT_CHOICES=(user userdebug eng)...# Clear this variable.  It will be built up again when the vendorsetup.sh# files are included at the end of this file.# 清空选项菜单unset LUNCH_MENU_CHOICES...# add the default one here# 添加默认的菜单编译选项,包括aosp_{arm, arm64, mips, mips64, x86, x86_64}-eng等选项add_lunch_combo aosp_arm-engadd_lunch_combo aosp_arm64-engadd_lunch_combo aosp_mips-engadd_lunch_combo aosp_mips64-engadd_lunch_combo aosp_x86-engadd_lunch_combo aosp_x86_64-eng...# 设置_lunch作为lunch命令的补全函数complete -F _lunch lunch...# 检查当前执行的shell环境是否为bash,如果不是,输出警告信息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"            ;;    esacfi# Execute the contents of any vendorsetup.sh files we can find.# 依次查找{device, vendor, product}目录下的vendorsetup.sh文件for f in `test -d device && find -L device -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort` \         `test -d vendor && find -L vendor -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort` \         `test -d product && find -L product -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort`do    # 对查找到的vendorsetup.sh文件执行"."操作,将其内容导入到当前环境中来    # 一个约定俗成是vendorsetup.sh里面调用add_lunch_combo添加编译选项,例如:    # 文件device\asus\fugu\vendorsetup.sh的内容为:    #     add_lunch_combo full_fugu-userdebug    #     add_lunch_combo aosp_fugu-userdebug    echo "including $f"    . $fdone# 清除变量funset f# 调用addcompletions函数设置基于sdk/bash_completion的补全功能addcompletions

简单来说,envsetup.sh搜集各个vendor定义的编译选项,存放到lunch menu中,供下一步的lunch操作使用。

执行lunch操作是,lunch函数解析传入的编译选项,如full_fugu-userdebug,更新相应的Android编译环境变量。

后记

对这样的脚本文件进行逐行分析真是花费时间,前后用掉了几个下午,写出来的内容也啰嗦冗长,也是够佩服自己的耐心,差点就忍不住了。
好吧,就当是对envsetup.sh的75个函数进行完全解读,留作字典来查询了。

如果只想知道envsetup.sh的大概功能,浏览下主要关心的函数就够了,没有必要对每一行代码都细致入微的分析,这样效率太低。
envsetup.sh的主要内容包括:
- m/mm/mmm操作
- lunch操作的过程
- 各个自定义的脚本文件vendorsetup.sh是如何起作用的就够了

除此之外的其它函数,如各种搜索函数,adb调试函数等,可能永远都不会用到。

更多相关文章

  1. Android(安卓)Activity之间相互调用与传递参数的原理与用法分析
  2. Android(安卓)JNI入门第二篇——Java参数类型与本地参数类型对照
  3. Android应用程序:(jni方式)控制LED/GPIO
  4. Android(安卓)局部刷新
  5. Android--使用开源vitamio做万能视频播放器
  6. [置顶] 《老罗的Android之旅》导读PPT
  7. Android(安卓)cavas将横向字体转换为竖直rotate(),drawText(),se
  8. Android(安卓)Studio 使用本地Maven仓库
  9. android视图学习---Android深入浅出之Surface[1]

随机推荐

  1. Android环境变量配置
  2. android layout,xml属性介绍
  3. Android(安卓)RelativeLayout 属性
  4. 《Android移动应用开发从入门到精通》下
  5. android线性布局控件之间的距离设置
  6. Android实现全屏显示的方法
  7. 高级Android工程师进阶系列文章汇总
  8. Android: Java的ClassLoader和Android的C
  9. Android(安卓)shape使用
  10. Android(安卓)Studio 简单介绍和使用问题