Android build中的envsetup.sh详解

本文详细介绍了Android编译系统中的envsetup.sh脚本,包括它在编译过程中的作用,如定义lunch、m、mm等命令,以及函数如validate_current_shell、source_vendorsetup、addcompletions的功能。此外,还分析了如croot、cproj、getprebuilt等辅助功能,以及编译配置和搜索功能的实现。
摘要由CSDN通过智能技术生成

源码基于:Android R

0. 前言

今天在编译项目的时候,想看看 envsetup.sh 中变化了些什么,才想起来编译专栏中好像没有详解该脚本,索性现在空余时间比较多,整理一下方便以后查看。

Android envsetup.sh 为编译前的准备工作,提供 lunch、m、mm等命令函数定义,是整个Android 编译系统的第一步。该文件一共1700 行左右,简单总结出两件事情:

  • 函数定义
  • 生成编译配置列表

本文会结合代码对 envsetup.sh 进行详细的剖析,由于函数比较多,本文会进行长期地补充和维护。

1. source envsetup.sh

当运行该脚本的时候终端提示:

including vendor/xxx/common/vendorsetup.sh
clang compdb: ln -s out/soong/development/ide/compdb/compile_commands.json $ANDROID_BUILD_TOP
including vendor/qqq/opensource/core-utils/vendorsetup.sh
including vendor/qqq/proprietary/common/vendorsetup.sh

那执行该脚本的时候到底做了些什么呢?

build/envsetup.sh

validate_current_shell
source_vendorsetup
addcompletions

执行该程序共运行三个函数,分别是 validate_current_shellsource_vendorsetupaddcompletions。下面来看下这三个函数做了些什么事情?

1.1 validate_current_shell

function validate_current_shell() {
    local current_sh="$(ps -o command -p $$)"
    case "$current_sh" in
        *bash*)
            function check_type() { type -t "$1"; }
            ;;
        *zsh*)
            function check_type() { type "$1"; }
            enable_zsh_completion ;;
        *)
            echo -e "WARNING: Only bash and zsh are supported.\nUse of other shell would lead to erroneous results."
            ;;
    esac
}

主要是测试下当前的shell 环境是否可用,对于 zsh 环境需要特殊处理,其他环境会其实warning。


1.2 source_vendorsetup

function source_vendorsetup() {
    unset VENDOR_PYTHONPATH
    allowed=
    for f in $(find -L device vendor product -maxdepth 4 -name 'allowed-vendorsetup_sh-files' 2>/dev/null | sort); do
        if [ -n "$allowed" ]; then
            echo "More than one 'allowed_vendorsetup_sh-files' file found, not including any vendorsetup.sh files:"
            echo "  $allowed"
            echo "  $f"
            return
        fi
        allowed="$f"
    done

    allowed_files=
    [ -n "$allowed" ] && allowed_files=$(cat "$allowed")
    for dir in device vendor product; do
        for f in $(test -d $dir && \
            find -L $dir -maxdepth 4 -name 'vendorsetup.sh' 2>/dev/null | sort); do

            if [[ -z "$allowed" || "$allowed_files" =~ $f ]]; then
                echo "including $f"; . "$f"
            else
                echo "ignoring $f, not in $allowed"
            fi
        done
    done
}
  • 首先,查找 device、vendor、product 目录下最大深度为4 的子目录中,是否存在 allowed-vendorsetup_sh-files 文件并排序,但这个文件只能1个,不然会报错退出;
  • 接着,在device、vendor、product 中同样查找最大深度为 4 的子目录中,是否存在 vendorsetup.sh,打印 includeing ... ,然后运行该 vendorsetup.sh 脚本,这些脚本大多是配置一些环境变量;

例如上面运行 envsetup.sh 时打印 including vendor/xxx/common/vendorsetup.sh,就是在 vendor/xxx/common 目录下找到了 vendorsetup.sh,运行的时候会配置一些环境变量,并打印:

