Android 构建编译笔记二---- 初始化编译环境

Google已经给出了android的原生编译流程:source build/envsetup.sh加载命令初始化环境、lunch选择编译目标,进行产品特定配置初始化,再进行编译。

关于Android如何查找特定product配置信息,还看需要从source build/envsetup.sh看起。source build/envsetup.sh 文件是为了将相关envsetup.sh文件中的环境变量和函数导出到shell终端环境,即初始化编译环境,为了下一步编译做铺垫。

 build/envsetup.sh脚本主题

T=$(_gettop_once)
if [ ! "$T" ]; then
    echo "Couldn't locate the top of the tree. Always source build/envsetup.sh from the root of the tree." >&2
    return 1
fi  #确认编译根目录

validate_current_shell
set_global_paths
source_vendorsetup
addcompletions

从build/envsetup.sh的执行代码段可以看出,该脚本主要执行的四个方面的事情:

1、确认当前的shell环境,测试下当前的shell 环境是否可用

function 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
}

1)如果是bash环境没有问题,

2)对于 zsh 环境需要特殊处理,通过enable_zsh_completion函数自动加载 bashcompinit 来支持 bash 风格的补全

function enable_zsh_completion() {
    # Don't override user's options if bash-style completion is already enabled.
    if ! declare -f complete >/dev/null; then
        autoload -U compinit && compinit
        autoload -U bashcompinit && bashcompinit
    fi
}

3)如果是其他环境会warning。

2、初始化一些全局环境变量:

1)function set_global_paths()

通过function set_global_paths() 设置,主要是一些跟lunch特定target无关的一些全局路径添加到ANDROID_GLOBAL_BUILD_PATHS 变量中,再export给环境变量PATH。下次运行时,会将ANDROID_GLOBAL_BUILD_PATHS 从 PATH 中删除。 以便可以多次获取 envsetup.sh 并且仍然具有工作路径。

而lunch特定target的特定全局路径,则配置到set_lunch_paths函数。

function set_global_paths()
{
    local T=$(gettop)
    if [ ! "$T" ]; then
        echo "Couldn't locate the top of the tree.  Try setting TOP."
        return
    fi

    if [ -n "$ANDROID_GLOBAL_BUILD_PATHS" ] ; then
        export PATH=${PATH/$ANDROID_GLOBAL_BUILD_PATHS/}
    fi
    # And in with the new...
    ANDROID_GLOBAL_BUILD_PATHS=$T/build/bazel/bin  #配置了bazel的命令路径
    ANDROID_GLOBAL_BUILD_PATHS+=:$T/development/scripts # 配置了给应用开发脚本路径,如一些进程异常堆栈信息stack和gdb调试使用工具gdbclient.py的路径
# https://source.android.com/docs/core/tests/debug/gdb?hl=zh-cn
    ANDROID_GLOBAL_BUILD_PATHS+=:$T/prebuilts/devtools/tools # 配置了prebuilts目录下开发工具路径
    # add kernel specific binaries
    if [ $(uname -s) = Linux ] ; then  #uname -s命令用于显示操作系统名称
        ANDROID_GLOBAL_BUILD_PATHS+=:$T/prebuilts/misc/linux-x86/dtc # 配置了dtc路径
        ANDROID_GLOBAL_BUILD_PATHS+=:$T/prebuilts/misc/linux-x86/libufdt # 配置了libufdt 路径
    fi
    # If prebuilts/android-emulator// exists, prepend it to our PATH
    # to ensure that the corresponding 'emulator' binaries are used.
    case $(uname -s) in  #基于不同的系统设置ANDROID_EMULATOR_PREBUILTS变量
        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_GLOBAL_BUILD_PATHS+=:$ANDROID_EMULATOR_PREBUILTS  
# 如果ANDROID_EMULATOR_PREBUILTS变量指定的目录存在,则将其添加到Android编译相关的路径变量中
# 此处配置了适合linux操作系统使用的仿真器工具linux-x86_64路径
        export ANDROID_EMULATOR_PREBUILTS
    fi
    # Finally, set PATH
    export PATH=$ANDROID_GLOBAL_BUILD_PATHS:$PATH
}

2) function source_vendorsetup()

通过source相关的设置脚本。如下详解(一)

3、函数补全

特别是一些bash命令的补全。如下详解(二)

一、source 特定目录下配置的环境变量

文件包含了原始的Andorid 环境配置文件,除此之外,使用了函数source_vendorsetup同时查找vendor,device,prodcut三个目录4层范围的vendorenvsetup.sh文件,找到之后,先判断 allowed 是否为空,或者 allowed_files 和 $f 正则匹配,然后运行'. "$T/$f" '去 source $f,以包含这些文件内配置的环境变量。

function source_vendorsetup()

2082 function source_vendorsetup() {
2083     unset VENDOR_PYTHONPATH
2084     local T="$(gettop)"
2085     allowed=
2086     for f in $(cd "$T" && find -L device vendor product -maxdepth 4 -name 'allowed-vendorsetup_sh-files' 2>/dev/null | sort); do
2087         if [ -n "$allowed" ]; then
2088             echo "More than one 'allowed_vendorsetup_sh-files' file found, not including any vendorsetup.sh files:"
2089             echo "  $allowed"
2090             echo "  $f"
2091             return
2092         fi
2093         allowed="$T/$f"
2094     done
2095 
2096     allowed_files=
2097     [ -n "$allowed" ] && allowed_files=$(cat "$allowed")
2098     for dir in device vendor product motorola/build; do #可以添加product厂商vendorsetup.sh对应的目录
2099         for f in $(cd "$T" && test -d $dir && \
2100             find -L $dir -maxdepth 4 -name 'vendorsetup.sh' 2>/dev/null | sort); do
2101 
2102             if [[ -z "$allowed" || "$allowed_files" =~ $f ]]; then
2103                 echo "including $f"; . "$T/$f"
2104             else
2105                 echo "ignoring $f, not in $allowed"
2106             fi
2107         done
2108     done
2109 }
  • 首先,查找 device、vendor、product 目录下最大深度为4 的子目录中,是否存在 allowed-vendorsetup_sh-files 文件并排序,但这个文件只能1个,不然会报错退出;
  • 接着,在device、vendor、product 中同样查找最大深度为 4 的子目录中,是否存在 vendorsetup.sh,打印 includeing ... ,然后运行该 vendorsetup.sh 脚本,这些脚本大多是配置一些环境变量;

此处,如果环境变量需要修改,此处可以添加vendorsetup.sh对应的目录到代码里面。可以在product后面指定了厂商(chipet或者product)环境的路径,或者在指定目录(device、product、vendor)下添加vendorsetup.sh文件,去添加厂商自己的环境。运行结果:

