我們編譯Android源碼的步驟是什么?
全編:
- source build/envsetup.sh
- lunch 特定的分支
- make -j8 2>&1|tee build.log
單編:- mm frameworks/base/
- mmm packages/apps/Settings/
基礎(chǔ)知識(shí):正則霎箍,bash shell
source build/envsetup.sh
source是Linux 內(nèi)部命令琼蚯,這里不多講解沈跨,可以理解為執(zhí)行界斜;我們直接看build/envsetup.sh命令慷彤;
function hmm() {
cat <<EOF
Invoke ". build/envsetup.sh" from your shell to add the following functions to your environment:
- lunch: lunch <product_name>-<build_variant>
- tapas: tapas [<App1> <App2> ...] [arm|x86|mips|armv5|arm64|x86_64|mips64] [eng|userdebug|user]
- croot: Changes directory to the top of the tree.
- m: Makes from the top of the tree.
- mm: Builds all of the modules in the current directory, but not their dependencies.
- mmm: Builds all of the modules in the supplied directories, but not their dependencies.
To limit the modules being built use the syntax: mmm dir/:target1,target2.
- mma: Builds all of the modules in the current directory, and their dependencies.
- mmma: Builds all of the modules in the supplied directories, and their dependencies.
- cgrep: Greps on all local C/C++ files.
- ggrep: Greps on all local Gradle files.
- jgrep: Greps on all local Java files.
- resgrep: Greps on all local res/*.xml files.
- sgrep: Greps on all local source files.
- godir: Go to the directory containing a file.
Look at the source to view more functions. The complete list is:
EOF
T=$(gettop)
local A
A=""
for i in `cat $T/build/envsetup.sh | sed -n "/^[ \t]*function /s/function \([a-z_]*\).*/\1/p" | sort | uniq`; do
A="$A $i"
done
echo $A
}
......
文件以function開(kāi)始获搏,function就是一個(gè)方法,cat <<EOP就是把EOF后面到下一個(gè)EOF前面的內(nèi)容當(dāng)做一個(gè)文件輸出到標(biāo)準(zhǔn)輸出如孝。
當(dāng)我們?cè)赟hell 中執(zhí)行source build/envsetup.sh后宪哩,執(zhí)行hmm命令看看,截圖如下:
function也對(duì)應(yīng)于命令第晰,這里暫時(shí)只舉例了一個(gè)锁孟,有需要再列舉;回到我們我們的主線茁瘦,從source build/envsetup.sh開(kāi)始品抽;
1-1:source build/envsetup.sh
source build/envsetup.sh和". build/envsetup.sh"等價(jià),就是執(zhí)行這個(gè)sh腳本腹躁,這個(gè)腳本中全是方法桑包,方法是被調(diào)用的,所以程序肯定從方法外的程序開(kāi)始執(zhí)行:
......
# Clear this variable. It will be built up again when the vendorsetup.sh
# files are included at the end of this file.
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)
}
# add the default one here
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
......
unset LUNCH_MENU_CHOICES 刪除LUNCH_MENU_CHOICES纺非,
add_lunch_combo()的功能是向LUNCH_MENU_CHOICES變量中添加元素哑了,
相當(dāng)于初始化LUNCH_MENU_CHOICES;
這里執(zhí)行完成后LUNCH_MENU_CHOICES數(shù)組中就有了上述的元素值烧颖;
# Execute the contents of any vendorsetup.sh files we can find.
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`
do
echo "including $f"
. $f
done
unset f
shell 中反引號(hào)的作用:將反引號(hào)中的內(nèi)容當(dāng)做shell 命令來(lái)執(zhí)行弱左;
反引號(hào)中使用test -d 判斷device目錄是否存在,如果存在則利用find在device中查找vendorsetup.sh腳本炕淮,查找深度為4級(jí)目錄拆火;"2> /dev/null"是重定向等級(jí)為2的輸出到/dev/null,2代表錯(cuò)誤涂圆,即不輸出錯(cuò)誤们镜,sort 是排序; echo "including $f"將找到的文件輸出到標(biāo)準(zhǔn)輸出润歉,". $f"和source $f等價(jià)模狭,即執(zhí)行找到的vendorsetup.sh;
我在項(xiàng)目中執(zhí)行source build/envsetup.sh踩衩,截圖如下:
上述途中的including 項(xiàng)就是我們找到的vendorsetup.sh嚼鹉,這里我打開(kāi)第一個(gè)device/samsung/manta/vendorsetup.sh腳本:
add_lunch_combo aosp_manta-userdebug
這里只有這一句話,調(diào)用add_lunch_combo 方法驱富,即LUNCH_MENU_CHOICES數(shù)組中添加元素锚赤;上述圖中的vendorsetup.sh 在不同的目錄下,不同的vendorsetup.sh中內(nèi)容還不同褐鸥,比如asus,hts,samsung等线脚,代表不同公司不同項(xiàng)目;add_lunch_combo的參數(shù)由兩部分組成:product-variant,aosp_manta-userdebug代表product=aosp_manta酒贬,variant=userdebug又憨;
所有source build/envsetup.sh腳本執(zhí)行結(jié)束后翠霍,即初始化LUNCH_MENU_CHOICES數(shù)組完成锭吨;接下來(lái)我們一般執(zhí)行l(wèi)unch命令:
lunch
截圖如下:
這里顯示的內(nèi)容就是前面source build/envsetup.sh調(diào)用add_lunch_combo初始化的LUNCH_MENU_CHOICES數(shù)組內(nèi)容;我們看看具體代碼是如何顯示的呢寒匙?
function print_lunch_menu()
{
local uname=$(uname)
echo
echo "You're building on" $uname
echo
echo "Lunch menu... pick a combo:"
local i=1
local choice
for choice in ${LUNCH_MENU_CHOICES[@]}
do
echo " $i. $choice"
i=$(($i+1))
done
echo
}
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=
#這里是判斷answer變量長(zhǎng)度是否為0
if [ -z "$answer" ]
then
#如果長(zhǎng)度為0零如,則 selection=aosp_arm-eng
selection=aosp_arm-eng
#這里是通過(guò)echo -n輸出answer值,輸出沒(méi)輸出到屏幕锄弱,而是通過(guò)管道傳遞給grep -q -e "^[0-9][0-9]*$"考蕾,
#即判斷answer是不是以兩個(gè)數(shù)字開(kāi)頭,是則返回true
elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")
then
#如果answer的值是否小于等于數(shù)組LUNCH_MENU_CHOICES個(gè)數(shù)
if [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]
then
#如果上述條件都成立会宪,將LUNCH_MENU_CHOICES數(shù)組的answer-1位數(shù)據(jù)賦值給selection
selection=${LUNCH_MENU_CHOICES[$(($answer-1))]}
fi
#方括號(hào)中的^表示排除, 也就是不是這些字符的字符此例中的[^\-]表示一個(gè)不是'-'的字符,
#因?yàn)?-'在方括號(hào)中有表示范圍的意思,所以前面加了'\'來(lái)轉(zhuǎn)義成一個(gè)普通字#符'-',
#(但在此處轉(zhuǎn)義符'\'多余:此例中'-'明顯不是表示范圍,作者低估了正則引擎的理解能力)
#全表達(dá)式意思是:字符串開(kāi)頭是一個(gè)不為'-'的字符,后面跟0個(gè)到多個(gè)不為'-'的字符,
#再后面是一個(gè)'-',再后面又是一個(gè)不為'-'的字符,后面跟0個(gè)到多個(gè)不為'-'的字符肖卧;
#這里扯了這么多,其實(shí)這里就是期望得到product-varient的形式掸鹅。也就是我們之前
#使用add_lunch_combo添加的那些字符串的格式塞帐。比如:aosp_arm-eng,
#product就是aosp_arm,varient就是eng.中間的-是必須的
elif (echo -n $answer | grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$")
then
selection=$answer
fi
#如果selecetion長(zhǎng)度為0巍沙,則不合法葵姥,輸出提示
if [ -z "$selection" ]
then
echo
echo "Invalid lunch combo: $answer"
return 1
fi
export TARGET_BUILD_APPS=
#利用sed替換selection中"-eng,-user,-userdebug"為空,即去掉"-"后面部分
local product=$(echo -n $selection | sed -e "s/-.*$//")
#j檢查product是否合法
check_product $product
#根據(jù)上面product是否合法繼續(xù)執(zhí)行下面操作句携,如果返回值不等于0就出現(xiàn)問(wèn)題了
if [ $? -ne 0 ]
then
echo
echo "** Don't have a product spec for: '$product'"
echo "** Do you have the right repo manifest?"
product=
fi
#利用sed替換selection中product部分榔幸,即去掉"-"前面部分
local variant=$(echo -n $selection | sed -e "s/^[^\-]*-//")
#檢查variant
check_variant $variant
#如果返回值不為0就出現(xiàn)問(wèn)題了
if [ $? -ne 0 ]
then
echo
echo "** Invalid variant: '$variant'"
echo "** Must be one of ${VARIANT_CHOICES[@]}"
variant=
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
}
這個(gè)函數(shù)首先判斷有沒(méi)有傳入?yún)?shù),如果傳入了就把值賦給answer這個(gè)變量矮嫉,如果沒(méi)有傳參數(shù)削咆,也就是說(shuō)知識(shí)執(zhí)行了lunch,那么就會(huì)調(diào)用fprint_lunch_menu函數(shù)顯示一份菜單出來(lái)蠢笋,顯示出來(lái)后拨齐,接受用戶的輸入。
fprint_lunch_menu 就是利用for循環(huán)將LUNCH_MENU_CHOICES中的所有值都打印到屏幕上挺尿;接下來(lái)就是校驗(yàn)answer變量奏黑,看看answer是否合法;校驗(yàn)過(guò)程見(jiàn)上述代碼注釋编矾,校驗(yàn)完成就是從answer變量中獲取product值熟史,后去后再調(diào)用check_product檢驗(yàn)product的合法性;
# check to see if the supplied product is one we can build
function check_product()
{
#獲取源碼跟目錄
T=$(gettop)
if [ ! "$T" ]; then
echo "Couldn't locate the top of the tree. Try setting TOP." >&2
return
fi
TARGET_PRODUCT=$1 \
TARGET_BUILD_VARIANT= \
TARGET_BUILD_TYPE= \
TARGET_BUILD_APPS= \
get_build_var TARGET_DEVICE > /dev/null
# hide successful answers, but allow the errors to show
}
上述代碼會(huì)調(diào)用get_build_var方法窄俏,這里我們可以在shell中執(zhí)行這個(gè)命令看看結(jié)果:輸出了generic蹂匹,具體來(lái)看看代碼;
# Get the exact value of a build variable.
function get_build_var()
{
#獲取源碼跟目錄
T=$(gettop)
if [ ! "$T" ]; then
echo "Couldn't locate the top of the tree. Try setting TOP." >&2
return
fi
(\cd $T; CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \
command make --no-print-directory -f build/core/config.mk dumpvar-$1)
}
get_build_var 很簡(jiǎn)單凹蜈,就是執(zhí)行make --no-print-directory -f build/core/config.mk dumpvar-$1限寞,$1為T(mén)ARGET_DEVICE忍啸,這里傳遞的完整參數(shù)為dumpvar-TARGET_DEVICE,config.mk會(huì)include dumpvar.mk,這個(gè)文件中會(huì)提取我們傳入的dumpvar-TARGET_DEVICE變量中的TARGET_DEVICE履植,然后打印$(TARGET_DEVICE)计雌。
add_lunch_combo添加的字符串的格式為product-variant,檢查完了product接下來(lái)就會(huì)檢查variant玫霎;variant必須是(user userdebug eng)中的一種凿滤;
VARIANT_CHOICES=(user userdebug eng)
# check to see if the supplied variant is valid
function check_variant()
{
for v in ${VARIANT_CHOICES[@]}
do
if [ "$v" = "$1" ]
then
return 0
fi
done
return 1
}
各種校驗(yàn)結(jié)束后會(huì)調(diào)用
#product和variant都不能為空,為空就是有問(wèn)題
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
上述代碼就是就前面的product和variant變量賦值給全局變量并且導(dǎo)出為環(huán)境變量庶近。以后的需要用到這幾個(gè)變量就是這里賦值的翁脆;set_stuff_for_environment 函數(shù)還會(huì)設(shè)置一些變量:
function set_stuff_for_environment()
{
settitle
set_java_home
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_java_home會(huì)檢查導(dǎo)出JAVA_HOME這個(gè)環(huán)境變量,這個(gè)環(huán)境變量就是JDK所在路徑鼻种,setpaths函數(shù)會(huì)給PATH環(huán)境變量增加編譯Android需要的一些路徑反番。最后lunch調(diào)用了 printconfig函數(shù),這個(gè)函數(shù)打印出了配置信息叉钥。
總結(jié):source build/envsetup.sh會(huì)調(diào)用add_lunch_combo函數(shù)添加編譯信息罢缸,同時(shí)還會(huì)查找/device和/vendor下的vendorsetup.sh文件,查找深度為4級(jí)目錄沼侣,找到后就執(zhí)行它祖能,它里面至少會(huì)有這么一行:add_lunch_combo xxxx,繼續(xù)添加編譯信息。lunch函數(shù)則會(huì)打印出所有的單板信息供你選擇蛾洛,你輸入選擇后养铸,luch命令會(huì)對(duì)你的選擇做一系列檢測(cè),并從中提取出product和varient轧膘,并最終導(dǎo)出這些信息钞螟,供正式編譯的時(shí)候使用。