clang compdb: ln -s out/soong/development/ide/compdb/compile_commands.json 


1.3 addcompletions

function addcompletions()
{
    local T dir f

    #避免不在 bash 或 zsh 的环境中运行
    #需要提前指定这两个环境变量,如果没有指定,该函数return
    if [ -z "$BASH_VERSION" -a -z "$ZSH_VERSION" ]; then
        return
    fi

    #避免运行在太久的版本中,这里是小于 3 的版本
    if [ -n "$BASH_VERSION" -a ${BASH_VERSINFO[0]} -lt 3 ]; then
        return
    fi

    local completion_files=(
      system/core/adb/adb.bash
      system/core/fastboot/fastboot.bash
      tools/asuite/asuite.sh
    )
    
    #确认上面几个脚本是否存在,通过should_add_completion确认该脚本是否在白名单中
    for f in ${completion_files[*]}; do
        if [ -f "$f" ] && should_add_completion "$f"; then
            . $f
        fi
    done

    if should_add_completion bit ; then
        complete -C "bit --tab" bit
    fi
    if [ -z "$ZSH_VERSION" ]; then
        # Doesn't work in zsh.
        complete -o nospace -F _croot croot
    fi
    complete -F _lunch lunch

    complete -F _complete_android_module_names dumpmod
    complete -F _complete_android_module_names pathmod
    complete -F _complete_android_module_names gomod
    complete -F _complete_android_module_names m
}

详细的 complete 命令可以另外查找,最后就是把一些命令进行补齐的设置。

2. 辅助函数

2.1 hmm

hmm 函数输出 envsetupsh.sh 的帮助说明,执行 build/envsetup.sh 后可以调用的操作总结:

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, or a subdirectory thereof.
- m:          Makes from the top of the tree.
- mm:         Builds and installs all of the modules in the current directory, and their
              dependencies.
- mmm:        Builds and installs all of the modules in the supplied directories, and their
              dependencies.
              To limit the modules being built use the syntax: mmm dir/:target1,target2.
- mma:        Same as 'mm'
- mmma:       Same as 'mmm'
- 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.
- gogrep:     Greps on all local Go 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 and *.bp files.
- owngrep:    Greps on all local OWNERS files.
- sepgrep:    Greps on all local sepolicy files.
- sgrep:      Greps on all local source files.
- godir:      Go to the directory containing a file.
- allmod:     List all modules. or List all modules inside certain directory.
- gomod:      Go to the directory containing a module.
- dumpmod:    Get all info of specific modules from \$ANDROID_PRODUCT_OUT/module-info.json
- pathmod:    Get the directory containing a module.
- refreshmod: Refresh list of modules for allmod/gomod.
- ninja-build: Bypass ninja generate procedure, directly use ninja to build a target or module, also build droid is support too by no param given.
- ninja-query: Query input and output of a ninja target, this is the most powerful tool when digging the compile steps.
- ninja-commands: Print all build commands, like a --just-print version of ninja-build, also known as the --dry-run purpose
- mod:        Analyze $OUT/module-info.json with parameters, see mod -h for help

Environment options:
- SANITIZE_HOST: Set to 'address' to use ASAN for all host modules.
- ANDROID_QUIET_BUILD: set to 'true' to display only the essential messages.

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
}

最后还列出了完整的函数名,通过:

sed -n "/^[[:blank:]]*function /s/function \([a-z_]*\).*/\1/p"

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

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

2.2 gettop

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