including device/qcom/common/cuttlestone/vendorsetup.sh
including vendor/qcom/nonhlos/build_support/vendorsetup.sh
including vendor/qcom/opensource/core-utils/vendorsetup.sh
including vendor/qcom/proprietary/common/vendorsetup.sh
including vendor/qcom/proprietary/prebuilt_grease/vendorsetup.sh
including */build/vendorsetup.sh
Initializing the Official * Linux x64 Build Toolchain Setup.

到此为止,配置的都是一些简单环境变量。

这里你可以随意添加任意厂商独有的vendorsetup.sh文件,比如说设置一些厂商专属的common 定义和变量,比如说源码路径,工具路径,脚本路径,链接地址或其他环境变量(jave_home...), for your will!

二、vendorsetup.sh补全功能:

function addcompletions()

vendorsetup.sh还包含了一些命令补全的功能都在addcompletions这个函数里面。我们在Linux下都用shell的TAB自动补全功能,非常方便,有时Android开发程序,可能需要自定义实现TAB自动补全功能,这时可能就会用到complete命令来实现对命令的自定义补全,比如针对 lunch这个命令 使用 _lunch 这个函数进行补全,gomod, m 也是这样。

此处还导入了一些Android自身代码里面包含的bash补充脚本。

function addcompletions()
{
    local f=
    # Keep us from trying to run in something that's neither bash nor zsh.
    if [ -z "$BASH_VERSION" -a -z "$ZSH_VERSION" ]; then
        return #需要提前指定这两个环境变量,如果没有指定,该函数return
    fi  #检测 bash 或 zsh 版本以避免不在 bash 或 zsh 的环境中运行
    

    # 避免运行在太老的版本中,这里是小于 3 的版本
    if [ -n "$BASH_VERSION" -a ${BASH_VERSINFO[0]} -lt 3 ]; then
        return
    fi
    local completion_files=(
      packages/modules/adb/adb.bash
      system/core/fastboot/fastboot.bash
      tools/asuite/asuite.sh
      prebuilts/bazel/common/bazel-complete.bash
    )
    # Completion can be disabled selectively to allow users to use non-standard completion.支持禁用补全命令,使用方法如下例子,配置对应的命令到变量ENVSETUP_NO_COMPLETION
    # e.g.
    # ENVSETUP_NO_COMPLETION=adb # -> disable adb completion
    # ENVSETUP_NO_COMPLETION=adb:bit # -> disable adb and bit completion
    local T=$(gettop)
    # 确认上面几个脚本是否存在,通过should_add_completion确认该脚本是否在白名单中
    for f in ${completion_files[*]}; do
        f="$T/$f"
        if [ ! -f "$f" ]; then
          echo "Warning: completion file $f not found"
        elif should_add_completion "$f"; then
            . $f  #只有在不符合ENVSETUP_NO_COMPLETION配置的禁用命令时,才运行文件
        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
        # TODO(b/244559459): Support b autocompletion for zsh
        complete -F _bazel__complete -o nospace b
    fi
    complete -F _lunch lunch
    complete -F _complete_android_module_names pathmod
    complete -F _complete_android_module_names gomod
    complete -F _complete_android_module_names outmod
    complete -F _complete_android_module_names installmod
    complete -F _complete_android_module_names bmod
    complete -F _complete_android_module_names m
}

function should_add_completion():禁用补全命令判断函数

# Takes a command name, and check if it's in ENVSETUP_NO_COMPLETION or not.
function should_add_completion() {
    local cmd="$(basename $1| sed 's/_completion//' |sed 's/\.\(.*\)*sh$//')"
    case :"$ENVSETUP_NO_COMPLETION": in
        *:"$cmd":*)
            return 1
            ;;
    esac
    return 0
}
function _complete_android_module_names() {
    local word=${COMP_WORDS[COMP_CWORD]}
    COMPREPLY=( $(allmod | grep -E "^$word") )
}

这里有个特别重要的补全命令:lunch

$ lunch
aosp_coral-userdebug              aosp_flame-userdebug          aosp_x86-eng  

三、函数命令

除了变量定义和一些补全函数,vendorsetup.sh还定义了几个重要的函数,用于后续编译。

function Hmm()

hmm函数是打印envsetup.sh部分函数帮助信息的函数,除此之外外,还会打印envsetup.sh文件里面所有函数:

1 function hmm() {
2 cat <-
8               Selects  as the product to build, and  as the variant to build, and stores those selections in the environment to be read by subsequent
10               invocations of 'm' etc.
....
52 EOF
53     local T=$(gettop)
54     local A=""
55     local i
56     for i in `cat $T/build/envsetup.sh | sed -n "/^[[:blank:]]*function /s/function \([a-z_]*\).*/\1/p" | sort | uniq`; do   
57       A="$A $i"
58     done
59     echo $A ##列出了脚本内所有的函数名
60 }

function printconfig()

打印当前编译配置

126 function get_build_var()
127 {
128     if [ "$BUILD_VAR_CACHE_READY" = "true" ]
129     then
130         eval "echo \"\${var_cache_$1}\""
131         return 0
132     fi
133 
134     local T=$(gettop)
135     if [ ! "$T" ]; then
136         echo "Couldn't locate the top of the tree.  Try setting TOP." >&2
137         return 1
138     fi
139     (\cd $T; build/soong/soong_ui.bash --dumpvar-mode $1)
140 }
141 
348 function printconfig()
349 {
350     local T=$(gettop)
351     if [ ! "$T" ]; then
352         echo "Couldn't locate the top of the tree.  Try setting TOP." >&2
353         return
354     fi
355     get_build_var report_config
356 }
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=11
TARGET_PRODUCT=aosp_arm64
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR=
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm64
HOST_ARCH=x86
HOST_OS=Linux
HOST_BUILD_TYPE=release
BUILD_ID=OPENMASTER
OUT_DIR=out
============================================

function lunch()

android 系统编译之前的核心,根据输入的参数设置环境变量。将lunch函数命令关联_lunch函数,_lunch为lunch的补全函数,也就是说当关联之后,在终端输入lunch命令后,按tab键可以进行参数的补全。当执行lunch命令时,按下tab键一次,则执行_lunch函数一次,_lunch函数提供给我们可供选择的lunch选项(显示出匹配的lunch-combo)。lunch命令设置环境变量TARGET_PRODUCT,TARGET_BUILD_VARIANT,TARGET_SIMULATOR,TARGET_BUILD_TYPE,随后调用set_stuff_for_environment设置,并printconfig显示。不给参数时,将提示用户选择。
Usage:        lunch  [-]

1)判断lunch 后面是否有跟随参数?没有的话 调用print_lunch_menu,可以选择lunch的对应target。

