build/envsetup.sh分析

来源:互联网 发布:2013年网络炒作事件 编辑:程序博客网 时间:2024/04/30 03:52

build/envsetup.sh分析

1. 概述

通常我们是编译Android源码前要先执行

$ source build/envsetup.sh

该脚本执行后,我们就可以执行lunch等命令。怎么会这么神奇,执行了shell脚本就可以多出来几个命令了?

在研究lunch怎么出现的之前,我们先回顾一个关于shell的小知识

1.1 小知识:

当脚本中定义有函数。那么当脚本被执行后,不管脚本中定义的函数没有执行,都可以在命令行通过该函数名去调用该shell函数。

1.2 小实验

新建如下脚本,名字随意

#!/bin/bash# Author: wanghan# Created Time : Mon 15 May 2017 04:00:45 PM CST# File Name: add.sh# Description:add(){    local sum    sum=$(( $1 + $2 ))    echo "$1 + $2 = $sum"}

执行该脚本

$ source add.sh

像命令一样调用add函数

$ add 1 21 + 2 = 3$ add 1 31 + 3 = 4

1.3 lunch等命令出现的原因分析

由此我们可以知道,正是应为执行了”source build/envsetup.sh”命令,我们可以使用lunch等命令(实际上是shell函数)。

同样,我们也可以在Makefile中调用build/envsetup.sh中的函数如gettop等。

因此,build/envsetup.sh文件存在的意义就是,设置一些环境变量和shell函数为后续的编译工作做准备

2. 整体结构分析

envsetup.sh文件的整体结构体很清晰,主体是由shell函数构成。

2.1 整体代码布局如下

下面的伪代码中省略了所有的shell函数,只留下了envsetup.sh文件中被真正执行了的代码

...//省略一堆shell函数#设定三种编译的版本VARIANT_CHOICES=(user userdebug eng)...//省略一堆shell函数# 添加一些默认的lunch选项add_lunch_combo aosp_arm-engadd_lunch_combo aosp_arm64-engadd_lunch_combo aosp_mips-engadd_lunch_combo aosp_mips64-engadd_lunch_combo aosp_x86-engadd_lunch_combo aosp_x86_64-eng...//省略一堆shell函数# 这行代码前定义了lunch和_lunch函数,这行代码的意思是通过_lunch函数实现lunch命令的自动补全功能complete -F _lunch lunch...//省略一堆shell函数# 如果终端使用的shell使用的是Bash,则什么也不做,反之则警告if [ "x$SHELL" != "x/bin/bash" ]; then    case `ps -o command -p $$` in        *bash*)            ;;        *)            echo "WARNING: Only bash is supported, use of other shell would lead to erroneous results"            ;;    esacfi# 执行所有device、vendor以及product路径下所能找到的所有vendorsetup.sh文件for f in `test -d device && find -L device -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort` \         `test -d vendor && find -L vendor -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort` \         `test -d product && find -L product -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort`do    echo "including $f"    . $fdoneunset f# addcompletions是envsetup.sh文件定义的一个shell函数addcompletions # 这一行也是整个文件的最后一行

2.2 source build/envsetup.sh 执行后发生了什么?

通过2.1小节的代码,我们可以知道 ++source build/envsetup.sh++ 命令执行后,做了下面几件事。

  1. 系统先是加载了一些shell函数。这些shell函数可以在命令行,像普通命令一样被调用。也可以在别的shell脚本中被使用。

  2. 定义了VARIANT_CHOICES变量,设定三种编译的版本user、userdebug和eng

  3. 添加一些默认的lunch选项如aosp_arm-eng、aosp_arm64-eng、aosp_mips-eng、aosp_mips64-eng、aosp_x86-eng、aosp_x86_64-eng

  4. 定义了lunch函数,并实现了lunch命令的补全(定义_lucnh函数,使用complete 命令)

  5. 判断终端使用的shell使用的是不是Bash。如果是则什么也不做,如果不是就打印警告信息

  6. 执行所有源码根目录下的device、vendor以及product文件夹下所能找到的所有vendorsetup.sh文件

  7. 执行addcompletions函数(实现Linux终端下adb命令的补全)