function gettop
{
    local TOPFILE=build/make/core/envsetup.mk

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

2.3 croot

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

当然croot 之后可以跟一个参数标记切到根目录之后的下一级目录,例如 croot device 命令。

详细可以查看代码:

function croot()
{
    local T=$(gettop)
    if [ "$T" ]; then
        if [ "$1" ]; then
            \cd $(gettop)/$1
        else
            \cd $(gettop)
        fi
    else
        echo "Couldn't locate the top of the tree.  Try setting TOP."
    fi
}

2.4 cproj

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

function cproj()
{
    local TOPFILE=build/make/core/envsetup.mk
    local HERE=$PWD  #临时保存当前目录
    local T=

    #当前目录下build/make/core/envsetup.mk不存在,即当前不是编译根目录,且
    #当前目录不是系统根目录
    while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do
        T=$PWD
        if [ -f "$T/Android.mk" ]; then  #如果该目录下有Android.mk文件,则cd过去
            \cd $T
            return
        fi
        \cd ..
    done
    \cd $HERE   #恢复之前的目录
    echo "can't find Android.mk"
}

如上述命令,如果无法找到该模块下的 Android.mk,就会提示:

can't find Android.mk

例如在 packages/apps/Launcher3/protos/ 目录下运行 cproj 命令,则会退到 packages/apps/Launcher3/ 目录下。

2.5 getprebuilt

function getprebuilt
{
    get_abs_build_var ANDROID_PREBUILTS
}
function get_abs_build_var()
{
    if [ "$BUILD_VAR_CACHE_READY" = "true" ]
    then
        eval "echo \"\${abs_var_cache_$1}\""
    return
    fi

    local T=$(gettop)
    if [ ! "$T" ]; then
        echo "Couldn't locate the top of the tree.  Try setting TOP." >&2
        return
    fi
    (\cd $T; build/soong/soong_ui.bash --dumpvar-mode --abs $1)
}

函数 get_abs_build_var() 用于查找编译时变量值,这里通过该函数查找变量是 ANDROID_PREBUILTS 这个绝对路径为 $TOP/prebuilt/linux-x86

2.6 setpaths

主要是配置一些环境变量,在lunch 函数或者 choosecombo 函数最后调用:

function setpaths()
{
    local 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.

    # 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
    local prebuiltdir=$(getprebuilt)
    local gccprebuiltdir=$(get_abs_build_var ANDROID_GCC_PREBUILTS)

    # defined in core/config.mk
    local targetgccversion=$(get_build_var TARGET_GCC_VERSION)
    local 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=
    local ARCH=$(get_build_var TARGET_ARCH)
    local toolchaindir toolchaindir2=
    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
    if [ -d "$gccprebuiltdir/$toolchaindir" ]; then
        export ANDROID_TOOLCHAIN=$gccprebuiltdir/$toolchaindir
    fi

    if [ "$toolchaindir2" -a -d "$gccprebuiltdir/$toolchaindir2" ]; then
        export ANDROID_TOOLCHAIN_2ND_ARCH=$gccprebuiltdir/$toolchaindir2
    fi

    export ANDROID_DEV_SCRIPTS=$T/development/scripts:$T/prebuilts/devtools/tools:$T/external/selinux/prebuilts/bin

    # add kernel specific binaries
    case $(uname -s) in
        Linux)
            export ANDROID_DEV_SCRIPTS=$ANDROID_DEV_SCRIPTS:$T/prebuilts/misc/linux-x86/dtc:$T/prebuilts/misc/linux-x86/libufdt
            ;;
        *)
            ;;
    esac

    ANDROID_BUILD_PATHS=$(get_build_var ANDROID_BUILD_PATHS):$ANDROID_TOOLCHAIN
    if [ -n "$ANDROID_TOOLCHAIN_2ND_ARCH" ]; then
        ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS:$ANDROID_TOOLCHAIN_2ND_ARCH
    fi
    ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS:$ANDROID_DEV_SCRIPTS

    # Append llvm binutils prebuilts path to ANDROID_BUILD_PATHS.
    local ANDROID_LLVM_BINUTILS=$(get_abs_build_var ANDROID_CLANG_PREBUILTS)/llvm-binutils-stable
    ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS:$ANDROID_LLVM_BINUTILS

    # Set up ASAN_SYMBOLIZER_PATH for SANITIZE_HOST=address builds.
    export ASAN_SYMBOLIZER_PATH=$ANDROID_LLVM_BINUTILS/llvm-symbolizer

    # If prebuilts/android-emulator/<system>/ exists, prepend it to our PATH
    # to ensure that the corresponding 'emulator' binaries are used.
    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
    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

    # Append asuite prebuilts path to ANDROID_BUILD_PATHS.
    local os_arch=$(get_build_var HOST_PREBUILT_TAG)
    local ACLOUD_PATH="$T/prebuilts/asuite/acloud/$os_arch"
    local AIDEGEN_PATH="$T/prebuilts/asuite/aidegen/$os_arch"
    local ATEST_PATH="$T/prebuilts/asuite/atest/$os_arch"
    export ANDROID_BUILD_PATHS=$ANDROID_BUILD_PATHS:$ACLOUD_PATH:$AIDEGEN_PATH:$ATEST_PATH:

    export PATH=$ANDROID_BUILD_PATHS$PATH

    # out with the duplicate old
    if [ -n $ANDROID_PYTHONPATH ]; then
        export PYTHONPATH=${PYTHONPATH//$ANDROID_PYTHONPATH/}
    fi
    # and in with the new
    export ANDROID_PYTHONPATH=$T/development/python-packages:
    if [ -n $VENDOR_PYTHONPATH  ]; then
        ANDROID_PYTHONPATH=$ANDROID_PYTHONPATH$VENDOR_PYTHONPATH
    fi
    export PYTHONPATH=$ANDROID_PYTHONPATH$PYTHONPATH

    export ANDROID_JAVA_HOME=$(get_abs_build_var ANDROID_JAVA_HOME)
    export JAVA_HOME=$ANDROID_JAVA_HOME
    export ANDROID_JAVA_TOOLCHAIN=$(get_abs_build_var ANDROID_JAVA_TOOLCHAIN)
    export ANDROID_PRE_BUILD_PATHS=$ANDROID_JAVA_TOOLCHAIN:
    export PATH=$ANDROID_PRE_BUILD_PATHS$PATH

    unset ANDROID_PRODUCT_OUT
    export ANDROID_PRODUCT_OUT=$(get_abs_build_var PRODUCT_OUT)
    export OUT=$ANDROID_PRODUCT_OUT

    unset ANDROID_HOST_OUT
    export ANDROID_HOST_OUT=$(get_abs_build_var HOST_OUT)

    unset ANDROID_HOST_OUT_TESTCASES
    export ANDROID_HOST_OUT_TESTCASES=$(get_abs_build_var HOST_OUT_TESTCASES)

    unset ANDROID_TARGET_OUT_TESTCASES
    export ANDROID_TARGET_OUT_TESTCASES=$(get_abs_build_var TARGET_OUT_TESTCASES)

    # needed for building linux on MacOS
    # TODO: fix the path
    #export HOST_EXTRACFLAGS="-I "$T/system/kernel_headers/host_include
}