2)根据用户选择,确定 build variant和build product,将对应的值赋值给TARGET_PRODUCT,TARGET_BUILD_VARIANT

3)调用build_build_var_cache 函数,

4)set_stuff_for_environment 设置其他环境变量

5)调用printconfig 将 lunch配置信息打印出来

6)调用destroy_build_var_cache 清除build_build_var_cache 创建的变量键值对;

lunch函数解析如下:

642 function lunch()
643 {
644     local answer 
645     # 定义局部变量answer来存放`lunch`命令传进来的参数(即lunch-combo,可以是字符串或数字)
646     if [[ $# -gt 1 ]]; then
647         echo "usage: lunch [target]" >&2
648         return 1
649     fi
651     if [ "$1" ]; then
652         answer=$1
653     else
654         print_lunch_menu  
655         echo -n "Which would you like? [aosp_arm-eng] "
656         read answer
657     fi   # 如果用户没有没有传进来任何值,则打印出`lunch-combo`列表,提示用户输入
658 
659     local selection= # 定义局部变量`selection`,用来存放`lunch-combo`字符串
661     if [ -z "$answer" ] 
662     then
663         selection=aosp_arm-eng # 如果`answer`为空,则把`selection`设置为默认的`aosp_arm-eng`
664     elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")
665     then
666         local choices=($(TARGET_BUILD_APPS= get_build_var COMMON_LUNCH_CHOICES))
667         if [ $answer -le ${#choices[@]} ]
668         then
669             # array in zsh starts from 1 instead of 0.
670             if [ -n "$ZSH_VERSION" ]
671             then
672                 selection=${choices[$(($answer))]}
673             else
674                 selection=${choices[$(($answer-1))]}
675             fi
676         fi
677     else # 如果answer是数字,则把`selection`设置为对应的`lunch-combo`字符串
678         selection=$answer
679     fi  #如果`answer`是字符串,则把`selection`赋值为`answer`
680 
681     export TARGET_BUILD_APPS=
683     local product variant_and_version variant version
684     product=${selection%%-*} # Trim everything after first dash
685     variant_and_version=${selection#*-} # Trim everything up to first dash
686     if [ "$variant_and_version" != "$selection" ]; then
687         variant=${variant_and_version%%-*}
688         if [ "$variant" != "$variant_and_version" ]; then
689             version=${variant_and_version#*-}
690         fi
691     fi  #variant获得编译变体类型:"user/userdebug/eng"
692 
693     if [ -z "$product" ]
694     then
695         echo
696         echo "Invalid lunch combo: $selection"
697         return 1
698     fi #将selection去掉后缀"user/userdebug/eng"赋值给product,为空报错。
699 
700     TARGET_PRODUCT=$product \
701     TARGET_BUILD_VARIANT=$variant \
702     TARGET_PLATFORM_VERSION=$version \
703     build_build_var_cache
704     if [ $? -ne 0 ]
705     then
706         return 1
707     fi
708     export TARGET_PRODUCT=$(get_build_var TARGET_PRODUCT)
709     export TARGET_BOARD_PLATFORM=$(get_build_var TARGET_BOARD_PLATFORM)
710     export TARGET_BUILD_VARIANT=$(get_build_var TARGET_BUILD_VARIANT)
711     if [ -n "$version" ]; then
712       export TARGET_PLATFORM_VERSION=$(get_build_var TARGET_PLATFORM_VERSION)
713     else
714       unset TARGET_PLATFORM_VERSION
715     fi
716     export TARGET_BUILD_TYPE=release
        if [ $used_lunch_menu -eq 1 ]; then
           echo
           echo "Hint: next time you can simply run 'lunch $selection'"
       fi
717 
718     [[ -n "${ANDROID_QUIET_BUILD:-}" ]] || echo
720     set_stuff_for_environment
721     [[ -n "${ANDROID_QUIET_BUILD:-}" ]] || printconfig
722     destroy_build_var_cache
        if [[ -n "${CHECK_MU_CONFIG:-}" ]]; then
          check_mu_config
        fi
723 }
724 
725 unset COMMON_LUNCH_CHOICES_CACHE
726 # Tab completion for lunch.
727 function _lunch()  
728 {
729     local cur prev opts
730     COMPREPLY=()
731     cur="${COMP_WORDS[COMP_CWORD]}"
732     prev="${COMP_WORDS[COMP_CWORD-1]}"
733 
734     if [ -z "$COMMON_LUNCH_CHOICES_CACHE" ]; then
735         COMMON_LUNCH_CHOICES_CACHE=$(TARGET_BUILD_APPS= get_build_var COMMON_LUNCH_CHOICES)
736     fi
737 
738     COMPREPLY=( $(compgen -W "${COMMON_LUNCH_CHOICES_CACHE}" -- ${cur}) )
739     return 0
740 }

COMMON_LUNCH_CHOICES参数,通过get_build_var函数来获取值。

126 function get_build_var()
127 {
128     if [ "$BUILD_VAR_CACHE_READY" = "true" ]
129     then
130         eval "echo \"\${var_cache_$1}\""
131         return 0
132     fi
133 
134     local T=$(gettop)
135     if [ ! "$T" ]; then
136         echo "Couldn't locate the top of the tree.  Try setting TOP." >&2
137         return 1
138     fi
139     (\cd $T; build/soong/soong_ui.bash --dumpvar-mode $1)
140 }

function add_lunch_combo()

将提供的编译选项参数添加到LUNCH_MENU_CHOICES列表中。向环境变量LUNCH_MENU_CHOICES标识的列表中添加项。envsetup.sh中默认添加了full-eng,full_x86-eng,和simulator。

function print_lunch_menu()

读取LUNCH_MENU_CHOICES列表,打印列出可执行lunch的选项LUNCH_MENU_CHOICES。该函数调用get_build_var,并传递参数COMMON_LUNCH_CHOICES来获取当前所有平台,COMMON_LUNCH_CHOICES被定义在AndroidProducts.mk中。

607 function print_lunch_menu()
608 {
609     local uname=$(uname)
610     local choices
611     choices=$(TARGET_BUILD_APPS= TARGET_PRODUCT= TARGET_BUILD_VARIANT= get_build_var COMMON_LUNCH_CHOICES 2>/dev/null)
612     local ret=$?
613 
614     echo
615     echo "You're building on" $uname
616     echo
617 
618     if [ $ret -ne 0 ]
619     then
620         echo "Warning: Cannot display lunch menu."
621         echo
622         echo "Note: You can invoke lunch with an explicit target:"
623         echo
624         echo "  usage: lunch [target]" >&2
625         echo
626         return
627     fi
628 
629     echo "Lunch menu... pick a combo:"
630 
631     local i=1
632     local choice
633     for choice in $(echo $choices)
634     do
635         echo "     $i. $choice"
636         i=$(($i+1))
637     done
638 
639     echo
640 }
641 

 function make()

在Andorid 8.0之后,编译系统对makefile做的一层封装执行make命令后,实际调用envsetup.sh脚本里面的make函数。

1720 function make()
1721 {
1722     _wrap_build $(get_make_command "$@") "$@"
1723 }

function get_make_command()

get_make_command函数会先检测 soong_ui.bash 文件是否存在,去触发make的不同调用机制,由原来的make替换为soong_ui.bash --make-mode,通过参数配置实现make的间接调用以进行编译。如果soong_ui.bash 文件不存在,直接调用系统的make命令进行编译。

目前,最新的Android代码构建,是执行 build/soong/soong_ui.bash --make-mode。

所以,get_make_command决定了make命令执行方式,函数定义如下:

1626 function get_make_command()
1627 {
1628     # If we're in the top of an Android tree, use soong_ui.bash instead of make
1629     if [ -f build/soong/soong_ui.bash ]; then
1630         # Always use the real make if -C is passed in
1631         for arg in "$@"; do
1632             if [[ $arg == -C* ]]; then
1633                 echo command make
1634                 return
1635             fi
1636         done
1637         echo build/soong/soong_ui.bash --make-mode
1638     else
1639         echo command make
1640     fi
1641 }

function m,mm,mmm,mmma,mma()

这几个函数实际上都是执行make操作,区别如下:
m:不管当前路径如何,编译所有的模块,也可以指定module,相当于在Android代码树的根目录下执行make操作

都是需要调用的_trigger_build()函数,该函数主要调用 $TOP/build/soong/soong_ui.bash 脚本进行编译,然后通过 _wrap_build 函数对结果进行特殊显示。
mm:查找当前目录下的Android.mk文件,若找到,编译当前目录下的所有模块,当前目录下需要有Androd.mk,否则会往上一级找,只编译当前模块,不会编译依赖模块;
mmm:需要指定路径,并且在指定的路径下查找Android.mk文件,找到后编译该路径下所有模块
mma:与mm类似,但会将有依赖的模块一起编译
mmma:与mmm类似,但会将有依赖的模块一起编译

例如,mmm package/apps/Calculator

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

function m()
(
    _trigger_build "all-modules" "$@"
)
function mm()
(
    _trigger_build "modules-in-a-dir-no-deps" "$@"
)
function mmm()
(
    _trigger_build "modules-in-dirs-no-deps" "$@"
)
function mma()
(
    _trigger_build "modules-in-a-dir" "$@"
)
function mmma()
(
    _trigger_build "modules-in-dirs" "$@"
)

function _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_warning=$'\E'"[0;33m"
        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
}

function gettop()和_gettop_once()

获取编译源码的根目录,返回当前android代码树的顶层路径,前提是当前路径位于android代码树中。

该函数配置在 $T/build/make/shell_utils.sh脚本里,通过source导入,从指定的 $TOP 目录或者当前目录开始查找 build/make/core/envsetup.mk,并将能找到该文件的目录返回个调用函数作为操作的根目录。

## $T/build/make/shell_utils.sh

49 IMPORTING_ENVSETUP=true source $T/build/make/shell_utils.sh

15 function gettop
16 {
17     local TOPFILE=build/make/core/envsetup.mk
18     # The ${TOP-} expansion allows this to work even with set -u
19     if [ -n "${TOP:-}" -a -f "${TOP:-}/$TOPFILE" ] ; then  ##如果编译环境已经设置了 $TOP,就检查 $TOP/build/make/core/envsetup.mk文件是否存在
20         # The following circumlocution ensures we remove symlinks from TOP.
21         (cd "$TOP"; PWD= /bin/pwd)
          #跳转到$TOP 目录,将$TOP 目录指向的真实路径存放到PWD中
22     else   # TOP不存在的话
23         if [ -f $TOPFILE ] ; then #检测当前路径是否能找到 build/make/core/envsetup.mk文件
24             # The following circumlocution (repeated below as well) ensures
25             # that we record the true directory name and not one that is
26             # faked up with symlink names.
27             PWD= /bin/pwd  # 如果可以找到,则将当前目录的路径存放到PWD中
28         else
29             local HERE=$PWD  #找不到文件,将当前路径存入变量HERE
30             local T=          # 配置变量T为空
31             while [ \( ! \( -f $TOPFILE \) \) -a \( "$PWD" != "/" \) ]; do  
32                 \cd ..  #无法找到文件,则不断返回到外层目录查找,直至到根目录为止
33                 T=`PWD= /bin/pwd -P`
34             done
35             \cd "$HERE" #返回原路径
36             if [ -f "$T/$TOPFILE" ]; then #再次确认目录$T包含了build/make/core/envsetup.mk,如果是则说明$T是编译的根目录
37                 echo "$T"
38             fi
39         fi
40     fi
41 }

function godir()

在编译路径下搜索匹配的路径,并且根据选择跳转。给出一个关键词,godir会输出一个路径列表供用户选择要进入的路径。路径列表包含的路径满足,路径名中包含这个词,或这路径下的文件有文件名含这个词,忽略out路径。Usage: godir

函数执行了以下操作:

1)首先创建一个工程文件的filelist,如果已经指定OUT_DIR ,否则filelist 默认是放在 gettop 函数得到的工程根目录上。
2)如果 filelist 文件不存在,就使用find 命令在项目根目录里面查找所有普通文件,重定向到文件钟;
3)filelist 创建好后,调用时,将提供的KeyKord通过 grep命令从 filelist 中查找,并进行排序

A、如果没有查找到,则打印"Not found"并直接退出。

B、如果查到,但不是唯一值,则通过 while 循环将包含该文件的目录打印出来,并打印 "Select one: ",提醒用户选择;根据用户选择对应选择编号,赋值给pathname;

C、如果查找到,且是唯一,直接把对应路径赋值给pathname;

4)跳转至 $T/$pathname。

function godir () {
    if [[ -z "$1" ]]; then
        echo "Usage: godir "
        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
}

 function build_build_var_cache()

一次性调用在构建系统的中,获取此脚本所需的所有构建变量。在lunch之后调用,输出基于特定变体的变量信息。一般在set_stuff_for_environment 之前调用。

Usage: build_build_var_cache

该函数用以将 envsetup.sh 中使用函数 get_build_varget_abs_build_var 查询的变量都收集到 cached_vars 和 cached_abs_vars 中,然后通过 soong_ui.bash 脚本创建键值对,赋值给build_dicts_script,通过eval运行"$build_dicts_script"命令,设置的是一些var_cache_*开头的编译环境变量。该函数成功运行后,设置BUILD_VAR_CACHE_READY为true。

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 "=" 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 "=" 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"
}

