Skip to content

Latest commit

 

History

History
497 lines (426 loc) · 18 KB

20211026-lunch.md

File metadata and controls

497 lines (426 loc) · 18 KB

envsetup.sh 中的 lunch 函数分析

文章大纲

注:本文的分析基于 AOSP 12, tag android-12.0.0_r2

1. lunch() 函数整体分析

function lunch()
{
    local answer

    if [ "$1" ] ; then
        answer=$1
    else
        # 这里是在屏幕上打印所有的 product-variable
        # 注意这里通过调用 print_lunch_menu 函数,后面有专门介绍
        print_lunch_menu
        echo -n "Which would you like? [aosp_arm-eng] "
        read answer
    fi

    local selection=

    # 这里首先判断 answer变量是不是为0,如果为零的话,selection 变量就会赋值
    # 为默认值 "aosp_arm-eng"
    if [ -z "$answer" ]
    then
        selection=aosp_arm-eng
    # 如果不为零的话,首先会输出 answer 的值,使用 echo -n $answer, "-n"
    # 选项的作用是的输出的时候不输出换行符
    # answer 变量的值并没有输出到屏幕上,而是通过管道传给了后面一条命令:
    # grep -q -e "^[0-9][0-9]*$"
    # 这条命令从 answer 变量中搜寻以两位数字开头的字符串
    elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")
    then
        # 如果找到,就认为是输入的是数字。然后进一步对这个数字做有没有越界的检查。
        local choices=($(TARGET_BUILD_APPS= get_build_var COMMON_LUNCH_CHOICES))
        # 如果这个数字小于 LUNCH_MENU_CHOICES 的大小,就会把 LUNCH_MENU_CHOICES
        # 的第 $answer-1 项复制给 selection 变量。
        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
    # 最终得到的 selection 其实就是一个 product-varient 模式的字符串。

    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

    # 至此得到我们的 product 和 variant
    # 以下是为环境变量赋值,主要是针对 TARGET 的,即设备的
    TARGET_PRODUCT=$product \
    TARGET_BUILD_VARIANT=$variant \
    TARGET_PLATFORM_VERSION=$version \
    # 注意 build_build_var_cache 函数的实现,本质是调用了 "soong_ui --dumpvars-mode"
    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

    [[ -n "${ANDROID_QUIET_BUILD:-}" ]] || echo

    # 至此从用户提取 product 和 variant 的工作告一段落
    # set_stuff_for_environment 是一个重点函数,后面重点看一下
    set_stuff_for_environment

    # 打印重要的 config 信息
    [[ -n "${ANDROID_QUIET_BUILD:-}" ]] || printconfig
    destroy_build_var_cache
}