首先确认 gettop 是否能找到编译的根目录,如果找不到,则退出该函数;

接着export 一些环境变量:

  • TARGET_GCC_VERSION
  • ANDROID_DEV_SCRIPTS
  • ANDROID_TOOLCHAIN_2ND_ARCH
  • ANDROID_BUILD_PATHS
  • ASAN_SYMBOLIZER_PATH
  • ANDROID_EMULATOR_PREBUILTS
  • PYTHONPATH
  • ANDROID_PYTHONPATH
  • ANDROID_JAVA_HOME
  • JAVA_HOME
  • ANDROID_JAVA_TOOLCHAIN
  • ANDROID_PRE_BUILD_PATHS
  • ANDROID_PRODUCT_OUT
  • OUT
  • ANDROID_HOST_OUT
  • ANDROID_HOST_OUT_TESTCASES
  • ANDROID_TARGET_OUT_TESTCASES

2.7 set_sequence_number

function set_sequence_number()
{
    export BUILD_ENV_SEQUENCE_NUMBER=13
}

指定环境变量 BUILD_ENV_SEQUENCE_NUMBER,后面 buildspec.mk 中会确认该环境变量的值与CORRECT_BUILD_ENV_SEQUENCE_NUMBER 相等。

2.8 set_stuff_for_environment