function get_abs_build_var()

make脚本中某变量的值,加上当前路径为前缀。

Usage: get_abs_build_var TARGET_PRODUCT

在build_build_var_cache函数运行后,即BUILD_VAR_CACHE_READY为true才能使用。

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

function get_build_var()

获取绝对变量,make脚本中某变量的值。Usage: get_build_var VAR_NAME

在build_build_var_cache函数运行后,即BUILD_VAR_CACHE_READY为true才能使用。

这个函数其实核心就是:

# build/envsetup.sh 
126 function get_build_var()
127 {
        ...
139     (\cd $T; build/soong/soong_ui.bash --dumpvar-mode $1)
140 }
# build/soong/cmd/soong_ui/main.go
69 var commands []command = []command{
70 	{  ...
78 	}, {
79 		flag:         "--dumpvar-mode",
80 		description:  "print the value of the legacy make variable VAR to stdout",
81 		simpleOutput: true,
82 		logsPrefix:   "dumpvars-",
83 		config:       dumpVarConfig,
84 		stdio:        customStdio,
85 		run:          dumpVar,
86 	}, ...

306 func dumpVar(ctx build.Context, config build.Config, args []string, _ string) {
307 	flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
308 	flags.Usage = func() {
309 		fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] \n\n", os.Args[0])
310 		fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
311 		fmt.Fprintln(ctx.Writer, "")
312 
313 		fmt.Fprintln(ctx.Writer, "'report_config' is a special case that prints the human-readable config banner")
314 		fmt.Fprintln(ctx.Writer, "from the beginning of the build.")
315 		fmt.Fprintln(ctx.Writer, "")
316 		flags.PrintDefaults()
317 	}
318 	abs := flags.Bool("abs", false, "Print the absolute path of the value")
319 	flags.Parse(args)
320 
321 	if flags.NArg() != 1 {
322 		flags.Usage()
323 		os.Exit(1)
324 	}
325 
326 	varName := flags.Arg(0)
327 	if varName == "report_config" {
328 		varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
329 		if err != nil {
330 			ctx.Fatal(err)
331 		}
332 
333 		fmt.Println(build.Banner(varData))
334 	} else {
335 		varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
336 		if err != nil {
337 			ctx.Fatal(err)
338 		}
339 
340 		if *abs {
341 			var res []string
342 			for _, path := range strings.Fields(varData[varName]) {
343 				if abs, err := filepath.Abs(path); err == nil {
344 					res = append(res, abs)
345 				} else {
346 					ctx.Fatalln("Failed to get absolute path of", path, err)
347 				}
348 			}
349 			fmt.Println(strings.Join(res, " "))
350 		} else {
351 			fmt.Println(varData[varName])
352 		}
353 	}
354 }
39 func DumpMakeVars(ctx Context, config Config, goals, vars []string) (map[string]string, error) {
40 	soongUiVars := map[string]func() string{
41 		"OUT_DIR":  func() string { return config.OutDir() },
42 		"DIST_DIR": func() string { return config.DistDir() },
43 		"TMPDIR":   func() string { return absPath(ctx, config.TempDir()) },
44 	}
45 
46 	makeVars := make([]string, 0, len(vars))
47 	for _, v := range vars {
48 		if _, ok := soongUiVars[v]; !ok {
49 			makeVars = append(makeVars, v)
50 		}
51 	}
52 
53 	var ret map[string]string
54 	if len(makeVars) > 0 {
55 		var err error
56 		ret, err = dumpMakeVars(ctx, config, goals, makeVars, false, "")
57 		if err != nil {
58 			return ret, err
59 		}
60 	} else {
61 		ret = make(map[string]string)
62 	}
63 
64 	for _, v := range vars {
65 		if f, ok := soongUiVars[v]; ok {
66 			ret[v] = f()
67 		}
68 	}
69 
70 	return ret, nil
71 }
72 