简单总结一下 lunch 的基本流程

  • 打印所有的 product-variable(print_lunch_menu()
  • 读入用户的选择,如果没有则缺省为 "aosp_arm-eng"
  • 解析用户的选择后得到 product 和 variable
  • 根据 product 和 variable 得到基本的产品相关环境变量
    • TARGET_PRODUCT
    • TARGET_BUILD_VARIANT
    • TARGET_PLATFORM_VERSION
  • 获取所有的和 build 相关的环境变量(包括其绝对路径形式)并记录到 cache 中(build_build_var_cache()
  • 导出 product 相关变量
    • TARGET_PRODUCT
    • TARGET_BUILD_VARIANT
    • TARGET_PLATFORM_VERSION
    • TARGET_BUILD_TYPE
  • 设置 build 过程中需要用到的环境变量(set_stuff_for_environment()),其中主要是通 过 setpaths() 设置一些编译工具的路径
  • 输出主要的 config 信息(printconfig()

在此过程中,lunch 函数会调用 print_lunch_menu()set_stuff_for_environment() 这些重要的函数,而这些函数,譬如 set_stuff_for_environment() 又会调用 setpaths(), 进而 get_build_var()。关系大致如下图所示,后面我们专门对这些函数再做分析。

综上所述,lunch 可以有两种用法:

  • 如果直接执行 lunch,不带参数,那么 lunch 会给出一个 menu 来与用户交互,提示 Which would you like? [aosp_arm-eng] , 直接回车则默认为 aosp_arm-eng, 或者输入数字选择。
  • 我们也可以直接运行一个带参数的 lunch,比如 lunch s410-eng 或者 lunch 22,效果和上面是一样的。

一个输出的示例,参考 Setup Android 12 on RISC-V,运行 lunch sdk_phone64_riscv64

============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=12
TARGET_PRODUCT=sdk_phone64_riscv64
TARGET_BUILD_VARIANT=eng
TARGET_BUILD_TYPE=release
TARGET_ARCH=riscv64
TARGET_ARCH_VARIANT=riscv64
TARGET_CPU_VARIANT=generic
TARGET_2ND_ARCH_VARIANT=riscv64
TARGET_2ND_CPU_VARIANT=generic
HOST_ARCH=x86_64
HOST_2ND_ARCH=x86
HOST_OS=linux
HOST_OS_EXTRA=Linux-5.4.0-100-generic-x86_64-Ubuntu-20.04.4-LTS
HOST_CROSS_OS=windows
HOST_CROSS_ARCH=x86
HOST_CROSS_2ND_ARCH=x86_64
HOST_BUILD_TYPE=release
BUILD_ID=SP1A.210812.016
OUT_DIR=out
PRODUCT_SOONG_NAMESPACES=device/generic/goldfish device/generic/goldfish-opengl hardware/google/camera hardware/google/camera/devices/EmulatedCamera device/generic/goldfish device/generic/goldfish-opengl
============================================

2. print_lunch_menu() 函数分析

这个函数的逻辑本身很简单,值得注意的是这个函数会执行 get_build_var 这个函数。

function print_lunch_menu()
{
    local uname=$(uname)
    local choices
    # 注意这里 get_build_var COMMON_LUNCH_CHOICES 的调用
    # 有关 get_build_var 的分析见下面对该函数的分析。
    choices=$(TARGET_BUILD_APPS= TARGET_PRODUCT= TARGET_BUILD_VARIANT= get_build_var COMMON_LUNCH_CHOICES 2>/dev/null)
    local ret=$?

    echo
    echo "You're building on" $uname
    echo

    if [ $ret -ne 0 ]
    then
        echo "Warning: Cannot display lunch menu."
        echo
        echo "Note: You can invoke lunch with an explicit target:"
        echo
        echo "  usage: lunch [target]" >&2
        echo
        return
    fi

    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
}

3. get_build_var 函数分析

get_build_var() 这个函数会编译生成 soong_ui 这个小程序,然后执行该程序,带入 --dumpvar-mode 选项以及 $1。所以调用 get_build_var COMMON_LUNCH_CHOICES 就等价于调用 build/soong/soong_ui.bash --dumpvar-mode COMMON_LUNCH_CHOICES,这个命令会打印出 $COMMON_LUNCH_CHOICES,即所有注册到 lunch 菜单中的 product 名字。

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
    # 下面这条指令会确保生成 ./out/soong_ui 并执行之
    (\cd $T; build/soong/soong_ui.bash --dumpvar-mode $1)
}

所以我们知道在 envsetup.sh 中有很多形如执行 get_build_var AAA 的代码, 相当于执行 ./out/soong_ui --dumpvar-mode AAA

有关 soong_ui 这个函数的分析,可以参考另外一篇笔记 《代码走读:对 soong_ui 的深入理解》

4. 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'
}

set_stuff_for_environment() 中最关键的函数是 setpaths,下面有一章节专门看一下。

其他代码,譬如 set_sequence_number() 的工作只是导出了 BUILD_ENV_SEQUENCE_NUMBER 这个环境变量。其他的工作是导出了 ANDROID_BUILD_TOPGCC_COLORS 这些环境变量。

5. setpaths() 函数分析

function setpaths()
{
    # 此处省略 ......
    # 下面的工作主要是会设置两个重要的环境变量
    # ANDROID_BUILD_PATHS
    # PATH
    # 此处省略 ......
    # out with the old
    if [ -n "$ANDROID_BUILD_PATHS" ] ; then
        # 这个语法很奇妙,作用是会将 $ANDROID_BUILD_PATHS 从 PATH 中删除
        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)

    # 获取 gcc 的工具链的版本,定义在 make/core/config.mk 中
    # 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
            ;;
        # 此处省略 ......
        *)
            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
    <b>// 至此得到工具的路径 ANDROID_TOOLCHAIN 和 ANDROID_TOOLCHAIN_2ND_ARCH 并导出</b>

    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:
    export ANDROID_BUILD_PATHS
    <b>// 更新 ANDROID_BUILD_PATHS,此时追加 ANDROID_TOOLCHAIN、</b>
    <b>// ANDROID_TOOLCHAIN_2ND_ARCH 和 ANDROID_DEV_SCRIPTS </b>

    # 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
    # 还在追加 ANDROID_BUILD_PATHS

    # 差不多 ANDROID_BUILD_PATHS 就绪了,添加 ANDROID_BUILD_PATHS 到 PATH
    export PATH=$ANDROID_BUILD_PATHS$PATH

    # 下面是其他的环境变量导出,和 ANDROID_BUILD_PATHS 已经没什么关系了
    # 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_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)

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