function set_stuff_for_environment()
{
    setpaths
    set_sequence_number

    export ANDROID_BUILD_TOP=$(gettop)
    # With this environment variable new GCC can apply colors to warnings/errors
    export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
}

调用 2.6 2.7 节的函数,并设定环境变量 ANDROID_BUILD_TOP 和 GCC_COLORS。

之后调用函数 gettop 打印出来的路径,通过 ANDROID_BUILD_TOP 就可以获知。

该函数通常是在choosetype、chooseproduct、lunch 函数中调用。

2.9 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 函数将lunch 之后的编译配置信息:

============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=11
TARGET_PRODUCT=shift
TARGET_BUILD_VARIANT=userdebug
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm64
TARGET_ARCH_VARIANT=armv8-a
TARGET_CPU_VARIANT=cortex-a55
TARGET_2ND_ARCH=arm
TARGET_2ND_ARCH_VARIANT=armv8-a
TARGET_2ND_CPU_VARIANT=cortex-a55
HOST_ARCH=x86_64
HOST_2ND_ARCH=x86
HOST_OS=linux
HOST_OS_EXTRA=Linux-4.15.0-142-generic-x86_64-Ubuntu-16.04.5-LTS
HOST_CROSS_OS=windows
HOST_CROSS_ARCH=x86
HOST_CROSS_2ND_ARCH=x86_64
HOST_BUILD_TYPE=release
BUILD_ID=RD2A.211001.002
OUT_DIR=out
PRODUCT_SOONG_NAMESPACES=vendor/qqq/opensource/commonsys/packages/apps/Bluetooth vendor/qqq/opensource/commonsys/system/bt/conf external/v4l2_codec2
============================================

通常在 lunch 函数设定完环境变量后会调用该函数进行 config 的打印。详细看 lunch 函数。

2.10 gettargetarch

function gettargetarch
{
    get_build_var TARGET_ARCH
}

通过 get_build_var 函数获取编译目标系统的 CPU 架构,如arm64

2.11 godir

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)):1}
        done
    else
        pathname=${lines[@]:0:1}
    fi
    \cd $T/$pathname
}

这是一个很使用的函数,可以快速的跳转到所指向的目标目录。该命令需要一个目标文件名作为命令行参数。

  • 首先需要创建一个工程文件的filelist,该filelist 默认是放在 gettop 函数得到的工程根目录。当然如果有 OUT_DIR 指定了,那么filelist 会放在该目录下;
  • 如果 filelist 文件不存在,会创建并打印 "Creating  index...";
  • filelist 创建好后,通过 grep和sed 命令从 filelist 中查找所需要指向的目标文件所在的目录,并进行排序;
  • 如果没有找到指定的目标文件,则会打印 "Not found" 并返回;
  • 如果查找到有指定目标文件,则通过 while 循环将包含该文件的目录打印出来,并打印 "Select one: ",提醒工程师选择;
  • 选择 pathname,cd TOP/pathname;

3. 编译函数

3.1 build_build_var_cache

该函数会在 set_stuff_for_environment 函数之前调用,set_stuff_for_environment 详细看第 2.8 节。

该函数用以将 envsetup.sh 中使用函数 get_build_varget_abs_build_var 查询的变量都收集到 cached_vars cached_abs_vars 中,然后通过 soong_ui.bash 脚本创建键值对。