function destroy_build_var_cache()

删除构建变量缓存,这样我们仍然可以调用构建系统来获取此脚本中未列出的构建变量。

function destroy_build_var_cache()
{
    unset BUILD_VAR_CACHE_READY
    local v
    for v in $cached_vars; do
      unset var_cache_$v
    done
    unset cached_vars
    for v in $cached_abs_vars; do
      unset abs_var_cache_$v
    done
    unset cached_abs_vars
}

function *grep()

envsetup.sh脚本里面还设置了一部分指定特定文件grep的简写命令, grep会默认排除一些该目录:

.repo,.git,out

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

1060 case `uname -s` in
1061     Darwin)
1062         function sgrep()  #在所有资源文件中查找内容
1063         {
1064             find -E . -name .repo -prune -o -name .git -prune -o  -type f -iregex '.*\.(c|h|cc|cpp|hpp|S|java|kt|xml|sh|mk|aidl|vts|proto)' \
1065                 -exec grep --color -n "$@" {} +
1066         }
1067 
1068         ;;
1069     *)
1070         function sgrep()  # 在当前目录及子目录中所.c,.h,.cpp,.S,.java,.mk,.xml,.sh文件中查找。
1071         {
1072             find . -name .repo -prune -o -name .git -prune -o  -type f -iregex '.*\.\(c\|h\|cc\|cpp\|hpp\|S\|java\|kt\|xml\|sh\|mk\|aidl\|vts\|proto\)' \
1073                 -exec grep --color -n "$@" {} +
1074         }
1075         ;;
1076 esac
1083 function ggrep() #在当前目录以及子目录下所有.gradle文件中查找内容
1084 {
1085     find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.gradle" \
1086         -exec grep --color -n "$@" {} +
1087 }  
1088 
1089 function gogrep()
1090 {
1091     find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.go" \
1092         -exec grep --color -n "$@" {} +
1093 }
1094 
1095 function jgrep()  #在当前目录以及子目录下所有.java文件中查找内容
1096 {
1097     find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.java" \
1098         -exec grep --color -n "$@" {} +
1099 } 
1100 
1101 function rsgrep() 
1102 {
1103     find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.rs" \
1104         -exec grep --color -n "$@" {} +
1105 }
1106 
1107 function ktgrep()
1108 {
1109     find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.kt" \
1110         -exec grep --color -n "$@" {} +
1111 }
1112 
1113 function cgrep()  # 在当前目录以及子目录下所有.c/.cc/.cpp/.h/.hpp文件中查找内容
1114 {
1115     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' \) \
1116         -exec grep --color -n "$@" {} +
1117 }
1118
1119 function resgrep() #在res相关的.xml文件中查找
1120 {
1121     local dir
1122     for dir in `find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -name res -type d`; do
1123         find $dir -type f -name '*\.xml' -exec grep --color -n "$@" {} +
1124     done
1125 }
1126 
1127 function mangrep() # 在当前目录以及子目录下所有AndroidManifest.xml文件中查找内容
1128 {
1129     find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -type f -name 'AndroidManifest.xml' \
1130         -exec grep --color -n "$@" {} +
1131 }
1132 
1133 function owngrep()
1134 {
1135     find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -type f -name 'OWNERS' \
1136         -exec grep --color -n "$@" {} +
1137 }
1138  
1139 function sepgrep()  # 在当前目录以及子目录下对名为sepolicy目录中的文件查找内容
1140 {
1141     find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o -name sepolicy -type d \
1142         -exec grep --color -n -r --exclude-dir=\.git "$@" {} +
1143 }
1144 
1145 function rcgrep()  # 在当前目录以及子目录下所有.rc文件中查找内容
1146 {
1147     find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.rc*" \
1148         -exec grep --color -n "$@" {} +
1149 }

