build/envsetup.sh分析

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 2
1 + 2 = 3
$ add 1 3
1 + 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-eng
add_lunch_combo aosp_arm64-eng
add_lunch_combo aosp_mips-eng
add_lunch_combo aosp_mips64-eng
add_lunch_combo aosp_x86-eng
add_lunch_combo aosp_x86_64-eng

...//省略一堆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"
            ;;
    esac
fi

# 执行所有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"
    . $f
done
unset 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 函数的流程图

build/envsetup.sh分析_第1张图片

具体代码如下

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_CHOICES
function add_lunch_combo()
{
    local new_combo=$1
    local c
    for c in ${LUNCH_MENU_CHOICES[@]} ; do
        if [ "$new_combo" = "$c" ] ; then
            return
        fi
    done
    LUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]} $new_combo)
}

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命令的补全

你可能感兴趣的:(Android底层)