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

(一)开发环境

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 "$@"; 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 "$@") "$@"
}

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

function _wrap_build()
{
    local start_time=$(date +"%s")
    "$@"
    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 "$@" {} +
}

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 "$@" {} +
}

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 "$@" {} +
}

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 "$@" {} +
    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 "$@" {} +
}

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 "$@" {} +
}

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 "$@" {} +
}

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 "$@" {} +
        }

        ;;
    *)
        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 "$@" {} +
        }
        ;;
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 "$@" {} +
        }

        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 "$@" {} +
        }

        ;;
    *)
        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 "$@" {} +
        }

        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

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选项添加,第一步的环境变量即可设置完成。

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值