1151 case `uname -s` in
1152     Darwin)
1153         function mgrep() #在当前目录及子目录下所有makefile文件(包括Makefile、.mk文件等)中查找内容
1154         {
1155             find -E . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o \( -iregex '.*/(Makefile|Makefile\..*|.*\.make|.*\.mak|.*\.mk|.*\.bp)' -o -regex '(.*/)?(build|soong)/.*[^/]*\.go' \) -type f \
1156                 -exec grep --color -n "$@" {} +
1157         }
1158 
1159         function treegrep() #在当前目录及子目录下所有.(c|h|cpp|S|java|xml)文件中查找内容
1160         {
1161             find -E . -name .repo -prune -o -name .git -prune -o -type f -iregex '.*\.(c|h|cpp|hpp|S|java|kt|xml)' \
1162                 -exec grep --color -n -i "$@" {} +
1163         }
1164 
1165         ;;
1166     *)
1167         function mgrep()
1168         {
1169             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 '(.*/)?(build|soong)/.*[^/]*\.go' \) -type f \
1170                 -exec grep --color -n "$@" {} +
1171         }
1172 
1173         function treegrep()
1174         {
1175             find . -name .repo -prune -o -name .git -prune -o -regextype posix-egrep -iregex '.*\.(c|h|cpp|hpp|S|java|kt|xml)' -type f \
1176                 -exec grep --color -n -i "$@" {} +
1177         }
1178 
1179         ;;
1180 esac

function check_product()

检查指定的TARGET_PRODUCT是否允许,默认的有sim和generic。如果不允许,则输出错误信息,允许则无回显。Usage:     check_product

function help()                     # 显示帮助信息

function set_java_home()    # 设置JAVA_HOME环境变量为/usr/lib/jvm/java-6-sun

function check_variant()  

检查variant是否支持,支持则返回0,不支持则返回1。允许的variant列表定义在envsetup.sh中的VARIANT_CHOICES中,默认是user,userdebug,eng。定制android时,可以在VARIANT_CHOICES中添加vairant。
Usage:    check_variant

function set_lunch_paths()或setpaths()

主要是配置lunch后或者choosecombo 函数最后调用配置的一些环境变量,将ANDROID_BUILD_PATHS和ANDROID_PRE_BUILD_PATHS路径加到PATH中。脚本会先清除这两个变量在PATH的定义,在进行重新赋值。该函数在Android U被删除,改成set_lunch_paths函数。Usage: setpaths

ANDROID_BUILD_PATHS包括android编译中要使用到的路径,例如ANDROID_BUILD_PATHS,ANDROID_TOOLCHAIN,ANDROID_TOOLCHAIN_2ND_ARCH,ANDROID_DEV_SCRIPTS,ANDROID_LLVM_BINUTILS,ANDROID_EMULATOR_PREBUILTS,ACLOUD_PATH,AIDEGEN_PATH,ATEST_PATH

ANDROID_PRE_BUILD_PATHS包含ANDROID_JAVA_TOOLCHAIN

函数还配置了ANDROID_PYTHONPATH,JAVA_HOME,ANDROID_PRODUCT_OUT,ANDROID_SOONG_HOST_OUT,ANDROID_HOST_OUT,ANDROID_TARGET_OUT_TESTCASES等等变量。

function set_lunch_paths()
{
    local T=$(gettop)
    if [ ! "$T" ]; then
        echo "Couldn't locate the top of the tree.  Try setting TOP."
        return
    fi
    if [ -z "$ANDROID_LUNCH_BUILD_PATHS" -a -n "$ANDROID_BUILD_PATHS" ] ; then
      ANDROID_LUNCH_BUILD_PATHS="$ANDROID_BUILD_PATHS"
      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/:%/}
        ANDROID_PRE_BUILD_PATHS=
    fi
    # Out with the old...
    if [ -n "$ANDROID_LUNCH_BUILD_PATHS" ] ; then
        export PATH=${PATH/$ANDROID_LUNCH_BUILD_PATHS/}
    fi
    # And in with the new...
    ANDROID_LUNCH_BUILD_PATHS=$(get_abs_build_var SOONG_HOST_OUT_EXECUTABLES)
    ANDROID_LUNCH_BUILD_PATHS+=:$(get_abs_build_var HOST_OUT_EXECUTABLES)
    # Append llvm binutils prebuilts path to ANDROID_LUNCH_BUILD_PATHS.
    local ANDROID_LLVM_BINUTILS=$(get_abs_build_var ANDROID_CLANG_PREBUILTS)/llvm-binutils-stable
    ANDROID_LUNCH_BUILD_PATHS+=:$ANDROID_LLVM_BINUTILS
    # Set up ASAN_SYMBOLIZER_PATH for SANITIZE_HOST=address builds.
    export ASAN_SYMBOLIZER_PATH=$ANDROID_LLVM_BINUTILS/llvm-symbolizer
    # Append asuite prebuilts path to ANDROID_LUNCH_BUILD_PATHS.
    local os_arch=$(get_build_var HOST_PREBUILT_TAG)
    ANDROID_LUNCH_BUILD_PATHS+=:$T/prebuilts/asuite/acloud/$os_arch
    ANDROID_LUNCH_BUILD_PATHS+=:$T/prebuilts/asuite/aidegen/$os_arch
    ANDROID_LUNCH_BUILD_PATHS+=:$T/prebuilts/asuite/atest/$os_arch
    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)
    ANDROID_LUNCH_BUILD_PATHS+=:$ANDROID_JAVA_TOOLCHAIN
    # Fix up PYTHONPATH
    if [ -n $ANDROID_PYTHONPATH ]; then
        export PYTHONPATH=${PYTHONPATH//$ANDROID_PYTHONPATH/}
    fi
    export ANDROID_PYTHONPATH=$T/development/python-packages/adb:$T/development/python-packages/gdbrunner:$T/development/python-packages:
    if [ -n $VENDOR_PYTHONPATH ]; then
        ANDROID_PYTHONPATH=$ANDROID_PYTHONPATH$VENDOR_PYTHONPATH
    fi
    export PYTHONPATH=$ANDROID_PYTHONPATH$PYTHONPATH
    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_SOONG_HOST_OUT
    export ANDROID_SOONG_HOST_OUT=$(get_abs_build_var SOONG_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)
    # Finally, set PATH
    export PATH=$ANDROID_LUNCH_BUILD_PATHS:$PATH
}