function build_build_var_cache()
{
    local T=$(gettop)
    # Grep out the variable names from the script.
    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' ' '`)
    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 "<val>=<value>" pairs as a shell script.
    build_dicts_script=`\builtin cd $T; build/soong/soong_ui.bash --dumpvars-mode \
                        --vars="${cached_vars[*]}" \
                        --abs-vars="${cached_abs_vars[*]}" \
                        --var-prefix=var_cache_ \
                        --abs-var-prefix=abs_var_cache_`
    local ret=$?
    if [ $ret -ne 0 ]
    then
        unset build_dicts_script
        return $ret
    fi
    # Execute the script to store the "<val>=<value>" pairs as shell variables.
    eval "$build_dicts_script"
    ret=$?
    unset build_dicts_script
    if [ $ret -ne 0 ]
    then
        return $ret
    fi
    BUILD_VAR_CACHE_READY="true"
}

如果在 eval 之前将 $build_dicts_script 打印出来,结果大致如下:

var_cache_2ND_TARGET_GCC_VERSION='4.9'
var_cache_ANDROID_BUILD_PATHS='/home/justinwei/work/myproduct/android/out/soong/host/linux-x86/bin:/home/justinwei/work/myproduct/android/out/host/linux-x86/bin'
var_cache_COMMON_LUNCH_CHOICES='C3MN-userdebug aosp_arm-eng aosp_arm64-eng aosp_blueline_car-userdebug aosp_bonito_car-userdebug aosp_car_arm-userdebug aosp_car_arm64-userdebug aosp_car_x86-userdebug aosp_car_x86_64-userdebug aosp_cf_arm64_auto-userdebug aosp_cf_arm64_phone-userdebug aosp_cf_x86_64_phone-userdebug aosp_cf_x86_auto-userdebug aosp_cf_x86_phone-userdebug aosp_cf_x86_tv-userdebug aosp_coral_car-userdebug aosp_crosshatch_car-userdebug aosp_flame_car-userdebug aosp_x86-eng aosp_x86_64-eng arm_krait-eng arm_v7_v8-eng armv8-eng armv8_kryo385-eng car_x86_64-userdebug shift-userdebug shift_global-userdebug shift_in-userdebug shiftevb-userdebug qemu_trusty_arm64-userdebug silvermont-eng'
var_cache_HOST_PREBUILT_TAG='linux-x86'
var_cache_TARGET_ARCH='arm64'
var_cache_TARGET_BUILD_VARIANT='user'
var_cache_TARGET_DEVICE='shift'
var_cache_TARGET_GCC_VERSION='4.9'
var_cache_TARGET_PLATFORM_VERSION='RP1A'
var_cache_TARGET_PRODUCT='shift'
var_cache_print=''
var_cache_report_config='============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=11
TARGET_PRODUCT=shift
TARGET_BUILD_VARIANT=user
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm64
TARGET_ARCH_VARIANT=armv8-a
TARGET_CPU_VARIANT=cortex-a55
TARGET_2ND_ARCH=arm
TARGET_2ND_ARCH_VARIANT=armv8-a
TARGET_2ND_CPU_VARIANT=cortex-a55
HOST_ARCH=x86_64
HOST_2ND_ARCH=x86
HOST_OS=linux
HOST_OS_EXTRA=Linux-5.10.102.1-microsoft-standard-WSL2-x86_64-Ubuntu-18.04.2-LTS
HOST_CROSS_OS=windows
HOST_CROSS_ARCH=x86
HOST_CROSS_2ND_ARCH=x86_64
HOST_BUILD_TYPE=release
BUILD_ID=RD2A.211001.002
OUT_DIR=out
PRODUCT_SOONG_NAMESPACES=vendor/qcom/opensource/commonsys/packages/apps/Bluetooth vendor/qcom/opensource/commonsys/system/bt/conf
============================================'
abs_var_cache_ANDROID_CLANG_PREBUILTS='/home/justinwei/work/myproduct/android/prebuilts/clang/host/linux-x86'
abs_var_cache_ANDROID_GCC_PREBUILTS='/home/justinwei/work/myproduct/android/prebuilts/gcc/linux-x86'
abs_var_cache_ANDROID_JAVA_HOME='/home/justinwei/work/myproduct/android/prebuilts/jdk/jdk11/linux-x86'
abs_var_cache_ANDROID_JAVA_TOOLCHAIN='/home/justinwei/work/myproduct/android/prebuilts/jdk/jdk11/linux-x86/bin'
abs_var_cache_ANDROID_PREBUILTS='/home/justinwei/work/myproduct/android/prebuilt/linux-x86'
abs_var_cache_HOST_OUT='/home/justinwei/work/myproduct/android/out/host/linux-x86'
abs_var_cache_HOST_OUT_TESTCASES='/home/justinwei/work/myproduct/android/out/host/linux-x86/testcases'