总结一下 setpaths 的工作, export 如下变量

  • PATH
  • TARGET_GCC_VERSION
  • ANDROID_TOOLCHAIN:这个是形如
  • ANDROID_TOOLCHAIN_2ND_ARCH
  • ANDROID_DEV_SCRIPTS: 形如 $T/development/scripts:$T/prebuilts/devtools/tools:$T/external/selinux/prebuilts/bin, 如果是在 Linux 上编译还会补上 :$T/prebuilts/misc/linux-x86/dtc:$T/prebuilts/misc/
  • ANDROID_EMULATOR_PREBUILTS
  • ANDROID_BUILD_PATHS: 这个路径是一个所有和 build 有关的路径的全集,包括:
    • ANDROID_BUILD_PATHS
    • ANDROID_TOOLCHAIN
    • ANDROID_TOOLCHAIN_2ND_ARCH
    • ANDROID_DEV_SCRIPTS
    • ANDROID_EMULATOR_PREBUILTS
    • ACLOUD_PATH
    • AIDEGEN_PATH
    • ATEST_PATH 注意:ANDROID_BUILD_PATHS 还会被添加到 PATH 中 下面是 python 相关路径
  • ANDROID_PYTHONPATH
  • PYTHONPATH 下面是 java 相关路径
  • ANDROID_JAVA_HOME
  • JAVA_HOME
  • ANDROID_JAVA_TOOLCHAIN
  • ANDROID_PRE_BUILD_PATHS
  • 将 ANDROID_PRE_BUILD_PATHS 加入 PATH 设置 aosp 的 out 路径
  • ANDROID_PRODUCT_OUT
  • OUT
  • ANDROID_HOST_OUT
  • ANDROID_HOST_OUT_TESTCASES
  • ANDROID_TARGET_OUT_TESTCASES

一个例子:参考 $T/out/soong.log

2ND_TARGET_GCC_VERSION
ANDROID_BUILD_PATHS /home/wangchen/ws/aosp/aosp-riscv/aosp/out/soong/host/linux-x86/bin:/home/wangchen/ws/aosp/aosp-riscv/aosp/out/host/linux-x86/bin
COMMON_LUNCH_CHOICES aosp_arm-eng aosp_arm64-eng aosp_blueline-userdebug aosp_bonito-userdebug aosp_car_arm-userdebug aosp_car_arm64-userdebug aosp_car_x86-userdebug aosp_car_x86_64-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_crosshatch-userdebug aosp_marlin-userdebug aosp_riscv64-eng aosp_sailfish-userdebug aosp_sargo-userdebug aosp_taimen-userdebug aosp_walleye-userdebug aosp_walleye_test-userdebug aosp_x86-eng aosp_x86_64-eng beagle_x15-userdebug fuchsia_arm64-eng fuchsia_x86_64-eng hikey-userdebug hikey64_only-userdebug hikey960-userdebug hikey960_tv-userdebug hikey_tv-userdebug ice910-userdebug m_e_arm-userdebug mini_emulator_arm64-userdebug mini_emulator_x86-userdebug mini_emulator_x86_64-userdebug poplar-eng poplar-user poplar-userdebug qemu_trusty_arm64-userdebug uml-userdebug
HOST_PREBUILT_TAG linux-x86
print
TARGET_ARCH riscv64
TARGET_BUILD_VARIANT eng
TARGET_DEVICE generic_riscv64
TARGET_GCC_VERSION 8.1
TARGET_PLATFORM_VERSION QP1A
TARGET_PRODUCT aosp_riscv64
ANDROID_GCC_PREBUILTS prebuilts/gcc/linux-x86
ANDROID_JAVA_HOME prebuilts/jdk/jdk9/linux-x86
ANDROID_JAVA_TOOLCHAIN prebuilts/jdk/jdk9/linux-x86/bin
ANDROID_PREBUILTS prebuilt/linux-x86
HOST_OUT out/host/linux-x86
HOST_OUT_TESTCASES out/host/linux-x86/testcases
print
PRODUCT_OUT out/target/product/generic_riscv64
TARGET_OUT_TESTCASES out/target/product/generic_riscv64/testcases
PLATFORM_VERSION_CODENAME REL
PLATFORM_VERSION 10
TARGET_PRODUCT aosp_riscv64
TARGET_BUILD_VARIANT eng
TARGET_BUILD_TYPE release
TARGET_BUILD_APPS
TARGET_ARCH riscv64
TARGET_ARCH_VARIANT riscv64
TARGET_CPU_VARIANT generic
TARGET_2ND_ARCH
TARGET_2ND_ARCH_VARIANT
TARGET_2ND_CPU_VARIANT
HOST_ARCH x86_64
HOST_2ND_ARCH x86
HOST_OS linux
HOST_OS_EXTRA Linux-4.15.0-144-generic-x86_64-Ubuntu-18.04.6-LTS
HOST_CROSS_OS windows
HOST_CROSS_ARCH x86
HOST_CROSS_2ND_ARCH x86_64
HOST_BUILD_TYPE release
BUILD_ID QP1A.191105.004
AUX_OS_VARIANT_LIST
TARGET_BUILD_PDK
PDK_FUSION_PLATFORM_ZIP
PRODUCT_SOONG_NAMESPACES