欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android编译流程(一):envsetup.sh文件解析

程序员文章站 2022-07-02 15:26:20
...

(一)开发环境

CPU:Freescale I.MX 8M Mini
Android平台版本:Android 9.0.0(Pie)
kernel版本:4.14.98
uboot版本:2018.03
Android repo源码版本:imx-p9.0.0_2.0.0-ga

(二)概述

当需要编译Android源码时,第一步基本上都是:

source build/envsetup.sh

这一步的目的是将build/envsetup.sh这个文件中的环境变量和函数导出到shell终端环境,在这个文件中,绝大部分的代码都是关于函数的,使用以下命令:

sed -n "/^[[:blank:]]*function /s/function \([a-z_]\w*\).*/\1/p" < build/envsetup.sh | wc -l
75

能得到函数个数为75个。逐个函数分析太费时费力了,本文挑部分个人觉得重要的进行讲解。

(三)函数

在envsetup.sh中,最值得关注的几个函数分别是lunch、mm、mmm,这些都是在编译调试的时候经常用到的,但是除此之外的一些函数在我们调试过程中也能够提供很大的帮助,以下是一些介绍。

hmm

hmm函数是打印帮助信息的函数,函数定义如下:

function hmm() {
cat <<EOF

Run "m help" for help with the build system itself.

Invoke ". build/envsetup.sh" from your shell to add the following functions to your environment:
- lunch:     lunch <product_name>-<build_variant>
             Selects <product_name> as the product to build, and <build_variant> as the variant to
             build, and stores those selections in the environment to be read by subsequent
             invocations of 'm' etc.
- tapas:     tapas [<App1> <App2> ...] [arm|x86|mips|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
    local T=$(gettop)
    local A=""
    local i
    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命令后,除了会打印部分函数的帮助信息外,还会打印envsetup.sh文件里面包括的函数:

Look at the source to view more functions. The complete list is:
mgrep sgrep treegrep _lunch _wrap_build add_lunch_combo addcompletions atest build_build_var_cache cgrep check_product check_variant choosecombo chooseproduct choosetype choosevariant core coredump_enable coredump_setup cproj croot destroy_build_var_cache findmakefile get_abs_build_var get_build_var get_make_command getbugreports getlastscreenshot getprebuilt getscreenshotpath getsdcardpath gettargetarch gettop ggrep godir hmm is isviewserverstarted jgrep key_back key_home key_menu lunch m make mangrep mm mma mmm mmma pez pid print_lunch_menu printconfig provision qpid rcgrep resgrep runhat runtest sepgrep set_sequence_number set_stuff_for_environment setpaths settitle smoketest stacks startviewserver stopviewserver systemstack tapas tracedmdump

gettop

获取源码根目录,函数定义如下:

function gettop
{
    local TOPFILE=build/make/core/envsetup.mk
    if [ -n "$TOP" -a -f "$TOP/$TOPFILE" ] ; then
        # The following circumlocution ensures we remove symlinks from TOP.
        (cd $TOP; PWD= /bin/pwd)
    else
        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
            local HERE=$PWD
            local T=
            while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do
                \cd ..
                T=`PWD= /bin/pwd -P`
            done
            \cd $HERE
            if [ -f "$T/$TOPFILE" ]; then
                echo $T
            fi
        fi
    fi
}

printconfig

打印当前编译配置,函数定义如下:

function printconfig()
{
    local 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实现打印,在终端输入printconfig,会打印出:

============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=9
TARGET_PRODUCT=evk_8mm
TARGET_BUILD_VARIANT=eng
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm64
TARGET_ARCH_VARIANT=armv8-a
TARGET_CPU_VARIANT=cortex-a53
TARGET_2ND_ARCH=arm
TARGET_2ND_ARCH_VARIANT=armv7-a-neon
TARGET_2ND_CPU_VARIANT=cortex-a9
HOST_ARCH=x86_64
HOST_2ND_ARCH=x86
HOST_OS=linux
HOST_OS_EXTRA=Linux-4.15.0-101-generic-x86_64-Ubuntu-16.04.6-LTS
HOST_CROSS_OS=windows
HOST_CROSS_ARCH=x86
HOST_CROSS_2ND_ARCH=x86_64
HOST_BUILD_TYPE=release
BUILD_ID=2.0.0-ga-rc4
OUT_DIR=out
============================================

该函数能非常方便地查看当前配置。

findmakefile

查找当前模块的Android.mk文件,函数定义如下:

function findmakefile()
{
    local TOPFILE=build/make/core/envsetup.mk
    local HERE=$PWD
    local T=
    while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do
        T=`PWD= /bin/pwd`
        if [ -f "$T/Android.mk" -o -f "$T/Android.bp" ]; then
            echo $T/Android.mk
            \cd $HERE
            return
        fi
        \cd ..
    done
    \cd $HERE
}

对于模块化编译的情况,修改文件后不知道需要在什么路径编译时,可以用这个函数迅速查找到Android.mk文件。

get_make_command

source build/envsetup.sh之后make有两种情况:一种是系统的可执行文件,还有一种是envsetup.sh定义的make。get_make_command可以返回make命令执行的脚本,函数定义如下:

function get_make_command()
{
    # If we're in the top of an Android tree, use soong_ui.bash instead of make
    if [ -f build/soong/soong_ui.bash ]; then
        # Always use the real make if -C is passed in
        for arg in "[email protected]"; do
            if [[ $arg == -C* ]]; then
                echo command make
                return
            fi
        done
        echo build/soong/soong_ui.bash --make-mode
    else
        echo command make
    fi
}

在终端输入命令得:

$ get_make_command
$ build/soong/soong_ui.bash --make-mode

make

在source build/envsetup.sh后,我们使用make编译实际上是调用到了envsetup.sh文件中定义的make函数了,函数定义如下:

function make()
{
    _wrap_build $(get_make_command "[email protected]") "[email protected]"
}

因为是调用了_wrap_build函数,_wrap_build函数的定义如下:

function _wrap_build()
{
    local start_time=$(date +"%s")
    "[email protected]"
    local ret=$?
    local end_time=$(date +"%s")
    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}#### build completed successfully "
    else
        echo -n "${color_failed}#### failed to build some targets "
    fi
    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
}

所以编译时会打印时间戳,以及成功还是失败的提示。

xxgrep

xxgrep是泛指,代指几种同类型函数,这类函数一般用于在特定文件中查找内容,详情如下:

ggrep

在当前目录以及子目录下所有.gradle文件中查找内容,函数定义如下:

function ggrep()
{
    find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.gradle" \
        -exec grep --color -n "[email protected]" {} +
}

jgrep

在当前目录以及子目录下所有.java文件中查找内容,函数定义如下:

function jgrep()
{
    find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.java" \
        -exec grep --color -n "[email protected]" {} +
}

cgrep

在当前目录以及子目录下所有.c/.cc/.cpp/.h/.hpp文件中查找内容,函数定义如下:

function cgrep()
{
    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 "[email protected]" {} +
}

resgrep

在当前目录以及子目录下所有res/目录下的所有.xml文件中查找内容,函数定义如下:

function resgrep()
{
    local dir
    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 "[email protected]" {} +
    done
}

mangrep

在当前目录以及子目录下所有AndroidManifest.xml文件中查找内容,函数定义如下:

function mangrep()
{
    find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -type f -name 'AndroidManifest.xml' \
        -exec grep --color -n "[email protected]" {} +
}

sepgrep

在当前目录以及子目录下对名为sepolicy目录中的文件查找内容,函数定义如下:

function sepgrep()
{
    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 "[email protected]" {} +
}

rcgrep

在当前目录以及子目录下所有.rc文件中查找内容,函数定义如下:

function rcgrep()
{
    find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.rc*" \
        -exec grep --color -n "[email protected]" {} +
}

sgrep

在所有资源文件中查找内容,函数定义如下:

case `uname -s` in
    Darwin)
        function sgrep()
        {
            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 "[email protected]" {} +
        }

        ;;
    *)
        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 "[email protected]" {} +
        }
        ;;