3.2 get_abs_build_var

function get_abs_build_var()
{
    if [ "$BUILD_VAR_CACHE_READY" = "true" ]
    then
        eval "echo \"\${abs_var_cache_$1}\""
    return
    fi

    local T=$(gettop)
    if [ ! "$T" ]; then
        echo "Couldn't locate the top of the tree.  Try setting TOP." >&2
        return
    fi
    (\cd $T; build/soong/soong_ui.bash --dumpvar-mode --abs $1)
}

通过 soog_ui.bash 解析需要查找的变量,该变量已经在之前的 build_build_var_cache 函数中创建了键值对,详细看第 3.1 节。

这些变量都是以 abs_var_cache_ 作为前缀,详细看第 3.1 节中打印的情况。

3.3 get_build_var

function get_build_var()
{
    if [ "$BUILD_VAR_CACHE_READY" = "true" ]
    then
        eval "echo \"\${var_cache_$1}\""
    return
    fi

    local T=$(gettop)
    if [ ! "$T" ]; then
        echo "Couldn't locate the top of the tree.  Try setting TOP." >&2
        return
    fi
    (\cd $T; build/soong/soong_ui.bash --dumpvar-mode $1)
}

该函数与上面的 get_abs_build_var 差不多的,区别在于 get_abs_build_var 指定的变量都是以 abs_var_cache_ 为前缀,而get_build_var 指定的变量都是以 var_cache_ 为前缀。

3.4 print_lunch_menu

function print_lunch_menu()
{
    local uname=$(uname)
    local choices=$(TARGET_BUILD_APPS= get_build_var COMMON_LUNCH_CHOICES)
    echo
    echo "You're building on" $uname
    echo
    echo "Lunch menu... pick a combo:"

    local i=1
    local choice
    for choice in $(echo $choices)
    do
        echo "     $i. $choice"
        i=$(($i+1))
    done

    echo
}

打印 lunch 可选的 combo:

You're building on Linux

Lunch menu... pick a combo:
     1. C3MN-userdebug
     2. aosp_arm-eng
     3. aosp_arm64-eng
     4. aosp_blueline_car-userdebug
     5. aosp_bonito_car-userdebug
     6. aosp_car_arm-userdebug
     7. aosp_car_arm64-userdebug
     8. aosp_car_x86-userdebug
     9. aosp_car_x86_64-userdebug
     10. aosp_cf_arm64_auto-userdebug
     11. aosp_cf_arm64_phone-userdebug
     12. aosp_cf_x86_64_phone-userdebug
     13. aosp_cf_x86_auto-userdebug
     14. aosp_cf_x86_phone-userdebug
     15. aosp_cf_x86_tv-userdebug
     16. aosp_coral_car-userdebug
     17. aosp_crosshatch_car-userdebug
     18. aosp_flame_car-userdebug

