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++ 命令執行後,做了下面幾件事。
系統先是加載了一些shell函數。這些shell函數可以在命令行,像普通命令一樣被調用。也可以在別的shell腳本中被使用。
定義了VARIANT_CHOICES變量,設定三種編譯的版本user、userdebug和eng
添加一些默認的lunch選項如aosp_arm-eng、aosp_arm64-eng、aosp_mips-eng、aosp_mips64-eng、aosp_x86-eng、aosp_x86_64-eng
定義了lunch函數,並實現了lunch命令的補全(定義_lucnh函數,使用complete 命令)
判斷終端使用的shell使用的是不是Bash。如果是則什麼也不做,如果不是就打印警告信息
執行所有源碼根目錄下的device、vendor以及product文件夾下所能找到的所有vendorsetup.sh文件
執行addcompletions函數(實現Linux終端下adb命令的補全)
2.3 具體函數功能分析
2.3.1 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命令的補全的步驟:
首先定義了lunch函數
然後定義了_lunch函數
使用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
}
執行過程:
如果shell使用的是Bash且版本大於3,則include”sdk/bash_completion”路徑下所有以小寫字母開頭的.bash文件
主要是執行了adb.bash文件,來adb命令的補全