Android 编译系统分析(一)
一、Makefile的主要流程
以下主要流程都在build/core/main.mk里安排。
?主要配置文件:
??????build/core/config.mk?????????summary of config
??????build/core/envsetup.mk????generate dir config and so on
??????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結合的編譯器和編譯選項。
配置部分主要完成以下幾個工作:
a)?基于Android?產品的配置(product config):選擇構建安裝的運行程序(user package)
b)?設置?target?等相關變量TARGET_ARCH, TARGET_OS,TARGET_BUILD_TYPE, TARGET_PREBUILT_TAG
c)?根據編譯環境設置?host等相關變量HOST_OS, HOST_ARCH,HOST_BUILD_TYPE, HOST_PREBUILT_TAG
d)?編譯?target上運行程序所需的工具鏈及編譯參數設置,如linux-arm-cc,cflag,include目錄等。
e)?編譯?host上運行程序所需的工具鏈及編譯參數設置。
下圖簡要介紹了Android build system的配置部分的主要構成及相互關系。
?二、初始化參數設置
在main.mk里,簡單設置幾個主要編譯路徑的變量后,來到config.mk:
——————————————config.mk——————————————
其中設置了源文件的一系列路徑,包括頭文件、庫文件、服務、API已經編譯工具的路徑。(前36行)
從40行開始,定義一些編譯模塊的生成規則:
除了CLEAR_VARS是清楚本地變量之外,其他所有的都對應了一種模塊的生成規則,每一個本地模塊最后都會include其中的一種來生成目標模塊。
?回到 config.mk ,接著會嘗試讀取 buildspec.mk 的設置:
? 如同注釋所說,會嘗試查找 buildspec.mk ,如果文件不存在會自動使用環境變量的設置,如果仍然未定義,會按 arm 默認的設置去 build 。
這里的buildspec.mk可以自己創建,也可以將原先build/下的buildspec.mk.default直接命名為buildspec.mk并移到根目錄。
實際上,buildspec.mk配置都被屏蔽了,我們可以根據需要直接打開和修改一些變量。在這里我們可以加入自己的目標產品信息:
ifndef?TARGET_PRODUCT
TARGET_PRODUCT:=generic_x86
endif
以及輸出目錄設置:
OUT_DIR:=$(TOPDIR)generic_x86
?
三、讀取Product的設定
回到config.mk,接著進行全局變量設置,進入envsetup.mk:
——————————————envsetup.mk——————————————
里面的大部分函數都在build/envsetup.sh中定義。
首先,設置版本信息,(11行)在build/core/version_defaults.mk中具體定義平臺版本、SDK版本、Product版本,我們可以將BUILD_NUMBER作為我們產品generic_x86的version信息,當然,也可以自定義一個版本變量。
回到envsetup.mk,接著設置默認目標產品(generic),這里由于我們在buildspec.mk里設置過TARGET_PRODUCT,事實上這個變量值為generic_x86。
然后讀取product的設置(41行),具體實現在build/core/product_config.mk中,進而進入product.mk,從build/target/product/AndroidProducts.mk中讀出PRODUCT_MAKEFILES,這些makefile各自獨立定義product,而我們的產品generic_x86也應添加一個makefile文件generic_x86.mk。在generic_x86.mk中我們可以加入所需編譯的PRODUCT_PACKAGES。
下面為generic_x86.mk:
? 四、讀取BoardConfig接著回到config.mk,(114行)這里會搜索所有的BoardConfig.mk,主要有以下幾個地方:
?這里的TARGET_DEVICE就是generic_x86,就是說為了定義我們自己的產品generic_x86,我們要在build/target/board下添加一個自己的目錄generic_x86用來加載自己的board配置。
在BoardConfig.mk中會決定是否編譯bootloader、kernel等信息。
?
五、讀取所有Module
結束全局變量配置后,回到main.mk,馬上對編譯工具及版本進行檢查,錯誤便中斷編譯。
142行,包含文件definitions.mk,這里面定義了許多變量和函數供main.mk使用。main.mk第446行,這里會去讀取所有的Android.mk文件:
?其中include?$(ONE_SHOT_MAKEFILE)
這個ONE_SHOT_MAKEFILE是在前面提到的mm(envsetup.mk)函數中賦值的:
ONE_SHOT_MAKEFILE=$M?make?-C?$T?files?$@
? 回到 main.mk ,最終將遍歷查找到的所有子目錄下的 Android.mk 的路徑保存到 subdir_makefiles 變量里 (main.mk 里的 470 行 ) :我們在package/apps下每個模塊根目錄都能看到Android.mk,里面會去定義當前本地模塊的Tag:LOCAL_MODULE_TAGS,Android會通過這個Tag來決定哪些本地模塊會編譯進系統,通過PRODUCT和LOCAL_MODULE_TAGS來決定哪些應用包會編譯進系統。(前面說過,你也能通過buildspec.mk來制定你要編譯進系統的模塊)
這個過程在mian.mk的445行開始,最后需要編譯的模塊路徑打包到ALL_DEFAULT_INSTALLED_MODULES(602行):
?
六、產生相應的Rules,生成image
所有需要配置的準備工作都已完成,下面該決定如何生成image輸出文件了,這一過程實際上在build/core/Makefile中處理的。
這里定義各種img的生成方式,包括ramdisk.img、userdata.img、system.img、update.zip、recover.img等。具體對應的rules可以參考下圖:
http://img154.ph.126.net/5ekAHoRqfPf17ALmDJGDYA==/2269251262242871911.png
當Make?include所有的文件,完成對所有make文件的解析以后就會尋找生成對應目標的規則,依次生成它的依賴,直到所有滿足的模塊被編譯好,然后使用相應的工具打包成相應的img。
?
具體make操作:
完整編譯
我們在根目錄下輸入make命令即可開始完全編譯。這個命令實際編譯生成的默認目標是droid。
也就是說,大家敲入make實際上執行的make?droid。而接下來大家看看main.mk文件里最后面的部分,會有很多偽目標,如sdk、clean、clobber等,這些在默認的make?droid的命令下是不會執行的。我們可以在make后加上這些標簽來單獨實現一些操作。如:輸入make?sdk?將會生成該版本對應的SDK,輸入make?clean會清除上次編譯的輸出。
模塊編譯
有時候我們只修改了某一個模塊,希望能單獨編譯這個模塊而不是重新完整編譯一次,這時候我們要用到build/envsetup.sh中提供的幾個bash的幫助函數。
在源代碼根目錄下執行:
.?build/envsetup.sh(.后面有空格)
這樣大家相當于多了幾個可用的命令。
這時可以用help命令查看幫助信息:
其中對模塊編譯有幫助的是tapas、m、mm、mmm這幾個命令。
1、tapas——以交互方式設置build環境變量。
???輸入:tapas
第一步,選擇目標設備:
第二步,選擇代碼格式:
第三步,選擇產品平臺:
?
?注意:這里,Google源代碼里默認是generic,而我們針對自己的產品應修改成generic_x86
?具體在build/envsetup.sh里的函數chooseproduct()中對相應代碼進行修改。
2、m、mm、mmm使用獨立模塊的make命令。
幾個命令的功能使用help命令查看。
舉個例子,我們修改了Camera模塊的代碼,現在需要重新單獨編譯這一塊,這時可以使用mmm命令,后面跟指定模塊的路徑(注意是模塊的根目錄)。
具體如下:
mmm?packages/apps/Camera/
為了可以直接測試改動,編譯好后需要重新生成system.img
可以執行:make?snod
單獨編譯image文件
一般我們完整編譯后,會生成三個重要的image文件:ramdisk.img、system.img和userdata.img。當然我們可以分開單獨去編譯這三個目標:
make?ramdisk?——?ramdisk.img
make?userdataimage?——?userdata.img
make?systemimage??——?system.img
總結
以上是生活随笔為你收集整理的Android 编译系统分析(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: gcc 的visibility
 - 下一篇: Android 编译系统分析(二)