2.3 具体函数功能分析

2.3.1 lunch 函数

lunch 函数的流程图

lunch

具体代码如下

function lunch(){    local answer    if [ "$1" ] ; then        answer=$1    else        print_lunch_menu        echo -n "Which would you like? [aosp_arm-eng] "        read answer    fi    local selection=    if [ -z "$answer" ]    then        selection=aosp_arm-eng    elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")    then        if [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]        then            selection=${LUNCH_MENU_CHOICES[$(($answer-1))]}        fi    elif (echo -n $answer | grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$")    then        selection=$answer    fi    if [ -z "$selection" ]    then        echo        echo "Invalid lunch combo: $answer"        return 1    fi    export TARGET_BUILD_APPS=    local variant=$(echo -n $selection | sed -e "s/^[^\-]*-//")    check_variant $variant    if [ $? -ne 0 ]    then        echo        echo "** Invalid variant: '$variant'"        echo "** Must be one of ${VARIANT_CHOICES[@]}"        variant=    fi    local product=$(echo -n $selection | sed -e "s/-.*$//")    TARGET_PRODUCT=$product \    TARGET_BUILD_VARIANT=$variant \    build_build_var_cache    if [ $? -ne 0 ]    then        echo        echo "** Don't have a product spec for: '$product'"        echo "** Do you have the right repo manifest?"        product=    fi    if [ -z "$product" -o -z "$variant" ]    then        echo        return 1    fi    export TARGET_PRODUCT=$product    export TARGET_BUILD_VARIANT=$variant    export TARGET_BUILD_TYPE=release    echo    set_stuff_for_environment //设置环境    printconfig //打印环境信息    destroy_build_var_cache //销毁编译变量缓存}

2.3.2 _lunch函数

实现lunch命令的补全的步骤:

  1. 首先定义了lunch函数

  2. 然后定义了_lunch函数

  3. 使用complete命令

# Tab completion for lunch.function _lunch(){    local cur prev opts    COMPREPLY=()    cur="${COMP_WORDS[COMP_CWORD]}"    prev="${COMP_WORDS[COMP_CWORD-1]}"    COMPREPLY=( $(compgen -W "${LUNCH_MENU_CHOICES[*]}" -- ${cur}) )    return 0}complete -F _lunch lunch

++complete -F _lunch lunch++ 是上面代码中最关键的一行。
其实名字是不是lunch都没关系,关键是有这一行代码。
当bash在遇到lunch这个词的时候,会调用_lunch函数。
该函数会传入三个参数:要补全的命令名、当前的光标所在的词、当前光标所在的词的前一个词。
补全的结果需要存储到COMPREPLY变量中,以待bash获取。

2.3.3 add_lunch_combo函数

# 清除这个变量。它会在vendorsetup.sh文件中重新定义一遍# 注:文件(vendorsetup.sh)在本文件的末尾被包含(included)见2.2小节,第5步unset LUNCH_MENU_CHOICESfunction add_lunch_combo(){    local new_combo=$1    local c    for c in ${LUNCH_MENU_CHOICES[@]} ; do        if [ "$new_combo" = "$c" ] ; then            return        fi    done    LUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]} $new_combo)}

2.3.4 addcompletions函数

具体代码:

# 如果shell使用的是Bash且版本大于3,则include"sdk/bash_completion"路径下所有以小写字母开头的.bash文件function addcompletions(){    local T dir f    # Keep us from trying to run in something that isn't bash.    if [ -z "${BASH_VERSION}" ]; then        return    fi    # Keep us from trying to run in bash that's too old.    if [ ${BASH_VERSINFO[0]} -lt 3 ]; then        return    fi    dir="sdk/bash_completion"    if [ -d ${dir} ]; then        for f in `/bin/ls ${dir}/[a-z]*.bash 2> /dev/null`; do            echo "including $f"            . $f        done    fi}

执行过程:

  1. 如果shell使用的是Bash且版本大于3,则include”sdk/bash_completion”路径下所有以小写字母开头的.bash文件

  2. 主要是执行了adb.bash文件,来adb命令的补全