Android編譯過程詳解(一)

Android的優勢就在於其開源,手機和平板生產商可以根據自己的硬件進行個性定制自己的手機產品,如小米,LePhone,M9等,因此,在我們在對Android的源碼進行定制的時候,很有必要瞭解下,Android的編譯過程。

如果你從來沒有做過Android代碼的編譯,那麼最官方的編譯過程就是查看Android的官方網站:http://source.android.com/source/building.html

但是,這兒隻是告訴你瞭如何去編譯一個通用的系統,並沒有詳細告訴你細節,我們跟著編譯過程來瞭解下。

+——————————————————————————————————————–+

 本文使用Android版本為2.1,采用開發板為華清遠見研發的FS_S5PC100 A8開發板。

+——————————————————————————————————————–+

1. source build/envsetup.sh

這個命令是用來將envsetup.sh裡的所有用到的命令加載到環境變量裡去,我們來分析下它。

envsetup.sh裡的主要命令如下:

[plain] 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 ()                 # 跳到指定目錄 
 
  
# 這是系統自動增加瞭一個默認的編譯項 generic-eng 
# add the default one here 
<strong><span style="color:#ff0000;">add_lunch_combo generic-eng</span></strong> 
 
  
# 下面的代碼很重要,它要從vendor目錄下查找vendorsetup.sh文件,如果查到瞭,就加載它 
# Execute the contents of any vendorsetup.sh files we can find. 
<strong><span style="color:#ff0000;">for f in `/bin/ls vendor/*/vendorsetup.sh vendor/*/build/vendorsetup.sh 2> /dev/null` 
do 
    echo "including $f" 
    . $f 
done</span></strong> 
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 ()                 # 跳到指定目錄

 
# 這是系統自動增加瞭一個默認的編譯項 generic-eng
# add the default one here
<strong><span style="color:#ff0000;">add_lunch_combo generic-eng</span></strong>

 
# 下面的代碼很重要,它要從vendor目錄下查找vendorsetup.sh文件,如果查到瞭,就加載它
# Execute the contents of any vendorsetup.sh files we can find.
<strong><span style="color:#ff0000;">for f in `/bin/ls vendor/*/vendorsetup.sh vendor/*/build/vendorsetup.sh 2> /dev/null`
do
    echo "including $f"
    . $f
done</span></strong>根據上面的內容,可以推測出,如果要想定義自己的產品編譯項,簡單的辦法是直接在envsetup.sh最後,添加上add_lunch_combo myProduct-eng,當然這麼做,不太符合上面代碼最後的本意,我們還是老實的在vendor目錄下創建自己公司名字,然後在公司目錄下創建一個新的vendorsetup.sh,在裡面添加上自己的產品編譯項

[plain] #mkdir vendor/farsight/ 
#touch vendor/farsight/vendorsetup.sh 
#echo "add_lunch_combo fs100-eng" > vendor/farsight/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上看到下面的信息:

[plain] including vendor/farsight/vendorsetup.sh 
including vendor/farsight/vendorsetup.sh
2. 按照android官網的步驟,開始執行lunch full-eng

當然如果你按上述命令執行,它編譯的還是通用的eng版本系統,不是我們個性系統,我們可以執行lunch命令,它會打印出一個選擇菜單,列出可用的編譯選項

如果你按照第一步中添加瞭vendorsetup.sh那麼,你的選項中會出現:

[plain] You're building on Linux 
 
generic-eng simulator fs100-eng 
Lunch menu… pick a combo: 
     1. generic-eng 
     2. simulator 
     3. fs100-eng 
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:調試測試機

由此可見,除瞭eng和user外,另外兩個一般不能交給最終用戶的,記得m8出來的時候,先放出瞭一部分eng工程機,然後出來瞭user機之後,可以用工程機換。

好瞭,我們來分析下lunch命令幹瞭什麼?

[plain] 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 
        # 模擬器模式 www.aiwalls.com
        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 
<span style="white-space:pre">    </span>#  導出環境變量,這兒很重要,因為後面的編譯系統都是依賴於這裡定義的幾個變量的 
        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 

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
<span style="white-space:pre"> </span>#  導出環境變量,這兒很重要,因為後面的編譯系統都是依賴於這裡定義的幾個變量的
        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命令可以帶參數和不帶參數,最終導出一些重要的環境變量,從而影響編譯系統的編譯結果。導出的變量如下(以實際運行情況為例)

[plain] TARGET_PRODUCT=fs100 
TARGET_BUILD_VARIANT=eng 
TARGET_SIMULATOR=false 
TARGET_BUILD_TYPE=release 
        TARGET_PRODUCT=fs100
        TARGET_BUILD_VARIANT=eng
        TARGET_SIMULATOR=false
        TARGET_BUILD_TYPE=release
執行完上述兩個步驟,就該執行:make命令瞭,下篇來分析。

 

摘自 mr_raptor的專欄

發佈留言