3.5 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
        local choices=($(TARGET_BUILD_APPS= get_build_var COMMON_LUNCH_CHOICES))
        if [ $answer -le ${#choices[@]} ]
        then
            # array in zsh starts from 1 instead of 0.
            if [ -n "$ZSH_VERSION" ]
            then
                selection=${choices[$(($answer))]}
            else
                selection=${choices[$(($answer-1))]}
            fi
        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
}

函数是android 系统编译之前的核心操作,大致流程如下:

  • lunch 有参数否?没有的话 print_lunch_menu,工程师进行选择;
  • 根据选择,确定 build variant;
  • 根据选择,确定 build product;
  • build_build_var_cache 函数调用,配置编译所需变量,详细看第 3.1 节;
  • 设置环境变量 TARGET_PRODUCT、TARGET_BUILD_VARIANT、TARGET_PLATFORM_VERSION、TARGET_BUILD_TYPE;
  • set_stuff_for_environment 设置其他环境变量,详细看第 2.8 节;
  • printconfig 将 lunch配置信息打印出来,详细看第 2.9 节;
  • destroy_build_var_cache 销毁build_build_var_cache 创建的变量键值对;

3.6 _trigger_build

在分析 m、mm、mmm 等函数之前,需要了解下 _trigger_build 这个函数:

function _trigger_build()
(
    local -r bc="$1"; shift
    if T="$(gettop)"; then
      _wrap_build "$T/build/soong/soong_ui.bash" --build-mode --${bc} --dir="$(pwd)" "$@"
    else
      echo "Couldn't locate the top of the tree. Try setting TOP."
    fi
)

主要调用 $TOP/build/soong/soong_ui.bash 脚本进行编译,然后通过 _wrap_build 函数对结果进行特殊显示,详细见下面。

3.6.1 _wrap_build

function _wrap_build()
{
    if [[ "${ANDROID_QUIET_BUILD:-}" == true ]]; then
      "$@"
      return $?
    fi
    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
}

主要处理结果,以及结果的颜色效果、编译用时。

3.7 m 和mm系列

function m()
(
    call_hook ${FUNCNAME[0]} $@
    if [ $? -ne 0 ]; then
        return 1
    fi

    _trigger_build "all-modules" "$@"
)

function mm()
(
    call_hook ${FUNCNAME[0]} $@
    if [ $? -ne 0 ]; then
        return 1
    fi

    _trigger_build "modules-in-a-dir-no-deps" "$@"
)

function mmm()
(
    call_hook ${FUNCNAME[0]} $@
    if [ $? -ne 0 ]; then
        return 1
    fi

    _trigger_build "modules-in-dirs-no-deps" "$@"
)

function mma()
(
    call_hook ${FUNCNAME[0]} $@
    if [ $? -ne 0 ]; then
        return 1
    fi

    _trigger_build "modules-in-a-dir" "$@"
)

function mmma()
(
    call_hook ${FUNCNAME[0]} $@
    if [ $? -ne 0 ]; then
        return 1
    fi

    _trigger_build "modules-in-dirs" "$@"
)
  • m,从根部开始编译所有的模块,m 也可以指定module,编译直接用 m 命令即可;
  • mm,编译当前目录下所有的模块,当前目录下需要有Androd.mk,否则会往上一级找,只编译当前模块,不会编译依赖模块;
  • mmm,同mm,编译指定目录下的所有模块;
  • mma,同mm,需要编译依赖模块;
  • mmma,同mmm,需要编译依赖模块;

4. 搜索函数

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

function gogrep()
{
    find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.go" \
        -exec grep --color -n "$@" {} +
}

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

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

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
}

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

function owngrep()
{
    find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -type f -name 'OWNERS' \
        -exec grep --color -n "$@" {} +
}

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

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

搜索的几个函数基本相同,都是使用的 find 命令和 grep 命令结合。

首先, find 会排除一些文件夹:

  • .repo
  • .git
  • out

接着,find 会指定文件类型:

  • ggrep 针对 *.gradle 文件
  • gogreap 针对 *.go 文件
  • jgrep 针对 *.java 文件
  • cgrep 针对 *.c  *.cc  *.cpp  *.h  *.hpp
  • resgrep 针对目录中含有 *.xml
  • mangrep 针对 AndroidManifest.xml 文件
  • owngrep 针对 OWNERS 文件
  • sepgrep 针对名为 sepolicy 目录
  • rcgrep 针对 *.rc 的文件

最后,find 通过 -exec 执行 grep 命令

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

私房菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值
>