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 函數的流程圖

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

發佈了68 篇原創文章 · 獲贊 41 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章