android系統編譯過程詳解


第一部分:概述

 

在研究Android編譯系統之前,我們首先需要了解Linux系統的make命令。在Linux系統中,我們可以通過make命令來編譯代碼。Make命令在執行的時候,默認會在當前目錄找到一個Makefile文件,然后根據Makefile文件中的指令來對代碼進行編譯。也就是說,make命令執行的是Makefile文件中的指令。Makefile文件中的指令可以是編譯命令,例如gcc,也可以是其它命令,例如Linux系統中的shell命令cp、rm等等。理解這一點非常重要,因為雖然通常我們說make命令是可以編譯代碼的,但是它實際上可以做任何事情。

        看到這里,有的小伙伴可能會說,在Linux系統中,直接通過shell命令也可以做很多事情啊,它和make命令有什么區別呢?通過前面的介紹可以知道,make命令事實也是通過shell命令來完成任務的,但是它的神奇之處是可以幫我們處理好文件之間的依賴關系。我們通常都有會這樣的一個需求,假設有一個文件T,它依賴於另外一個文件D,要求只有當文件D的內容發生變化,才重新生成文件T。這種需求在編譯系統中表現得尤其典型,當一個*.c文件include的*.h文件發生變化時,需要重新編譯該*.c文件,或者當一個模塊A所引用的模塊B發生變化時,重新編譯模塊B。正是由於編譯系統中存在這種典型的文件依賴需求,而make命令又是專門用來解決這種文件依賴問題的,因此我們通常認為make命令是用來編譯代碼的。

 Make命令是怎么知道兩個文件之間存在依賴關系,以及當被依賴文件發生變化時如何處理目標文件的呢?答案就在前面提到的Makefile文件。Makefile文件實際上是一個腳本文件,就像普通的shell腳本文件一樣,只不過它遵循的是Makefile語法。Makefile文件最基礎的功能就是描述文件之間的依賴關系,以及怎么處理這些依賴關系。例如,假設有一個目錄文件target,它依賴於文件dependency,並且當文件dependency發生變化時,需要通過command命令來重新生成文件T,這時候我們就可以在Makefile編寫以下語句:

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
 
  1. target: dependency  
  2. <tab>command -o target -i dependency  
        我們假設命令command的-o選項指定的是輸出文件,而-i選項指定的是輸入文件。此外,命令command必須是另起一行,並且以tab鍵開頭。

 

        這就是最基礎也是最主要的Makefile文件語法。當然,Makefile文件還有很多其它的語法,這里不可能一一描述。推薦一本書《GNU make中文手冊》,里面非常詳細地介紹了make以及Makefile文件語法。

 

 

整個工程只有一個Makefile,聽起來似乎是一件很瘋狂的事情,因為這個Makefile可能會變得無比龐大和復雜。其實不用擔心,我們可以按照模塊來將這個Makefile划分成一個個Makefile片段(fragement),然后通過Makefile的include指令來將這些Makefile片段組裝在一個Makefile中。與遞歸Makefile相比,每一個模塊現在擁有的是一個Makefile片段,而不是一個Makefile文件。這正是Android編譯系統的設計思想和原則,也就是說,我們平時所編寫的Android.mk編譯腳本都只不過是整個Android編譯系統的一個Makefile片段。

        明白了Android編譯系統的設計思想和原則之后,我們就可以通過圖5來觀察一下Android編譯系統的整體架構了:

圖5 Android編譯系統架構

       在使用Android編譯系統之前,我們需要打開一個shell進入到Android源碼根目錄中,並且在該shell中將build/envsetup.sh腳本文件source進來。腳本文件build/envsetup.sh被source到當前shell的過程中,會在vendor和device兩個目錄將廠商指定的envsetup.sh也source到當前shell當中,這樣就可以獲得廠商提供的產品配置信息。此外,腳本文件build/envsetup.sh還提供了以下幾個重要的命令來幫助我們編譯Android源碼:

       1. lunch

        用來初始化編譯環境,例如設置環境變量和指定目標產品型號。Lunch命令在執行的時候,主要做兩件事情。第一件事情是設置TARGET_PRODUCT、TARGET_BUILD_VARIANT、TARGET_BUILD_TYPE和TARGET_BUILD_APPS等環境變量,用來指定目標產品類型和編譯類型。第二件事情是通過make命令執行build/core/config.mk腳本,並且通過加載另外一個腳本build/core/dumpvar.mk打印出當前的編譯環境配置信息。注意,build/core/config.mk和build/core/dumpvar.mk均為Makefile腳本,因此它們可以通過make命令來執行。另外,build/core/config.mk腳本還會加載一個名稱為BoradConfig.mk的腳本以及build/core/envsetup.mk腳本來配置目標產品型號的相關信息。

       2. m

       相當於是在執行make命令。對整個Android源碼進行編譯。

       3. mm

       如果是在Android源碼根目錄下執行,那么就相當於是執行make命令對整個源碼進行編譯。如果是在Android源碼根目錄下的某一個子目錄執行,那么就在會在從該子目錄開始,一直往上一個目錄直至到根目錄,尋找是否存在一個Android.mk文件。如果存在的話,那么就通過make命令對該Android.mk文件描述的模塊進行編譯。

       4. mmm

       后面可以跟一個或者若干個目錄。如果指定了多個目錄,那么目錄之間以空格分隔,並且每一個目錄下都必須存在一個Android,mk文件。如果沒有在目錄后面通過冒號指定模塊名稱,那么在Android.mk文件中描述的所有模塊都會被編譯,否則只有指定的模塊會被編譯。如果需要同時指定多個模塊,那么這些模塊名稱必須以逗號分隔。它的語法如下所示:

 

