Android編譯詳解之lunch命令
阿新 • • 發佈:2019-01-22
Android的優勢就在於其開源,手機和平板生產商可以根據自己的硬體進行個性定製自己的手機產品,
如小米,LePhone,M9等,因此,在我們在對Android的原始碼進行定製的時候,很有必要了解下,Android的編譯過程。
如果你從來沒有做過Android程式碼的編譯,那麼最官方的編譯過程就是檢視Android的官方網站:
http://source.android.com/source/building.html
但是,這兒只是告訴你瞭如何去編譯一個通用的系統,並沒有詳細告訴你細節,我們跟著編譯過程來了解下
+-------------------------------------------------------------------------------------------------------------+
本文使用Android版本為2.1,採用開發板為華清遠見研發的FS_S5PC100 A8開發板。
+-------------------------------------------------------------------------------------------------------------+
按照google給出的編譯步驟如下:
1> source build/envsetup.sh:載入命令
2> lunch:選擇平臺編譯選項
3> make:執行編譯
我們按照編譯步驟來分析編譯過程的細節,最終新增自己的平臺編譯選項。
1. source build/envsetup.sh
envsetup.sh裡的主要命令如下:
function help() # 顯示幫助資訊
function get_abs_build_var() # 獲取絕對變數
function get_build_var() # 獲取絕對變數
function check_product() # 檢查product
function check_variant() # 檢查變數
function setpaths() # 設定檔案路徑
function printconfig() # 列印配置
function set_stuff_for_environment() # 設定環境變數
function set_sequence_number() # 設定序號
function settitle() # 設定標題
function choosetype() # 設定type
function chooseproduct() # 設定product
function choosevariant() # 設定variant
function tapas() # 功能同choosecombo
function choosecombo() # 設定編譯引數
function add_lunch_combo() # 新增lunch專案
function print_lunch_menu() # 列印lunch列表
function lunch() # 配置lunch
function m() # make from top
function findmakefile() # 查詢makefile
function mm() # make from current directory
function mmm() # make the supplied directories
function croot() # 回到根目錄
function cproj()
function pid()
function systemstack()
function gdbclient()
function jgrep() # 查詢java檔案
function cgrep() # 查詢c/cpp檔案
function resgrep()
function tracedmdump()
function runhat()
function getbugreports()
function startviewserver()
function stopviewserver()
function isviewserverstarted()
function smoketest()
function runtest()
function godir () # 跳到指定目錄 405
# add_lunch_combo函式被多次呼叫,就是它來新增Android編譯選項
# Clear this variable. It will be built up again when the vendorsetup.sh
406 # files are included at the end of this file.
# 清空LUNCH_MENU_CHOICES變數,用來存在編譯選項
407 unset LUNCH_MENU_CHOICES
408 function add_lunch_combo()
409 {
410 local new_combo=$1 # 獲得add_lunch_combo被呼叫時的引數
411 local c # 依次遍歷LUNCH_MENU_CHOICES裡的值,其實該函式第一次呼叫時,該值為空
412 for c in ${LUNCH_MENU_CHOICES[@]} ; do
413 if [ "$new_combo" = "$c" ] ; then # 如果引數裡的值已經存在於LUNCH_MENU_CHOICES變數裡,則返回
414 return
415 fi
416 done # 如果引數的值不存在,則新增到LUNCH_MENU_CHOICES變數裡
417 LUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]} $new_combo)
418 }
# 這是系統自動增加了一個預設的編譯項 generic-eng
420 # add the default one here
421 add_lunch_combo generic-eng # 呼叫上面的add_lunch_combo函式,將generic-eng作為引數傳遞過去
422
423 # if we're on linux, add the simulator. There is a special case
424 # in lunch to deal with the simulator
425 if [ "$(uname)" = "Linux" ] ; then
426 add_lunch_combo simulator
427 fi
# 下面的程式碼很重要,它要從vendor目錄下查詢vendorsetup.sh檔案,如果查到了,就載入它
1037 # Execute the contents of any vendorsetup.sh files we can find.
1038 for f in `/bin/ls vendorbuild/vendorsetup.sh 2> /dev/null`
1039 do
1040 echo "including $f"
1041 . $f # 執行找到的指令碼,其實裡面就是廠商自己定義的編譯選項
1042 done
1043 unset f
envsetup.sh其主要作用如下:
1. 載入了編譯時使用到的函式命令,如:help,lunch,m,mm,mmm等
2. 添加了兩個編譯選項:generic-eng和simulator,這兩個選項是系統預設選項
3. 查詢vendor/<-廠商目錄>/和vendor/<廠商目錄>/build/目錄下的vendorsetup.sh,如果存在的話,載入執行它,新增廠商自己定義產品的編譯選項
根據上面的內容,可以推測出,如果要想定義自己的產品編譯項,簡單的辦法是直接在envsetup.sh最後,
#mkdir vendor/farsight/
#touch vendor/farsight/vendorsetup.sh
#echo "add_lunch_combo fs100-eng" > vendor/farsight/vendorsetup.sh
這樣,當我們在執行source build/envsetup.sh命令的時候,可以在shell上看到下面的資訊:
including vendor/farsight/vendorsetup.sh
2. 按照android官網的步驟,開始執行lunch full-eng
當然如果你按上述命令執行,它編譯的還是通用的eng版本系統,不是我們個性系統,我們可以執行lunch命令,它會打印出一個選擇選單,列出可用的編譯選項
如果你按照第一步中添加了vendorsetup.sh那麼,你的選項中會出現:
You're building on Linux
generic-eng simulator fs100-eng
Lunch menu... pick a combo:
1. generic-eng
2. simulator
3. fs100-eng
其中第3項是我們自己新增的編譯項。
lunch命令是envsetup.sh裡定義的一個命令,用來讓使用者選擇編譯項,來定義Product和編譯過程中用到的全域性變數。
我們一直沒有說明前面的fs100-eng是什麼意思,現在來說明下,fs100是我定義的產品的名字,eng是產品的編譯型別,除了eng外,還有user, userdebug,分別表示:
eng: 工程機,
user:終端使用者機
userdebug:除錯測試機
tests:測試機
由此可見,除了eng和user外,另外兩個一般不能交給終端使用者的,記得m8出來的時候,先放出了一部分eng工程機,然後出來了user機之後,可以用工程機換。
那麼這四個型別是幹什麼用的呢?其實,在main.mk裡有說明,在Android的原始碼裡,每一個目標(也可以看成工程)目錄都有一個Android.mk的makefile,每個目標的Android.mk中有一個型別宣告:
LOCAL_MODULE_TAGS,這個TAGS就是用來指定,當前的目標編譯完了屬於哪個分類裡。
PS:Android.mk和Linux裡的makefile不太一樣,它是Android編譯系統自己定義的一個makefile來方便編譯成:c,c++的動態、靜態庫或可執行程式,或java庫或android的程式,好了,我們來分析下lunch命令幹了什麼?
function lunch()
{
local answer
if [ "$1" ] ; then
# lunch後面直接帶引數
answer=$1
else
# lunch後面不帶引數,則列印處所有的target product和variant選單提供使用者選擇
print_lunch_menu
echo -n "Which would you like? [generic-eng] "
read answer
fi
local selection=
if [ -z "$answer" ]
then
# 如果使用者在選單中沒有選擇,直接回車,則為系統預設的generic-eng
selection=generic-eng
elif [ "$answer" = "simulator" ]
then
# 如果是模擬器
selection=simulator
elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")
then
# 如果answer是選擇選單的數字,則獲取該數字對應的字串
if [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]
then
selection=${LUNCH_MENU_CHOICES[$(($answer-$_arrayoffset))]}
fi
# 如果 answer字串匹配 *-*模式(*的開頭不能為-)
elif (echo -n $answer | grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$")
then
selection=$answer
fi
if [ -z "$selection" ]
then
echo
echo "Invalid lunch combo: $answer"
return 1
fi
# special case the simulator
if [ "$selection" = "simulator" ]
then
# 模擬器模式
export TARGET_PRODUCT=sim
export TARGET_BUILD_VARIANT=eng
export TARGET_SIMULATOR=true
export TARGET_BUILD_TYPE=debug
else
# 將 product-variant模式中的product分離出來
local product=$(echo -n $selection | sed -e "s/-.*$//")
# 檢查之,呼叫關係 check_product()->get_build_var()->build/core/config.mk比較羅嗦,不展開了
check_product $product
if [ $? -ne 0 ]
then
echo
echo "** Don't have a product spec for: '$product'"
echo "** Do you have the right repo manifest?"
product=
fi
# 將 product-variant模式中的variant分離出來
local variant=$(echo -n $selection | sed -e "s/^[^\-]*-//")
# 檢查之,看看是否在 (user userdebug eng) 範圍內
check_variant $variant
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_SIMULATOR=false
export TARGET_BUILD_TYPE=release
fi # !simulator
echo
# 設定到環境變數,比較多,不再一一列出,最簡單的方法 set >env.txt 可獲得
set_stuff_for_environment
# 列印一些主要的變數, 呼叫關係 printconfig()->get_build_var()->build/core/config.mk-
>build/core/envsetup.mk 比較羅嗦,不展開了
printconfig
}
由上面分析可知,lunch命令可以帶引數和不帶引數,最終匯出一些重要的環境變數,從而影響編譯系統的編譯結果。匯出的變數如下(以實際執行情況為例)
TARGET_PRODUCT=fs100
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR=false
如小米,LePhone,M9等,因此,在我們在對Android的原始碼進行定製的時候,很有必要了解下,Android的編譯過程。
如果你從來沒有做過Android程式碼的編譯,那麼最官方的編譯過程就是檢視Android的官方網站:
http://source.android.com/source/building.html
但是,這兒只是告訴你瞭如何去編譯一個通用的系統,並沒有詳細告訴你細節,我們跟著編譯過程來了解下
+-------------------------------------------------------------------------------------------------------------+
本文使用Android版本為2.1,採用開發板為華清遠見研發的FS_S5PC100 A8開發板。
+-------------------------------------------------------------------------------------------------------------+
按照google給出的編譯步驟如下:
1> source build/envsetup.sh:載入命令
2> lunch:選擇平臺編譯選項
3> make:執行編譯
我們按照編譯步驟來分析編譯過程的細節,最終新增自己的平臺編譯選項。
1. source build/envsetup.sh
這個命令是用來將envsetup.sh裡的所有用到的命令載入到環境變數裡去,我們來分析下它。
envsetup.sh裡的主要命令如下:
function help() # 顯示幫助資訊
function get_abs_build_var() # 獲取絕對變數
function get_build_var() # 獲取絕對變數
function check_product() # 檢查product
function check_variant() # 檢查變數
function setpaths() # 設定檔案路徑
function printconfig() # 列印配置
function set_stuff_for_environment() # 設定環境變數
function set_sequence_number() # 設定序號
function settitle() # 設定標題
function choosetype() # 設定type
function chooseproduct() # 設定product
function choosevariant() # 設定variant
function tapas() # 功能同choosecombo
function choosecombo() # 設定編譯引數
function add_lunch_combo() # 新增lunch專案
function print_lunch_menu() # 列印lunch列表
function lunch() # 配置lunch
function m() # make from top
function findmakefile() # 查詢makefile
function mm() # make from current directory
function mmm() # make the supplied directories
function croot() # 回到根目錄
function cproj()
function pid()
function systemstack()
function gdbclient()
function jgrep() # 查詢java檔案
function cgrep() # 查詢c/cpp檔案
function resgrep()
function tracedmdump()
function runhat()
function getbugreports()
function startviewserver()
function stopviewserver()
function isviewserverstarted()
function smoketest()
function runtest()
function godir () # 跳到指定目錄 405
# add_lunch_combo函式被多次呼叫,就是它來新增Android編譯選項
# Clear this variable. It will be built up again when the vendorsetup.sh
406 # files are included at the end of this file.
# 清空LUNCH_MENU_CHOICES變數,用來存在編譯選項
407 unset LUNCH_MENU_CHOICES
408 function add_lunch_combo()
409 {
410 local new_combo=$1 # 獲得add_lunch_combo被呼叫時的引數
411 local c # 依次遍歷LUNCH_MENU_CHOICES裡的值,其實該函式第一次呼叫時,該值為空
412 for c in ${LUNCH_MENU_CHOICES[@]} ; do
413 if [ "$new_combo" = "$c" ] ; then # 如果引數裡的值已經存在於LUNCH_MENU_CHOICES變數裡,則返回
414 return
415 fi
416 done # 如果引數的值不存在,則新增到LUNCH_MENU_CHOICES變數裡
417 LUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]} $new_combo)
418 }
# 這是系統自動增加了一個預設的編譯項 generic-eng
420 # add the default one here
421 add_lunch_combo generic-eng # 呼叫上面的add_lunch_combo函式,將generic-eng作為引數傳遞過去
422
423 # if we're on linux, add the simulator. There is a special case
424 # in lunch to deal with the simulator
425 if [ "$(uname)" = "Linux" ] ; then
426 add_lunch_combo simulator
427 fi
# 下面的程式碼很重要,它要從vendor目錄下查詢vendorsetup.sh檔案,如果查到了,就載入它
1037 # Execute the contents of any vendorsetup.sh files we can find.
1038 for f in `/bin/ls vendorbuild/vendorsetup.sh 2> /dev/null`
1039 do
1040 echo "including $f"
1041 . $f # 執行找到的指令碼,其實裡面就是廠商自己定義的編譯選項
1042 done
1043 unset f
envsetup.sh其主要作用如下:
1. 載入了編譯時使用到的函式命令,如:help,lunch,m,mm,mmm等
2. 添加了兩個編譯選項:generic-eng和simulator,這兩個選項是系統預設選項
3. 查詢vendor/<-廠商目錄>/和vendor/<廠商目錄>/build/目錄下的vendorsetup.sh,如果存在的話,載入執行它,新增廠商自己定義產品的編譯選項
其實,上述第3條是向編譯系統添加了廠商自己定義產品的編譯選項,裡面的程式碼就是:add_lunch_combo xxx-xxx。
根據上面的內容,可以推測出,如果要想定義自己的產品編譯項,簡單的辦法是直接在envsetup.sh最後,
新增上add_lunch_combo myProduct-eng,當然這麼做,不太符合上面程式碼最後的本意,
我們還是老實的在vendor目錄下建立自己公司名字,然後在公司目錄下建立一個新的vendorsetup.sh,在裡面新增上自己的產品編譯項
#mkdir vendor/farsight/
#touch vendor/farsight/vendorsetup.sh
#echo "add_lunch_combo fs100-eng" > vendor/farsight/vendorsetup.sh
這樣,當我們在執行source build/envsetup.sh命令的時候,可以在shell上看到下面的資訊:
including vendor/farsight/vendorsetup.sh
2. 按照android官網的步驟,開始執行lunch full-eng
當然如果你按上述命令執行,它編譯的還是通用的eng版本系統,不是我們個性系統,我們可以執行lunch命令,它會打印出一個選擇選單,列出可用的編譯選項
如果你按照第一步中添加了vendorsetup.sh那麼,你的選項中會出現:
You're building on Linux
generic-eng simulator fs100-eng
Lunch menu... pick a combo:
1. generic-eng
2. simulator
3. fs100-eng
其中第3項是我們自己新增的編譯項。
lunch命令是envsetup.sh裡定義的一個命令,用來讓使用者選擇編譯項,來定義Product和編譯過程中用到的全域性變數。
我們一直沒有說明前面的fs100-eng是什麼意思,現在來說明下,fs100是我定義的產品的名字,eng是產品的編譯型別,除了eng外,還有user, userdebug,分別表示:
eng: 工程機,
user:終端使用者機
userdebug:除錯測試機
tests:測試機
由此可見,除了eng和user外,另外兩個一般不能交給終端使用者的,記得m8出來的時候,先放出了一部分eng工程機,然後出來了user機之後,可以用工程機換。
那麼這四個型別是幹什麼用的呢?其實,在main.mk裡有說明,在Android的原始碼裡,每一個目標(也可以看成工程)目錄都有一個Android.mk的makefile,每個目標的Android.mk中有一個型別宣告:
LOCAL_MODULE_TAGS,這個TAGS就是用來指定,當前的目標編譯完了屬於哪個分類裡。
PS:Android.mk和Linux裡的makefile不太一樣,它是Android編譯系統自己定義的一個makefile來方便編譯成:c,c++的動態、靜態庫或可執行程式,或java庫或android的程式,好了,我們來分析下lunch命令幹了什麼?
function lunch()
{
local answer
if [ "$1" ] ; then
# lunch後面直接帶引數
answer=$1
else
# lunch後面不帶引數,則列印處所有的target product和variant選單提供使用者選擇
print_lunch_menu
echo -n "Which would you like? [generic-eng] "
read answer
fi
local selection=
if [ -z "$answer" ]
then
# 如果使用者在選單中沒有選擇,直接回車,則為系統預設的generic-eng
selection=generic-eng
elif [ "$answer" = "simulator" ]
then
# 如果是模擬器
selection=simulator
elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")
then
# 如果answer是選擇選單的數字,則獲取該數字對應的字串
if [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]
then
selection=${LUNCH_MENU_CHOICES[$(($answer-$_arrayoffset))]}
fi
# 如果 answer字串匹配 *-*模式(*的開頭不能為-)
elif (echo -n $answer | grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$")
then
selection=$answer
fi
if [ -z "$selection" ]
then
echo
echo "Invalid lunch combo: $answer"
return 1
fi
# special case the simulator
if [ "$selection" = "simulator" ]
then
# 模擬器模式
export TARGET_PRODUCT=sim
export TARGET_BUILD_VARIANT=eng
export TARGET_SIMULATOR=true
export TARGET_BUILD_TYPE=debug
else
# 將 product-variant模式中的product分離出來
local product=$(echo -n $selection | sed -e "s/-.*$//")
# 檢查之,呼叫關係 check_product()->get_build_var()->build/core/config.mk比較羅嗦,不展開了
check_product $product
if [ $? -ne 0 ]
then
echo
echo "** Don't have a product spec for: '$product'"
echo "** Do you have the right repo manifest?"
product=
fi
# 將 product-variant模式中的variant分離出來
local variant=$(echo -n $selection | sed -e "s/^[^\-]*-//")
# 檢查之,看看是否在 (user userdebug eng) 範圍內
check_variant $variant
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_SIMULATOR=false
export TARGET_BUILD_TYPE=release
fi # !simulator
echo
# 設定到環境變數,比較多,不再一一列出,最簡單的方法 set >env.txt 可獲得
set_stuff_for_environment
# 列印一些主要的變數, 呼叫關係 printconfig()->get_build_var()->build/core/config.mk-
>build/core/envsetup.mk 比較羅嗦,不展開了
printconfig
}
由上面分析可知,lunch命令可以帶引數和不帶引數,最終匯出一些重要的環境變數,從而影響編譯系統的編譯結果。匯出的變數如下(以實際執行情況為例)
TARGET_PRODUCT=fs100
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR=false
TARGET_BUILD_TYPE=release
執行完上述兩個步驟,就該執行:make命令了