esac

mgrep treegrep

mgrep:在当前目录以及子目录下所有makefile文件(包括Makefile、.mk文件等)中查找内容
treegrep:在当前目录以及子目录下所有.(c|h|cpp|S|java|xml)文件中查找内容
函数定义如下:

case `uname -s` in
    Darwin)
        function mgrep()
        {
            find -E . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o \( -iregex '.*/(Makefile|Makefile\..*|.*\.make|.*\.mak|.*\.mk|.*\.bp)' -o -regex '(.*/)?soong/[^/]*.go' \) -type f \
                -exec grep --color -n "[email protected]" {} +
        }

        function treegrep()
        {
            find -E . -name .repo -prune -o -name .git -prune -o -type f -iregex '.*\.(c|h|cpp|S|java|xml)' \
                -exec grep --color -n -i "[email protected]" {} +
        }

        ;;
    *)
        function mgrep()
        {
            find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o \( -regextype posix-egrep -iregex '(.*\/Makefile|.*\/Makefile\..*|.*\.make|.*\.mak|.*\.mk|.*\.bp)' -o -regextype posix-extended -regex '(.*/)?soong/[^/]*.go' \) -type f \
                -exec grep --color -n "[email protected]" {} +
        }

        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 "[email protected]" {} +
        }

        ;;
esac

godir

在编译路径下搜索匹配的路径,并且根据选择跳转,如:

$ godir evb_8mm
   [1] ./device/fsl/imx8m/evb_8mm
   [2] ./device/fsl/imx8m/evb_8mm/bluetooth
   [3] ./device/fsl/imx8m/evb_8mm/overlay/frameworks/base/core/res/res/drawable-nodpi
   [4] ./device/fsl/imx8m/evb_8mm/overlay/frameworks/base/core/res/res/drawable-sw600dp-nodpi
   [5] ./device/fsl/imx8m/evb_8mm/overlay/frameworks/base/core/res/res/drawable-sw720dp-nodpi
   [6] ./device/fsl/imx8m/evb_8mm/overlay/frameworks/base/core/res/res/values
   [7] ./device/fsl/imx8m/evb_8mm/overlay/frameworks/base/core/res/res/xml
   [8] ./device/fsl/imx8m/evb_8mm/overlay/frameworks/base/packages/SettingsProvider/res/values
   [9] ./device/fsl/imx8m/evb_8mm/overlay/frameworks/base/packages/SystemUI/res/values
  [10] ./device/fsl/imx8m/evb_8mm/overlay/packages/apps/Settings/res/values
  [11] ./device/fsl/imx8m/evb_8mm/seccomp
  [12] ./device/fsl/imx8m/evb_8mm/sepolicy
  [13] ./device/fsl/imx8m/evb_8mm/sepolicy_drm
  [14] ./hardware/broadcom/libbt/conf/fsl/evb_8mm
  [15] ./hardware/broadcom/libbt/include

Select one: 1

如上,选择1后则会跳转到./device/fsl/imx8m/evb_8mm目录。函数定义如下:

function godir () {
    if [[ -z "$1" ]]; then
        echo "Usage: godir <regex>"
        return
    fi
    local T=$(gettop)
    local FILELIST
    if [ ! "$OUT_DIR" = "" ]; then
        mkdir -p $OUT_DIR
        FILELIST=$OUT_DIR/filelist
    else
        FILELIST=$T/filelist
    fi
    if [[ ! -f $FILELIST ]]; then
        echo -n "Creating index..."
        (\cd $T; find . -wholename ./out -prune -o -wholename ./.repo -prune -o -type f > $FILELIST)
        echo " Done"
        echo ""
    fi
    local lines
    lines=($(\grep "$1" $FILELIST | sed -e 's/\/[^/]*$//' | sort | uniq))
    if [[ ${#lines[@]} = 0 ]]; then
        echo "Not found"
        return
    fi
    local pathname
    local choice
    if [[ ${#lines[@]} > 1 ]]; then
        while [[ -z "$pathname" ]]; do
            local index=1
            local line
            for line in ${lines[@]}; do
                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=${lines[0]}
    fi
    \cd $T/$pathname
}

print_lunch_menu

读取LUNCH_MENU_CHOICES列表,打印可执行lunch的选项,函数定义如下:

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
    for choice in ${LUNCH_MENU_CHOICES[@]}
    do
        echo "     $i. $choice"
        i=$(($i+1))
    done

    echo
}

add_lunch_combo

将提供的编译选项参数添加到LUNCH_MENU_CHOICES列表中,函数定义如下:

unset 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)
}

lunch

根据输入的参数设置环境变量,函数定义如下:

function lunch()
{
    local answer

    if [ "$1" ] ; then
        answer=$1
    else
        print_lunch_menu
        echo -n "Which would you like? [aosp_arm-eng] "
        read answer
    fi

    local selection=

    if [ -z "$answer" ]
    then
        selection=aosp_arm-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
    else
        selection=$answer
    fi

    export TARGET_BUILD_APPS=

    local product variant_and_version variant version

    product=${selection%%-*} # Trim everything after first dash
    variant_and_version=${selection#*-} # Trim everything up to first dash
    if [ "$variant_and_version" != "$selection" ]; then
        variant=${variant_and_version%%-*}
        if [ "$variant" != "$variant_and_version" ]; then
            version=${variant_and_version#*-}
        fi
    fi

    if [ -z "$product" ]
    then
        echo
        echo "Invalid lunch combo: $selection"
        return 1
    fi

    TARGET_PRODUCT=$product \
    TARGET_BUILD_VARIANT=$variant \
    TARGET_PLATFORM_VERSION=$version \
    build_build_var_cache
    if [ $? -ne 0 ]
    then
        return 1
    fi

    export TARGET_PRODUCT=$(get_build_var TARGET_PRODUCT)
    export TARGET_BUILD_VARIANT=$(get_build_var TARGET_BUILD_VARIANT)
    if [ -n "$version" ]; then
      export TARGET_PLATFORM_VERSION=$(get_build_var TARGET_PLATFORM_VERSION)
    else
      unset TARGET_PLATFORM_VERSION
    fi
    export TARGET_BUILD_TYPE=release

    echo

    set_stuff_for_environment
    printconfig
    destroy_build_var_cache
}

m mm mmm mma mmma

这几个函数实际上都是执行make操作,区别如下:
m:不管当前路径如何,编译所有的模块,相当于在根目录下执行make操作
mm:查找当前目录下的Android.mk文件,若找到,编译当前目录下的所有模块
mmm:需要指定路径,并且根据指定的路径下查找Android.mk文件,找到后编译该路径下所有模块
mma:与mm类似,但会将有依赖的模块一起编译
mmma:与mmm类似,但会将有依赖的模块一起编译

(四)非函数部分

将函数部分去除,可以得到以下很少量的非函数部分如下:

...
VARIANT_CHOICES=(user userdebug eng)
...

这部分是设置VARIANT_CHOICES变量为user userdebug和eng三种。

...
# add the default one here
add_lunch_combo aosp_arm-eng
add_lunch_combo aosp_arm64-eng
add_lunch_combo aosp_mips-eng
add_lunch_combo aosp_mips64-eng
add_lunch_combo aosp_x86-eng
add_lunch_combo aosp_x86_64-eng
...

添加默认lunch选项。

...
complete -F _lunch lunch
...

将lunch函数命令关联_lunch函数,_lunch为lunch的补全函数,也就是说当关联之后,在终端输入lunch命令后,按tab键可以进行参数的补全。示例如下:

$ lunch
aiy_8mq-eng                         aosp_cf_x86_auto-userdebug          aosp_x86-eng                        evk_6sl-eng                         m_e_arm-userdebug                   sabreauto_6sx-userdebug
aiy_8mq-userdebug                   aosp_cf_x86_phone-userdebug         aosp_x86_64-eng                     evk_6sl-userdebug                   m_e_mips-userdebug                  sabresd_6dq-eng
aosp_arm-eng                        aosp_cf_x86_tablet-userdebug        cf_x86_64_phone-userdebug           evk_7ulp-eng                        m_e_mips64-eng                      sabresd_6dq-userdebug
aosp_arm64-eng                      aosp_cf_x86_tablet_3g-userdebug     cf_x86_64_tablet-userdebug          evk_7ulp-userdebug                  mek_8q-eng                          sabresd_6dq_car-eng
aosp_blueline-userdebug             aosp_cf_x86_tv-userdebug            cf_x86_64_tablet_3g-userdebug       evk_8mm-eng                         mek_8q-userdebug                    sabresd_6dq_car-userdebug
aosp_car_arm-userdebug              aosp_cf_x86_wear-userdebug          cf_x86_64_tv-userdebug              evk_8mm-userdebug                   mek_8q_car-eng                      sabresd_6sx-eng
aosp_car_arm64-userdebug            aosp_crosshatch-userdebug           cf_x86_64_wear-userdebug            evk_8mm_drm-eng                     mek_8q_car-userdebug                sabresd_6sx-userdebug
aosp_car_x86-userdebug              aosp_marlin-userdebug               cf_x86_auto-userdebug               evk_8mm_drm-userdebug               mek_8q_car2-eng                     sabresd_7d-eng
aosp_car_x86_64-userdebug           aosp_marlin_svelte-userdebug        cf_x86_phone-userdebug              evk_8mq-eng                         mek_8q_car2-userdebug               sabresd_7d-userdebug
aosp_cf_x86_64_auto-userdebug       aosp_mips-eng                       cf_x86_tablet-userdebug             evk_8mq-userdebug                   mini_emulator_arm64-userdebug       uml-userdebug
aosp_cf_x86_64_phone-userdebug      aosp_mips64-eng                     cf_x86_tablet_3g-userdebug          evk_8mq_drm-eng                     mini_emulator_x86-userdebug
aosp_cf_x86_64_tablet-userdebug     aosp_sailfish-userdebug             cf_x86_tv-userdebug                 evk_8mq_drm-userdebug               mini_emulator_x86_64-userdebug
aosp_cf_x86_64_tablet_3g-userdebug  aosp_taimen-userdebug               cf_x86_wear-userdebug               hikey-userdebug                     sabreauto_6q-eng
aosp_cf_x86_64_tv-userdebug         aosp_walleye-userdebug              evb_8mm-eng                         hikey64_only-userdebug              sabreauto_6q-userdebug
aosp_cf_x86_64_wear-userdebug       aosp_walleye_test-userdebug         evb_8mm-userdebug                   hikey960-userdebug                  sabreauto_6sx-eng
...
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"
            ;;
    esac
fi
...

检查当前执行的shell环境是不是bash,如果不是,会输出警告信息。

...
# Execute the contents of any vendorsetup.sh files we can find.
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
    echo "including $f"
    . $f
done
unset f
...

在device、vendor、product目录下查找vendorsetup.sh文件,并且执行这个文件。一般vendorsetup.sh文件中是使用add_lunch_combo函数将lunch选项加入列表。

...
addcompletions
...

调用addcompletions函数,基于sdk/bash_completion目录下的.bash文件内容添加补全规则。

(五)总结

source build/envsetup.sh的流程实际上是环境变量以及各种函数设置的流程,是为了下一步设置与厂商相关的变量以及编译做铺垫。这一步将公共部分剥离,并且指定了厂商环境的路径,这样做的话,如果需要添加厂商自己的环境,只需要在指定目录(device、product、vendor)下添加vendorsetup.sh文件,将lunch选项添加,第一步的环境变量即可设置完成。

相关标签: Android android