[html]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
 
  1. mmm <dir-1<dir-2> ... <dir-N>[:module-1,module-2,...,module-M]  
       該命令會通過make命令來執行Android源碼根目錄下的Makefile文件,該Makefile文件又會將build/core/main.mk加載進來。文件build/core/main.mk在加載的過程中,還會加載以下幾個主要的文件:

 

       (1). build/core/config.mk

       該文件根據lunch命令所配置的產品信息在build/target/board、vendor或者device目錄中找到對應的BoradConfig.mk文件,以及通過加載build/core/product_config.mk文件在build/target/product、vendor或者device目錄中找到對應的AndroidProducts.mk文件,來進一步對編譯環境進行配置,以便接下來編譯指定模塊時可以獲得必要的信息。

       (2). build/core/definitions.mk

       該文件定義了在編譯過程需要調用到的各種自定義函數。

       (3). 指定的Android.mk

       這些指定的Android.mk環境是由mmm命令通過環境變量ONE_SHOT_MAKEFILE傳遞給build/core/main.mk文件使用的。這些Android.mk文件一般還會通過環境變量BUILD_PACKAGE、BUILD_JAVA_LIBRARY、BUILD_STATIC_JAVA_LIBRARY、BUILD_SHARED_LIBRARY、BUILD_STATIC_LIBRARY、BUILD_EXECUTABLE和BUILD_PREBUILT將build/core/package.mk、build/core/java_library.mk、build/core/static_java_library.mk、build/core/shared_library.mk、build/core/static_library.mk、build/core/executable.mk和build/core/prebuilt.mk等編譯片段模板文件加載進來,來表示要編譯是APK、Java庫、Linux動態庫/靜態庫/可執行文件或者預先編譯好的文件等等。

       (4). build/core/Makefile

       該文件包含了用來制作system.img、ramdisk.img、boot.img和recovery.img等鏡像文件的腳本。


 

第二部分:Android編譯環境初始化

 

對編譯環境進行初始化,其中最主要就是指定編譯的類型和目標設備的型號。Android的編譯類型主要有eng、userdebug和user三種,而支持的目標設備型號則是不確定的,它們由當前的源碼配置情況所決定。為了確定源碼支持的所有目標設備型號,Android編譯系統在初始化的過程中,需要在特定的目錄中加載特定的配置文件。

 


 

Android的優勢就在於其開源,

我們在對Android的源碼進行定制的時候,很有必要了解下,Android的編譯過程。

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

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

 

按照google給出的編譯步驟如下:

   1> source build/envsetup.sh:加載命令

   2> lunch:選擇平台編譯選項

   3> make:執行編譯

我們按照編譯步驟來分析編譯過程的細節,最終添加自己的平台編譯選項。

1. source build/envsetup.sh

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

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

 

 

[html]  view plain  copy
 
  1. function help()                  # 顯示幫助信息  
  2. function get_abs_build_var()           # 獲取絕對變量  
  3. function get_build_var()             # 獲取絕對變量  
  4. function check_product()             # 檢查product  
  5. function check_variant()             # 檢查變量  
  6. function setpaths()                # 設置文件路徑  
  7. function printconfig()              # 打印配置  
  8. function set_stuff_for_environment()        # 設置環境變量  
  9. function set_sequence_number()            # 設置序號  
  10. function settitle()                # 設置標題  
  11. function choosetype()               # 設置type  
  12. function chooseproduct()              # 設置product  
  13. function choosevariant()              # 設置variant  
  14. function tapas()                  # 功能同choosecombo  
  15. function choosecombo()               # 設置編譯參數  
  16. function add_lunch_combo()             # 添加lunch項目  
  17. function print_lunch_menu()            # 打印lunch列表  
  18. function lunch()                 # 配置lunch  
  19. function m()                   # make from top  
  20. function findmakefile()              # 查找makefile  
  21. function mm()                   # make from current directory  
  22. function mmm()                   # make the supplied directories  
  23. function croot()                 # 回到根目錄  
  24. function cproj()  
  25. function pid()  
  26. function systemstack()  
  27. function gdbclient()  
  28. function jgrep()                 # 查找java文件  
  29. function cgrep()                  # 查找c/cpp文件  
  30. function resgrep()  
  31. function tracedmdump()  
  32. function runhat()  
  33. function getbugreports()  
  34. function startviewserver()  
  35. function stopviewserver()  
  36. function isviewserverstarted()  
  37. function smoketest()  
  38. function runtest()  
  39. function godir ()                 # 跳到指定目錄 405  
  40.   
  41.  # add_lunch_combo函數被多次調用,就是它來添加Android編譯選項  
  42.  # Clear this variable.  It will be built up again when the vendorsetup.sh  
  43.  # files are included at the end of this file.  
  44.  # 清空LUNCH_MENU_CHOICES變量,用來存在編譯選項  
  45.  unset LUNCH_MENU_CHOICES  
  46. function add_lunch_combo()     
  47. {  
  48.      local new_combo=$1         # 獲得add_lunch_combo被調用時的參數  
  49.      local c  
  50.      # 依次遍歷LUNCH_MENU_CHOICES里的值,其實該函數第一次調用時,該值為空  
  51.      for c in ${LUNCH_MENU_CHOICES[@]} ; do   
  52.          if [ "$new_combo" = "$c" ] ; then    # 如果參數里的值已經存在於LUNCH_MENU_CHOICES變量里,則返回  
  53.              return  
  54.          fi  
  55.      done  
  56.      # 如果參數的值不存在,則添加到LUNCH_MENU_CHOICES變量里  
  57.      LUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]} $new_combo)  
  58. }  
  59.   
  60.   
  61. # 這是系統自動增加了一個默認的編譯項 generic-eng  
  62. # add the default one here  
  63. add_lunch_combo generic-eng    # 調用上面的add_lunch_combo函數,將generic-eng作為參數傳遞過去  
  64.    
  65. # if we're on linux, add the simulator.  There is a special case  
  66. # in lunch to deal with the simulator  
  67. if [ "$(uname)" = "Linux" ] ; then  
  68.      add_lunch_combo simulator  
  69. fi  
  70.   
  71. # 下面的代碼很重要,它要從vendor目錄下查找vendorsetup.sh文件,如果查到了,就加載它  
  72. # Execute the contents of any vendorsetup.sh files we can find.  
  73. for f in `/bin/ls vendor/*/vendorsetup.sh vendor/*/build/vendorsetup.sh 2> /dev/null`  
  74.    do  
  75.      echo "including $f"  
  76.      . $f       # 執行找到的腳本,其實里面就是廠商自己定義的編譯選項  
  77.    done  
  78. 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命令干了什么?

 

 