function set_stuff_for_environment()                   

依次调用set_lunch_paths,set_sequence_number。设置android编译需要的环境变量。设定环境变量 ANDROID_BUILD_TOP,该函数通常是在choosetype、chooseproduct、lunch 函数中调用。

function set_stuff_for_environment()
{
    set_lunch_paths
    set_sequence_number
    export ANDROID_BUILD_TOP=$(gettop)
}

function set_sequence_number()                

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

function set_sequence_number()
{
    export BUILD_ENV_SEQUENCE_NUMBER=13
}

function settitle()   

设置shell的prompt提示,PROMPT_COMMAND中加入TARGET_PRODUCT,TARGET_BUILD_VARIANT,和TARGET_BUILD_APPS等信息提示。

function choosetype()

配置环境变量TARGET_SIMULATOR。linux下会提示用户选择device或simulator。然后调用set_stuff_for_environment设置

function choosetype()

配置环境变量TARGET_BUILD_TYPE_SIMULATOR。会提示用户选择release或debug。然后调用set_stuff_for_environment设置

function chooseproduct()

配置环境变量TARGET_PRODUCT。会提示用户选择release或debug。然后调用set_stuff_for_environment设置

function choosevariant()

配置环境变量TARGET_BUILD_VARIANT。会提示用户选择release或debug

function tapas()          # 功能同choosecombo

用户给定variant和一个或多个app name,就是LOCAL_PACKAGE_NAME的名字。tapas设定
export TARGET_PRODUCT=generic

export TARGET_BUILD_VARIANT=

 Usage:       tapas *
  ?代表可选,*代表0个,1个或多个。YourVariant 和YourAppName的次序可颠倒。
Example:       tapas user Calculator Calender

function choosecombo():

设置编译参数,依次调用choosesim,choosetype,chooseproduct,choosevariant,set_stuff_for_environment配置,然后调用printconfig输出

function findmakefile()

 查找makefile。查找当前或最接近自己的祖辈路径上的Android.mk,返回Android.mk的路径,假设当前路径处于android代码树中。

function gettargetarch()

获取编译目标系统的 CPU 架构,如arm64

function gettargetarch
{
    get_build_var TARGET_ARCH
}

function croot()     

  # 回到源码的根目录,改变当前路径到代码树顶层

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
}

function cproj()       

#改变当前路径到最近的还有Android.mk文件的祖/父辈路径。

function cproj()
{
    local TOPFILE=build/make/core/envsetup.mk
    local HERE=$PWD
    local T=
    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"
}

function pid()         # 使用adb shell ps命令列出手机上指定名字的进程。Usage: pid

function systemstack() 

 使用kill -3system_server将系统进程中的线程信息写入/data/anr/traces.txt。

function gdbclient()

建立gdb调试环境,包括两步,手机上运行gdbserver,本机上运行arm-eabi-gdb。

Usage:       gdbclient

EXE: AppName的执行名

PORT:gdbserver的端口,例如, 192.168.2.102:5039

AppName:手机中ps列出的app名字,据此查pid。

function getprebuilt()               

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

# build/make/core/dumpvar.mk
ANDROID_PREBUILTS := prebuilt/$(HOST_PREBUILT_TAG)

function getprebuilt
{
    get_abs_build_var ANDROID_PREBUILTS
}

function tracedmdump()

生成dexlist文件qtrace.dexlit,dmtrace数据文件dmtrace,和调用dmtracedump工具生成的dmtrace解析文件dmtrace.html,将生成文件放到指定路径。

Usage:        tracedmdump

如果YourDirName中不含’\’,则将放置的路径是ANDROID_PRODUCT_OUT/traces/YourDirName

function runhat()

貌似使用kill -10的方法得到heap dump并取到本地。使用hat以http方式展现出来。hat可能是个lightweight http server

function getbugreports()

使用adb shell ls /sdcard/bugreports+abd pull 命令,将手机/sdcard/bugreports目录下的文件下载到本地并压缩打包

function getbugreports()
{
    local reports=(`adb shell ls /sdcard/bugreports | tr -d '\r'`)
    if [ ! "$reports" ]; then
        echo "Could not locate any bugreports."
        return
    fi
    local report
    for report in ${reports[@]}
    do
        echo "/sdcard/bugreports/${report}"
        adb pull /sdcard/bugreports/${report} ${report}
        gunzip ${report}
    done
}

function startviewserver()

用指定端口启动viewserver。Usage: startviewserver       不指定端口,则默认4939

function stopviewserver()              # 关闭viewserver

function isviewserverstarted()       # 检查viewserver是否可用

function smoketest()             # 编译smoketest并安装手机运行

function runtest()                                 # 运行development/testrunner/runtest.py $@

function refreshmod()

命令必须在lunch之后执行,主要执行m 去编译更新生成out/target/product/*/module-info.json文件。

function refreshmod() {
    if [ ! "$ANDROID_PRODUCT_OUT" ]; then
        echo "No ANDROID_PRODUCT_OUT. Try running 'lunch' first." >&2
        return 1
    fi
    echo "Refreshing modules (building module-info.json). Log at $ANDROID_PRODUCT_OUT/module-info.json.build.log." >&2
    # for the output of the next command
    mkdir -p $ANDROID_PRODUCT_OUT || return 1
    # Note, can't use absolute path because of the way make works.
    m $(get_build_var PRODUCT_OUT)/module-info.json \
        > $ANDROID_PRODUCT_OUT/module-info.json.build.log 2>&1
}

function allmod()

命令必须在lunch之后执行,函数会列出所有的模块名,AndroidU 该命令是由build/make/core/tasks/module-info.mk文件生成out/target/product/*/all_modules.txt ,调用的时候直接通过cat列出所有的模块名称。但是Andorid T之前,是由python直接导入json模块,去读取out/target/product/*/module-info.json所有的key值。

function allmod() {
    cat $ANDROID_PRODUCT_OUT/all_modules.txt 2>/dev/null
}
#U之前
function allmod() {
    verifymodinfo || return 1
    python3 -c "import json; print('\n'.join(sorted(json.load(open('$ANDROID_PRODUCT_OUT/module-info.json')).keys())))"
}

function pathmod() 

命令必须在lunch之后执行,可以获得某个模块路径。从module-info.json 中获取模块的path key的值。

Usage: pathmod

function pathmod() {
    if [[ $# -ne 1 ]]; then
        echo "usage: pathmod " >&2
        return 1
    fi
    verifymodinfo || return 1
    local relpath=$(python3 -c "import json, os
module = '$1'
module_info = json.load(open('$ANDROID_PRODUCT_OUT/module-info.json'))
if module not in module_info:
    exit(1)
print(module_info[module]['path'][0])" 2>/dev/null)
    if [ -z "$relpath" ]; then
        echo "Could not find module '$1' (try 'refreshmod' if there have been build changes?)." >&2
        return 1
    else
        echo "$ANDROID_BUILD_TOP/$relpath"
    fi
}

function dirmods()

命令必须在lunch之后执行,可以获得某个路径下模块所有的名称。

通过搜索指定的路径,作为path的值去搜索,可以获得对应模块清单列表。pathmod的逆搜索过程。

Usage: dirmods

function dirmods() {
    if [[ $# -ne 1 ]]; then
        echo "usage: dirmods " >&2
        return 1
    fi
    verifymodinfo || return 1
    python3 -c "import json, os
dir = '$1'
while dir.endswith('/'):
    dir = dir[:-1]
prefix = dir + '/'
module_info = json.load(open('$ANDROID_PRODUCT_OUT/module-info.json'))
results = set()
for m in module_info.values():
    for path in m.get(u'path', []):
        if path == dir or path.startswith(prefix):
            name = m.get(u'module_name')
            if name:
                results.add(name)
for name in sorted(results):
    print(name)
"
}

function gomod()

命令必须在lunch之后执行,转到 android 树中的特定模块。 

从module-info.json 中获取模块的path key的值,基于pathmod去执行,获得path值,则cd过去。

Usage: pathmod

function gomod() {
    if [[ $# -ne 1 ]]; then
        echo "usage: gomod " >&2
        return 1
    fi
    local path="$(pathmod $@)"
    if [ -z "$path" ]; then
        return 1
    fi
    cd $path
}

function installmod()

使用adb install 模块的 apk,缓存在 module-info.json 中。 如果进行了任何构建更改,并且应在输出中反映出来,则应首先运行“refreshmod”。

Usage: installmod [adb install arguments]
For example: installmod -r Dialer -> adb install -r /path/to/Dialer.apk

function installmod() {
    if [[ $# -eq 0 ]]; then
        echo "usage: installmod [adb install arguments] " >&2
        echo "" >&2
        echo "Only flags to be passed after the \"install\" in adb install are supported," >&2
        echo "with the exception of -s. If -s is passed it will be placed before the \"install\"." >&2
        echo "-s must be the first flag passed if it exists." >&2
        return 1
    fi
    local _path
    _path=$(outmod ${@:$#:1})
    if [ $? -ne 0 ]; then
        return 1
    fi
    _path=$(echo "$_path" | grep -E \\.apk$ | head -n 1)
    if [ -z "$_path" ]; then
        echo "Module '$1' does not produce a file ending with .apk (try 'refreshmod' if there have been build changes?)" >&2
        return 1
    fi
    local serial_device=""
    if [[ "$1" == "-s" ]]; then
        if [[ $# -le 2 ]]; then
            echo "-s requires an argument" >&2
            return 1
        fi
        serial_device="-s $2"
        shift 2
    fi
    local length=$(( $# - 1 ))
    echo adb $serial_device install ${@:1:$length} $_path
    adb $serial_device install ${@:1:$length} $_path
}

function verifymodinfo()

验证module-info.txt文件是否存在

function verifymodinfo() {
    if [ ! "$ANDROID_PRODUCT_OUT" ]; then
        if [ "$QUIET_VERIFYMODINFO" != "true" ] ; then
            echo "No ANDROID_PRODUCT_OUT. Try running 'lunch' first." >&2
        fi
        return 1
    fi
    if [ ! -f "$ANDROID_PRODUCT_OUT/module-info.json" ]; then
        if [ "$QUIET_VERIFYMODINFO" != "true" ] ; then
            echo "Could not find module-info.json. Please run 'refreshmod' first." >&2
        fi
        return 1
    fi
}

function bmod()

usage: bmod

如果使用 bp2build 转换,则返回 Soong 模块的 Bazel 标签。

function bmod()
(
    if [ $# -ne 1 ]; then
        echo "usage: bmod " >&2
        return 1
    fi
    # We could run bp2build here, but it might trigger bp2build invalidation
    # when used with `b` (e.g. --run_soong_tests) and/or add unnecessary waiting
    # time overhead.
    #
    # For a snappy result, use the latest generated version in soong_injection,
    # and ask users to run m bp2build if it doesn't exist.
    converted_json="$(get_abs_build_var OUT_DIR)/soong/soong_injection/metrics/converted_modules_path_map.json"
    if [ ! -f ${converted_json} ]; then
      echo "bp2build files not found. Have you ran 'm bp2build'?" >&2
      return 1
    fi
    local target_label=$(python3 -c "import json
module = '$1'
converted_json='$converted_json'
bp2build_converted_map = json.load(open(converted_json))
if module not in bp2build_converted_map:
    exit(1)
print(bp2build_converted_map[module] + ':' + module)")
    if [ -z "${target_label}" ]; then
      echo "$1 is not converted to Bazel." >&2
      return 1
    else
      echo "${target_label}"
    fi
)

function outmod()

获取模块已安装输出的列表,也就是安装路径,缓存在 module-info.json 中。

usage: outmod

从module-info.json 中获取模块的installed key的值,获得所有模块安装路径。

function outmod() {
    if [[ $# -ne 1 ]]; then
        echo "usage: outmod " >&2
        return 1
    fi
    verifymodinfo || return 1
    local relpath
    relpath=$(python3 -c "import json, os
module = '$1'
module_info = json.load(open('$ANDROID_PRODUCT_OUT/module-info.json'))
if module not in module_info:
    exit(1)
for output in module_info[module]['installed']:
    print(os.path.join('$ANDROID_BUILD_TOP', output))" 2>/dev/null)
    if [ $? -ne 0 ]; then
        echo "Could not find module '$1' (try 'refreshmod' if there have been build changes?)" >&2
        return 1
    elif [ ! -z "$relpath" ]; then
        echo "$relpath"
    fi
}

参考链接:

https://blog.csdn.net/Eliot_Haber/article/details/106687337

https://www.cnblogs.com/sjjg/p/6137364.html

https://blog.csdn.net/shift_wwx/article/details/130932382

你可能感兴趣的:(Android构建,android)