[html]  view plain  copy
 
  1. function lunch()  
  2. {  
  3.     local answer  
  4.   
  5.     if [ "$1" ] ; then  
  6.        # lunch后面直接帶參數  
  7.         answer=$1  
  8.     else  
  9.        # lunch后面不帶參數,則打印處所有的target product和variant菜單提供用戶選擇  
  10.         print_lunch_menu     
  11.         echo -n "Which would you like? [generic-eng] "  
  12.         read answer  
  13.     fi  
  14.   
  15.     local selection=  
  16.   
  17.     if [ -z "$answer" ]  
  18.     then  
  19.            # 如果用戶在菜單中沒有選擇,直接回車,則為系統缺省的generic-eng  
  20.         selection=generic-eng  
  21.     elif [ "$answer" = "simulator" ]  
  22.     then  
  23.         # 如果是模擬器  
  24.         selection=simulator  
  25.     elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")  
  26.     then  
  27.         # 如果answer是選擇菜單的數字,則獲取該數字對應的字符串  
  28.         if [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]  
  29.         then  
  30.             selection=${LUNCH_MENU_CHOICES[$(($answer-$_arrayoffset))]}  
  31.         fi  
  32.         # 如果 answer字符串匹配 *-*模式(*的開頭不能為-)  
  33.     elif (echo -n $answer | grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$")  
  34.     then  
  35.         selection=$answer  
  36.     fi  
  37.   
  38.     if [ -z "$selection" ]  
  39.     then  
  40.         echo  
  41.         echo "Invalid lunch combo: $answer"  
  42.         return 1  
  43.     fi  
  44.   
  45.     # special case the simulator  
  46.     if [ "$selection" = "simulator" ]  
  47.     then  
  48.         # 模擬器模式  
  49.         export TARGET_PRODUCT=sim  
  50.         export TARGET_BUILD_VARIANT=eng  
  51.         export TARGET_SIMULATOR=true  
  52.         export TARGET_BUILD_TYPE=debug  
  53.     else  
  54.   
  55.         # 將 product-variant模式中的product分離出來  
  56.         local product=$(echo -n $selection | sed -e "s/-.*$//")  
  57.   
  58.         # 檢查之,調用關系 check_product()->get_build_var()->build/core/config.mk比較羅嗦,不展開了  
  59.         check_product $product  
  60.         if [ $? -ne 0 ]  
  61.         then  
  62.             echo  
  63.             echo "** Don't have a product spec for: '$product'"  
  64.             echo "** Do you have the right repo manifest?"  
  65.             product=  
  66.         fi  
  67.   
  68.         # 將 product-variant模式中的variant分離出來  
  69.         local variant=$(echo -n $selection | sed -e "s/^[^\-]*-//")  
  70.   
  71.         # 檢查之,看看是否在 (user userdebug eng) 范圍內  
  72.         check_variant $variant  
  73.         if [ $? -ne 0 ]  
  74.         then  
  75.             echo  
  76.             echo "** Invalid variant: '$variant'"  
  77.             echo "** Must be one of ${VARIANT_CHOICES[@]}"  
  78.             variant=  
  79.         fi  
  80.   
  81.         if [ -z "$product" -o -z "$variant" ]  
  82.         then  
  83.             echo  
  84.             return 1  
  85.         fi  
  86.  #  導出環境變量,這里很重要,因為后面的編譯系統都是依賴於這里定義的幾個變量的  
  87.         export TARGET_PRODUCT=$product  
  88.         export TARGET_BUILD_VARIANT=$variant  
  89.         export TARGET_SIMULATOR=false  
  90.         export TARGET_BUILD_TYPE=release  
  91.     fi # !simulator  
  92.   
  93.     echo  
  94.   
  95.     # 設置到環境變量,比較多,不再一一列出,最簡單的方法 set >env.txt 可獲得  
  96.     set_stuff_for_environment  
  97.     # 打印一些主要的變量, 調用關系 printconfig()->get_build_var()->build/core/config.mk->build/core/envsetup.mk 比較羅嗦,不展開了  
  98.     printconfig  
  99. }  


 

 

由上面分析可知,lunch命令可以帶參數和不帶參數,最終導出一些重要的環境變量,從而影響編譯系統的編譯結果。導出的變量如下(以實際運行情況為例)

 

TARGET_PRODUCT=fs100
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR= false
TARGET_BUILD_TYPE=release

 
執行完上述兩個步驟,就該執行:make命令了,下篇來分析。


 

 

1. make 

執行make命令的結果就是去執行當前目錄下的Makefile文件,我們來看下它的內容:

### DO NOT EDIT THIS FILE ###
include build/core/main.mk
### DO NOT EDIT THIS FILE ###


呵呵,看到上面 的內容,我們都會笑,這是我見過最簡單的Makefile了,我們再看下build/core/main.mk

main.mk文件里雖然腳本不多,但是卻定義了整個Android的編譯關系,它主要引入了下列幾個重要的mk文件:

49 include $(BUILD_SYSTEM)/config.mk

55 include $(BUILD_SYSTEM)/cleanbuild.mk

142 include $(BUILD_SYSTEM)/definitions.mk

當然每個mk文件都有自己獨特的意義,我們一並將主線流程相關mk文件都列出來,大概來介紹下,先有個整體的概念,然后再細化了解。

所有的Makefile都通過build/core/main.mk這個文件組織在一起,它定義了一個默認goals:droid,當我們在TOP目錄下,敲Make實際上就等同於我們執行make droid。

當Make include所有的文件,完成對所有make我文件的解析以后就會尋找生成droid的規則,依次生成它的依賴,直到所有滿足的模塊被編譯好,然后使用相應的工具打包成相應的img。其中,config.mk,envsetup.mk,product_config.mk文件是編譯用戶指定平台系統的關鍵文件。上圖中紅色部分是用戶指定平台產品的編譯主線,我們先來看下config.mk的主要作用。

 

2. build/core/config.mk

該文件被main.mk包含。

定義了以下環境變量:

16 SRC_HEADERS := \
  17     $(TOPDIR) system /core/include \
  18     $(TOPDIR)hardware/libhardware/include \
  19     $(TOPDIR)hardware/libhardware_legacy/include \
  20     $(TOPDIR)hardware/ril/include \
  21     $(TOPDIR)dalvik/libnativehelper/include \
  22     $(TOPDIR)frameworks/base/include \
  23     $(TOPDIR)frameworks/base/opengl/include \
  24     $(TOPDIR)external/skia/include
  25 SRC_HOST_HEADERS:=$(TOPDIR)tools/include
  26 SRC_LIBRARIES:= $(TOPDIR)libs
  27 SRC_SERVERS:= $(TOPDIR)servers
  28 SRC_TARGET_DIR := $(TOPDIR)build/target
  29 SRC_API_DIR := $(TOPDIR)frameworks/base/api
.....然后定義了下面幾個重要的編譯命令 43 CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk
  44 BUILD_HOST_STATIC_LIBRARY:= $(BUILD_SYSTEM)/host_static_library.mk
  45 BUILD_HOST_SHARED_LIBRARY:= $(BUILD_SYSTEM)/host_shared_library.mk
  46 BUILD_STATIC_LIBRARY:= $(BUILD_SYSTEM)/static_library.mk
  47 BUILD_RAW_STATIC_LIBRARY := $(BUILD_SYSTEM)/raw_static_library.mk
  48 BUILD_SHARED_LIBRARY:= $(BUILD_SYSTEM)/shared_library.mk
  49 BUILD_EXECUTABLE:= $(BUILD_SYSTEM)/executable.mk
  50 BUILD_RAW_EXECUTABLE:= $(BUILD_SYSTEM)/raw_executable.mk
  51 BUILD_HOST_EXECUTABLE:= $(BUILD_SYSTEM)/host_executable.mk
  52 BUILD_PACKAGE:= $(BUILD_SYSTEM)/package.mk
  53 BUILD_HOST_PREBUILT:= $(BUILD_SYSTEM)/host_prebuilt.mk
  54 BUILD_PREBUILT:= $(BUILD_SYSTEM)/prebuilt.mk
  55 BUILD_MULTI_PREBUILT:= $(BUILD_SYSTEM)/multi_prebuilt.mk
  56 BUILD_JAVA_LIBRARY:= $(BUILD_SYSTEM)/java_library.mk
  57 BUILD_STATIC_JAVA_LIBRARY:= $(BUILD_SYSTEM)/static_java_library.mk
  58 BUILD_HOST_JAVA_LIBRARY:= $(BUILD_SYSTEM)/host_java_library.mk
  59 BUILD_DROIDDOC:= $(BUILD_SYSTEM)/droiddoc.mk
  60 BUILD_COPY_HEADERS := $(BUILD_SYSTEM)/copy_headers.mk
  61 BUILD_KEY_CHAR_MAP := $(BUILD_SYSTEM)/key_char_map.mk

 

[plain]  view plain  copy
 
  1. 上述命令變量其實是對應的mk文件名,所有的Android.mk文件里基本上都包含上述命令變量,如:  

CLEAR_VARS:用來清除之前定義的環境變量

BUILD_SHARED_LIBRARY:用來指定編譯動態庫過程

109 # ---------------------------------------------------------------
110 # Define most of the global variables.  These are the ones that
111 # are specific to the user's build configuration.
112 include $(BUILD_SYSTEM)/envsetup.mk
113
114 # Boards may be defined under $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)
115 # or under vendor /*/$(TARGET_DEVICE).  Search in both places, but
116 # make sure only one exists.
117 # Real boards should always be associated with an OEM vendor.
118 board_config_mk := \
119     $(strip $(wildcard \
120         $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \
121         vendor/*/ $(TARGET_DEVICE)/BoardConfig.mk \
122     ))
123 ifeq ($(board_config_mk),)
124   $(error No config file found for  TARGET_DEVICE $(TARGET_DEVICE))
125 endif
126 ifneq ($(words $(board_config_mk)),1)
127   $(error Multiple board config files for  TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk))
128 endif
129 include $(board_config_mk)
130 TARGET_DEVICE_DIR := $(patsubst %/,%,$(dir $(board_config_mk)))
131 board_config_mk :=

 

112行又包含了另外一個重要的mk文件envsetup.mk,我們來看一下。

 

3. envsetup.mk

 

復制代碼
 25 ifeq ($(TARGET_PRODUCT),)    #判斷TARGET_PRODUCT是否為空,
 26 ifeq ($(TARGET_SIMULATOR),true)
 27 TARGET_PRODUCT := sim
 28 else
 29 TARGET_PRODUCT := generic
 30 endif
 31 endif
復制代碼

 

第25行,判斷TARGET_PRODUCT是否為空,根據上一節分析可知,TARGET_PRODUCT=fs100 

復制代碼
 34 # the variant -- the set of files that are included for a build
 35 ifeq ($(strip $(TARGET_BUILD_VARIANT)),)
 36 TARGET_BUILD_VARIANT := eng
 37 endif
 38 
 39 # Read the product specs so we an get TARGET_DEVICE and other
 40 # variables that we need in order to locate the output files.
 41 include $(BUILD_SYSTEM)/product_config.mk
復制代碼

 

在41行又包含了product_config.mk文件,等會我們再分析它,先看下面的

 

復制代碼
148 # ---------------------------------------------------------------
149 # figure out the output directories
150 
151 ifeq (,$(strip $(OUT_DIR)))
152 OUT_DIR := $(TOPDIR)out
153 endif
154 
155 DEBUG_OUT_DIR := $(OUT_DIR)/debug
156 
157 # Move the host or target under the debug/ directory
158 # if necessary.
159 TARGET_OUT_ROOT_release := $(OUT_DIR)/target
160 TARGET_OUT_ROOT_debug := $(DEBUG_OUT_DIR)/target
161 TARGET_OUT_ROOT := $(TARGET_OUT_ROOT_$(TARGET_BUILD_TYPE))
162 
...
184 PRODUCT_OUT := $(TARGET_PRODUCT_OUT_ROOT)/$(TARGET_DEVICE)
187 
188 HOST_OUT_EXECUTABLES:= $(HOST_OUT)/bin
189 HOST_OUT_SHARED_LIBRARIES:= $(HOST_OUT)/lib
190 HOST_OUT_JAVA_LIBRARIES:= $(HOST_OUT)/framework
191 HOST_OUT_SDK_ADDON := $(HOST_OUT)/sdk_addon
...
200 TARGET_OUT_INTERMEDIATES := $(PRODUCT_OUT)/obj
201 TARGET_OUT_HEADERS:= $(TARGET_OUT_INTERMEDIATES)/include
202 TARGET_OUT_INTERMEDIATE_LIBRARIES := $(TARGET_OUT_INTERMEDIATES)/lib
203 TARGET_OUT_COMMON_INTERMEDIATES := $(TARGET_COMMON_OUT_ROOT)/obj
204 
205 TARGET_OUT := $(PRODUCT_OUT)/system
206 TARGET_OUT_EXECUTABLES:= $(TARGET_OUT)/bin
207 TARGET_OUT_OPTIONAL_EXECUTABLES:= $(TARGET_OUT)/xbin
208 TARGET_OUT_SHARED_LIBRARIES:= $(TARGET_OUT)/lib
209 TARGET_OUT_JAVA_LIBRARIES:= $(TARGET_OUT)/framework
210 TARGET_OUT_APPS:= $(TARGET_OUT)/app
211 TARGET_OUT_KEYLAYOUT := $(TARGET_OUT)/usr/keylayout
212 TARGET_OUT_KEYCHARS := $(TARGET_OUT)/usr/keychars
213 TARGET_OUT_ETC := $(TARGET_OUT)/etc
214 TARGET_OUT_STATIC_LIBRARIES:= $(TARGET_OUT_INTERMEDIATES)/lib
215 TARGET_OUT_NOTICE_FILES:=$(TARGET_OUT_INTERMEDIATES)/NOTICE_FILES
216 
217 TARGET_OUT_DATA := $(PRODUCT_OUT)/data
218 TARGET_OUT_DATA_EXECUTABLES:= $(TARGET_OUT_EXECUTABLES)
219 TARGET_OUT_DATA_SHARED_LIBRARIES:= $(TARGET_OUT_SHARED_LIBRARIES)
220 TARGET_OUT_DATA_JAVA_LIBRARIES:= $(TARGET_OUT_JAVA_LIBRARIES)
221 TARGET_OUT_DATA_APPS:= $(TARGET_OUT_DATA)/app
222 TARGET_OUT_DATA_KEYLAYOUT := $(TARGET_OUT_KEYLAYOUT)
223 TARGET_OUT_DATA_KEYCHARS := $(TARGET_OUT_KEYCHARS)
224 TARGET_OUT_DATA_ETC := $(TARGET_OUT_ETC)
225 TARGET_OUT_DATA_STATIC_LIBRARIES:= $(TARGET_OUT_STATIC_LIBRARIES)
226 
227 TARGET_OUT_UNSTRIPPED := $(PRODUCT_OUT)/symbols
228 TARGET_OUT_EXECUTABLES_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/system/bin
229 TARGET_OUT_SHARED_LIBRARIES_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/system/lib
230 TARGET_ROOT_OUT_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)
231 TARGET_ROOT_OUT_SBIN_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/sbin
232 TARGET_ROOT_OUT_BIN_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/bin
233 
234 TARGET_ROOT_OUT := $(PRODUCT_OUT)/root
235 TARGET_ROOT_OUT_BIN := $(TARGET_ROOT_OUT)/bin
236 TARGET_ROOT_OUT_SBIN := $(TARGET_ROOT_OUT)/sbin
237 TARGET_ROOT_OUT_ETC := $(TARGET_ROOT_OUT)/etc
238 TARGET_ROOT_OUT_USR := $(TARGET_ROOT_OUT)/usr
239 
240 TARGET_RECOVERY_OUT := $(PRODUCT_OUT)/recovery
241 TARGET_RECOVERY_ROOT_OUT := $(TARGET_RECOVERY_OUT)/root
242 
243 TARGET_SYSLOADER_OUT := $(PRODUCT_OUT)/sysloader
244 TARGET_SYSLOADER_ROOT_OUT := $(TARGET_SYSLOADER_OUT)/root
245 TARGET_SYSLOADER_SYSTEM_OUT := $(TARGET_SYSLOADER_OUT)/root/system
246 
247 TARGET_INSTALLER_OUT := $(PRODUCT_OUT)/installer
248 TARGET_INSTALLER_DATA_OUT := $(TARGET_INSTALLER_OUT)/data
249 TARGET_INSTALLER_ROOT_OUT := $(TARGET_INSTALLER_OUT)/root
250 TARGET_INSTALLER_SYSTEM_OUT := $(TARGET_INSTALLER_OUT)/root/system
復制代碼

 

上面的代碼是指定了目標輸出代碼的位置和主機輸出代碼的位置,重要的幾個如下:

 

復制代碼
PRODUCT_OUT = 這個的結果要根據product_config.mk文件內容來決定,其實是out/target/product/fs100/
TARGET_OUT = $(PRODUCT_OUT)/system
TARGET_OUT_EXECUTABLES =  $(PRODUCT_OUT)/system/bin
TARGET_OUT_SHARED_LIBRARIES =  $(PRODUCT_OUT)/system/lib
TARGET_OUT_JAVA_LIBRARIES = $(PRODUCT_OUT)/system/framework
TARGET_OUT_APPS = $(PRODUCT_OUT)/system/app
TARGET_OUT_ETC = $(PRODUCT_OUT)/system/etc
TARGET_OUT_STATIC_LIBRARIES  = $(PRODUCT_OUT)/obj/lib
TARGET_OUT_DATA = $(PRODUCT_OUT)/data
TARGET_OUT_DATA_APPS = $(PRODUCT_OUT)/data/app
TARGET_ROOT_OUT = $(PRODUCT_OUT)/root
TARGET_ROOT_OUT_BIN = $(PRODUCT_OUT)/bin
TARGET_ROOT_OUT_SBIN  = $(PRODUCT_OUT)/system/sbin
TARGET_ROOT_OUT_ETC = $(PRODUCT_OUT)/system/etc
TARGET_ROOT_OUT_USR = $(PRODUCT_OUT)/system/usr
復制代碼

 

總結下:

envsetup.mk文件主要包含了product_config.mk文件,然后指定了編譯時要輸出的所有文件的OUT目錄。


4. build/core/product_config.mk

 

復制代碼
157 include $(BUILD_SYSTEM)/product.mk
...
160 # Read in all of the product definitions specified by the AndroidProducts.mk
161 # files in the tree.
162 #
163 #TODO: when we start allowing direct pointers to product files,
164 #    guarantee that they're in this list.
165 $(call import-products, $(get-all-product-makefiles))
166 $(check-all-products)
...
170 # Convert a short name like "sooner" into the path to the product
171 # file defining that product.
172 #
173 INTERNAL_PRODUCT := $(call resolve-short-product-name, $(TARGET_PRODUCT))
...
176 # Find the device that this product maps to.
177 TARGET_DEVICE := $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEVICE)
復制代碼

 

157行,我靠,又包含了product.mk文件

 

165行,調用函數import-products, $(get-all-product-makefiles),這兒我們看上面的注釋:

    Read in all of the product definitions specified by the AndroidProducts.mk files in the tree.
    TODO: when we start allowing direct pointers to product files, guarantee that they're in this list.

 

    意思是說:讀取指定的目錄下所有的AndrodProducts.mk文件中定義的產品信息

    其實get-all-product-makefiles返回所有的產品文件xxx.mk

    import-products函數去驗證這些產品配置文件是否都包含有必須的配置信息,細節后面分析。

173行調用了resolve-short-product-name函數,它將返回TARGET_PRODUCT產品的配置文件目錄,並賦給INTERNAL_PRODUCT

    也就是說:

    INTERNAL_PRODUCT = vendor/farsight/products/fs100.mk
    TARGET_DEVICE = fs100

       如果調試看其結果,可以在167行,將#$(dump-product)取消注釋

     然后在175行添加: $(info $(INTERNAL_PRODUCT))

       在178行添加: $(info $(TARGET_DEVICE )),查看調試結果。

總結一下:

接合前面的圖,product_config.mk主要讀取vendor目錄下不同廠商自己定義的AndrodProducts.mk文件,從該文件里取得所有產品的配置文件,然后再根據lunch選擇的編譯項TARGET_PRODUCT,找到與之對應的配置文件,然后設置TARGET_DEVICE變量,用於后續編譯。

 

5. build/core/product.mk

 

復制代碼
17 #
 18 # Functions for including AndroidProducts.mk files
 19 #
 20 
 21 #
 22 # Returns the list of all AndroidProducts.mk files.
 23 # $(call ) isn't necessary.
 24 #
 25 define _find-android-products-files
 26 $(shell test -d vendor && find vendor -maxdepth 6 -name AndroidProducts.mk) \
 27   $(SRC_TARGET_DIR)/product/AndroidProducts.mk
 28 endef
 29 
 30 #
 31 # Returns the sorted concatenation of all PRODUCT_MAKEFILES
 32 # variables set in all AndroidProducts.mk files.
 33 # $(call ) isn't necessary.
 34 #
 35 define get-all-product-makefiles
 36 $(sort \
 37   $(foreach f,$(_find-android-products-files), \
 38     $(eval PRODUCT_MAKEFILES :=) \
 39     $(eval LOCAL_DIR := $(patsubst %/,%,$(dir $(f)))) \
 40     $(eval include $(f)) \
 41     $(PRODUCT_MAKEFILES) \
 42    ) \
 43   $(eval PRODUCT_MAKEFILES :=) \
 44   $(eval LOCAL_DIR :=) \
 45  )
 46 endef
復制代碼

 

[plain]  view plain  copy
 
  1. 通過注釋可知,本文件中主要是一些用來處理AndroidProduct.mk的函數<br style="box-sizing: border-box;" /><span style="box-sizing: border-box;">_find-android-products-files:</span>  

    用來獲得vendor目錄下,所有名字為AndroidProduct.mk的文件列表。
get-all-product-makefiles:

    用來獲得所有AndroidProduct.mk文件里定義的PRODUCT_MAKEFILES的值(其實是產品文件路徑名)。


在vendor目錄下,每個公司目錄下都會存在一個AndroidProduct.mk文件,這個文件是用來定義這個公司的產品列表,每個產品用<product_name>.mk來表示
如Android給的示例:

[plain]  view plain  copy
 
  1. vendor/sample/products/AndroidProduct.mk  

其內容如下:

復制代碼
1 #
  2 # This file should set PRODUCT_MAKEFILES to a list of product makefiles
  3 # to expose to the build system.  LOCAL_DIR will already be set to
  4 # the directory containing this file. 
  5 #
  6 # This file may not rely on the value of any variable other than
  7 # LOCAL_DIR; do not use any conditionals, and do not look up the
  8 # value of any variable that isn't set in this file or in a file that
  9 # it includes.
 10 #
 11 
 12 PRODUCT_MAKEFILES := \
 13   $(LOCAL_DIR)/sample_addon.mk
復制代碼

 

[plain]  view plain  copy
 
  1. 里面只定義了一個產品配置文件,即當前目錄下的sample_addon.mk:  
  1 # List of apps and optional libraries (Java and native) to put in the add-on system image.
  2 PRODUCT_PACKAGES := \
  3     PlatformLibraryClient \
  4     com.example.android.platform_library \
  5     libplatform_library_jni

 

上述文件里定義了產品相關個性化信息,如,PRODUCT_PACKAGES表示要在當前產品里添加一些安裝包。
由此可見,get-all-product-makefiles函數,其實就是返回了當前公司里全部的產品對應的mk文件列表。

 


總結:

如果用戶想個性定制自己的產品,應該有以下流程,包含上一節內容:

1. 創建公司目錄

    #mkdir vendor/farsight

2. 創建一個vendorsetup.sh文件,將當前產品編譯項添加到lunch里,讓lunch能找到用戶個性定制編譯項

    #echo "add_lunch_combo fs100-eng" > vendor/farsight/vendorsetup.sh

3. 仿着Android示例代碼,在公司目錄下創建products目錄

    #mkdir -p vendor/farsight/products

4. 仿着Android示例代碼,在products目錄下創建兩個mk文件

    #touch vendor/farsight/products/AndroidProduct.mk vendor/farsight/products/fs100.mk

在AndroidProduct.mk里添加如下內容:

 

[plain]  view plain  copy
 
  1. PRODUCT_MAKEFILES := $(LOCAL_DIR)/fs100.mk  

表示只有一個產品fs100,它對應的配置文件在當前目錄下的fs100.mk。

 

5. 在產品配置文件里添加最基本信息

 

復制代碼
  1 
  2 PRODUCT_PACKAGES := \
  3     IM \
  4     VoiceDialer
  5 
  6 $(call inherit-product, build/target/product/generic.mk)  ##從某一默認配置開始派生余下內容參考派生起點
  7 
  8 # Overrides
  9 PRODUCT_MANUFACTURER := farsight
 10 PRODUCT_NAME := fs100
 11 PRODUCT_DEVICE := fs100
復制代碼

 


 

前面兩節講解了自定義Android編譯項和創建Product產品配置文件,除了編譯和定義產品相關環境變量外,還需要定義Board相關環境變量。

1. build/core/config.mk

 
          
復制代碼
109 # ---------------------------------------------------------------  
110 # Define most of the global variables.  These are the ones that  
111 # are specific to the user's build configuration.  
112 include $(BUILD_SYSTEM)/envsetup.mk  
113   
114 # Boards may be defined under $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)  
115 # or under vendor/*/$(TARGET_DEVICE).  Search in both places, but  
116 # make sure only one exists.  
117 # Real boards should always be associated with an OEM vendor.  
118 board_config_mk := \  
119     $(strip $(wildcard \  
120         $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \  
121         vendor/*/$(TARGET_DEVICE)/BoardConfig.mk \  
122     ))  
123 ifeq ($(board_config_mk),)  
124   $(error No config file found for TARGET_DEVICE $(TARGET_DEVICE))  
125 endif  
126 ifneq ($(words $(board_config_mk)),1)  
127   $(error Multiple board config files for TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk))  
128 endif  
129 include $(board_config_mk)  
130 TARGET_DEVICE_DIR := $(patsubst %/,%,$(dir $(board_config_mk)))  
131 board_config_mk :=  
復制代碼
[plain]  view plain  copy
 
  1. <span style="box-sizing: border-box;">上述代碼在上一節已經見到過,只是分析了112行的envsetup.mk,根據上一節內容可知,</span>envsetup.mk設置了很多OUT變量,最終在build/core/product_config.mk文件里,設置了TARGET_DEVICE = fs100。  

 

我們從114行繼續分析。

從114~117行解釋大意可知:

    Board相關配置文件會存在於$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/或vendor/*/$(TARGET_DEVICE)/目錄中,一個Vendor廠商只能有一個對應的Board配置文件。

118行定義board_config_mk變量:

    $(wildcard xxx)函數就是找到與xxx的匹配項放到空格列表里,前面定義TARGET_DEVICE變量 = fs100,所以$(SRC_TARGET_DIR)/board/fs100/BoardConfig.mk不存在,必須要存在vendor/*/fs100/BoardConfig.mk文件來定義開發板配置信息。

129行,通過include將vendor/*/fs100/BoardConfig.mk包含進來,

130行,TARGET_DEVICE_DIR為board_config_mk的路徑,即:vendor/*/fs100

總結:

   一個vendor廠商必須要有一個對應的Board配置文件,即:vendor/*/fs100/BoardConfig.mk

    定義了TARGET_DEVICE_DIR變量,為board_config_mk的路徑,即:vendor/*/fs100

指定board 相關特性,一定要包含:
TARGET_CPU_ABI := armeabi/...
其他屬性參見其他board樣例.(build/target/board/XXX

 

2.  build/core/main.mk

 

復制代碼
141 # Bring in standard build system definitions.
142 include $(BUILD_SYSTEM)/definitions.mk
...
347 ifeq ($(SDK_ONLY),true)
348 
349 # ----- SDK for Windows ------
350 # These configure the build targets that are available for the SDK under Cygwin.
351 # The first section defines all the C/C++ tools that can be compiled under Cygwin,
352 # the second section defines all the Java ones (assuming javac is available.)
353 
354 subdirs := \
355     prebuilt \
356     build/libs/host \
357     build/tools/zipalign \
...
382 # The following can only be built if "javac" is available.
383 # This check is used when building parts of the SDK under Cygwin.
384 ifneq (,$(shell which javac 2>/dev/null))
385 $(warning sdk-only: javac available.)
386 subdirs += \
387     build/tools/signapk \
388     dalvik/dx \
389     dalvik/libcore \
...
414 else    # !SDK_ONLY
415 ifeq ($(BUILD_TINY_ANDROID), true)
416 
417 # TINY_ANDROID is a super-minimal build configuration, handy for board 
418 # bringup and very low level debugging
419 
420 subdirs := \
421     bionic \
422     system/core \
423     build/libs \
424     build/target \
...
433 else    # !BUILD_TINY_ANDROID
434 
435 #
436 # Typical build; include any Android.mk files we can find.
437 #
438 subdirs := $(TOP)
439 
440 FULL_BUILD := true
441 
442 endif   # !BUILD_TINY_ANDROID
443 
444 endif   # !SDK_ONLY
...
464 #
465 # Include all of the makefiles in the system
466 #
467 
468 # Can't use first-makefiles-under here because
469 # --mindepth=2 makes the prunes not work.
470 subdir_makefiles := \
471     $(shell build/tools/findleaves.py --prune=out --prune=.repo --prune=.git $(subdirs) Android.mk)
472 
473 include $(subdir_makefiles)
復制代碼

 

上一節只是講了main.mk第49行中包含了config.mk,我們繼續分析。

142行包含了:build/core/definitions.mk,該文件定義了很多全局變量與函數。

如下列常見函數:

    my-dir:返回當前路徑

    all-java-files-under:獲得指定目錄及子目錄一所有java文件

    all-subdir-c-files:獲得當前目錄下及子目錄下所有c文件

354~444行,定義了subdirs變量,依據不同的用戶編譯條件,而包含Android源碼中不同的目錄。

470行,定義了subdir_makefile變量,其值為subdirs定義的目錄中的Android.mk文件。

473行,將所有編譯目錄中的Android.mk文件包含進來。

3. build/target/board/Android.mk

復制代碼
 26 ifeq (,$(wildcard $(TARGET_DEVICE_DIR)/AndroidBoard.mk))
 27   ifeq (,$(wildcard $(TARGET_DEVICE_DIR)/Android.mk))
 28     $(error Missing "$(TARGET_DEVICE_DIR)/AndroidBoard.mk")
 29   else
 30     # TODO: Remove this check after people have had a chance to switch,
 31     # after April 2009.
 32     $(error Please rename "$(TARGET_DEVICE_DIR)/Android.mk" to "$(TARGET_DEVICE_DIR)/AndroidBoard.mk")
 33   endif
 34 endif
 35 include $(TARGET_DEVICE_DIR)/AndroidBoard.mk
復制代碼
[plain]  view plain  copy
 
  1. 由於將所有目錄中Android.mk文件include進來,build/target/board/Android.mk自然被包含進來,根據前面分析,TARGET_DEVICE_DIR = </span>vendor/*/fs100,<span style="box-sizing: border-box;">其中26~35行用來判斷對應的產品目錄下是否存在AndrodiBoard.mk,如果不存在,提示出錯退出,如果存在,將其包含到編譯腳本中。  

 

由此可見:我們必須要在產品目錄下創建AndrodiBoard.mk文件,來描述開發板相關配置項,我們可以借鑒:build/target/board/generic/AndroidBoard.mk內容,同時根據前面所分析,還要創建BoardConfig.mk文件。

 

$cp build/target/board/generic/AndroidBoard.mk build/target/board/generic/BoardConfig.mk  vendor/farsight/fs100/

 

[plain]  view plain  copy
 
  1. <span style="box-sizing: border-box;">至此,自定義Android編譯選項基本步驟已經分部分析完,細節還需要針對不同開發板具體分析。</span>  

 

總結:

build/core/main.mk包含了config.mk,它主要定義了編譯全部代碼的依賴關系

      build/core/config.mk         定義了大量的編譯腳本命令,編譯時用到的環境變量,引入了envsetup.mk 文件,加載board相關配置文件。
      build/core/envsetup.mk   定義了編譯時用到的大量OUT輸出目錄,加載product_config.mk文件
      build/core/product_config.mk 定義了Vendor目錄下Product相關配置文件解析腳本,讀取AndrodProducts.mk生成TARGET_DEVICE變量
      build/target/product          product config
      build/target/board            board config
      build/core/combo             build flags config 

      這里解釋下這里的board和product。borad主要是設計到硬件芯片的配置,比如是否提供硬件的某些功能,比如說GPU等等,或者芯片支持浮 點運算等等。product是指針對當前的芯片配置定義你將要生產產品的個性配置,主要是指APK方面的配置,哪些APK會包含在哪個product中, 哪些APK在當前product中是不提供的。
      config.mk是一個總括性的東西,它里面定義了各種module編譯所需要使用的HOST工具以及如何來編譯各種模塊,比如說 BUILT_PREBUILT就定義了如何來編譯預編譯模塊。envsetup.mk主要會讀取由envsetup.sh寫入環境變量中的一些變量來配置編譯過程中的輸出目錄,combo里面主要定義了各種Host和Target結合的編譯器和編譯選項。

1. 在vendor目錄下創建自己公司目錄,然后在公司目錄下創建一個新的vendorsetup.sh,在里面添加上自己的產品編譯項

$mkdir vendor/farsight/  
$touch vendor/farsight/vendorsetup.sh  
$echo "add_lunch_combo fs100-eng" > vendor/farsight/vendorsetup.sh  

 

[plain]  view plain  copy
 
  1. <span style="box-sizing: border-box;">2. </span>仿着Android示例代碼,在公司目錄下創建products目錄  
 $mkdir -p vendor/farsight/products
[plain]  view plain  copy
 
  1. <span style="box-sizing: border-box;">3. </span>仿着Android示例代碼,在products目錄下創建兩個mk文件  
$touch vendor/farsight/products/AndroidProduct.mk vendor/farsight/products/fs100.mk
[plain]  view plain  copy
 
  1.     在AndroidProduct.mk里添加如下內容:  
PRODUCT_MAKEFILES := $(LOCAL_DIR)/fs100.mk  
[plain]  view plain  copy
 
  1.     在產品配置文件里添加最基本信息  
復制代碼
 PRODUCT_PACKAGES := \  
     IM \  
     VoiceDialer  
   
 $(call inherit-product, build/target/product/generic.mk)  
   
 # Overrides  
 PRODUCT_MANUFACTURER := farsight  
 PRODUCT_NAME := fs100  
 PRODUCT_DEVICE := fs100  
復制代碼
[plain]  view plain  copy
 
  1. 借鑒build/target/board/generic/AndroidBoard.mk和BoardConfig.mk,創建對應文件。  
$cp build/target/board/generic/AndroidBoard.mk build/target/board/generic/BoardConfig.mk  vendor/farsight/fs100/

 


 

 

 

 

 

 

 

 

版權聲明:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/huangxiaominglipeng/article/details/41745199


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM