(轉(zhuǎn)載)理解 Android Build 系統(tǒng)

前言
Android Build 系統(tǒng)是 Android 源碼的一部分。關(guān)于如何獲取 Android 源碼,請參照 Android Source 官方網(wǎng)站:
http://source.android.com/source/downloading.html适荣。
Android Build 系統(tǒng)用來編譯 Android 系統(tǒng),Android SDK 以及相關(guān)文檔裆赵。該系統(tǒng)主要由 Make 文件,Shell 腳本以及 Python 腳本組成杠园,其中最主要的是 Make 文件顾瞪。
眾所周知,Android 是一個開源的操作系統(tǒng)抛蚁。Android 的源碼中包含了大量的開源項目以及許多的模塊陈醒。不同產(chǎn)商的不同設(shè)備對于 Android 系統(tǒng)的定制都是不一樣的。
如何將這些項目和模塊的編譯統(tǒng)一管理起來瞧甩,如何能夠在不同的操作系統(tǒng)上進行編譯钉跷,如何在編譯時能夠支持面向不同的硬件設(shè)備,不同的編譯類型肚逸,且還要提供面向各個產(chǎn)商的定制擴展爷辙,是非常有難度的。
但 Android Build 系統(tǒng)很好的解決了這些問題朦促,這里面有很多值得我們開發(fā)人員學(xué)習(xí)的地方膝晾。
對于 Android 平臺開發(fā)人員來說,本文可以幫助你熟悉你每天接觸到的構(gòu)建環(huán)境务冕。
對于其他開發(fā)人員來說血当,本文可以作為一個 GNU Make 的使用案例,學(xué)習(xí)這些成功案例,可以提升我們的開發(fā)經(jīng)驗臊旭。
回頁首
概述
Build 系統(tǒng)中最主要的處理邏輯都在 Make 文件中落恼,而其他的腳本文件只是起到一些輔助作用,由于篇幅所限离熏,本文只探討 Make 文件中的內(nèi)容佳谦。
整個 Build 系統(tǒng)中的 Make 文件可以分為三類:
第一類是 Build 系統(tǒng)核心文件,此類文件定義了整個 Build 系統(tǒng)的框架滋戳,而其他所有 Make 文件都是在這個框架的基礎(chǔ)上編寫出來的钻蔑。
圖 1 是 Android 源碼樹的目錄結(jié)構(gòu),Build 系統(tǒng)核心文件全部位于 /build/core(本文所提到的所有路徑都是以 Android 源碼樹作為背景的胧瓜,“/”指的是源碼樹的根目錄矢棚,與文件系統(tǒng)無關(guān))目錄下。
圖 1. Android 源碼樹的目錄結(jié)構(gòu)

(轉(zhuǎn)載)理解 Android Build 系統(tǒng) Linux 第1張
第二類是針對某個產(chǎn)品(一個產(chǎn)品可能是某個型號的手機或者平板電腦)的 Make 文件府喳,這些文件通常位于 device 目錄下,該目錄下又以公司名以及產(chǎn)品名分為兩級目錄蘑拯,圖 2 是 device 目錄下子目錄的結(jié)構(gòu)钝满。對于一個產(chǎn)品的定義通常需要一組文件,這些文件共同構(gòu)成了對于這個產(chǎn)品的定義申窘。例如弯蚜,/device/sony/it26 目錄下的文件共同構(gòu)成了對于 Sony LT26 型號手機的定義。
圖 2. device 目錄下子目錄的結(jié)構(gòu)
(轉(zhuǎn)載)理解 Android Build 系統(tǒng) Linux 第2張
第三類是針對某個模塊(關(guān)于模塊后文會詳細討論)的 Make 文件剃法。整個系統(tǒng)中碎捺,包含了大量的模塊,每個模塊都有一個專門的 Make 文件贷洲,這類文件的名稱統(tǒng)一為“Android.mk”收厨,該文件中定義了如何編譯當(dāng)前模塊。Build 系統(tǒng)會在整個源碼樹中掃描名稱為“Android.mk”的文件并根據(jù)其中的內(nèi)容執(zhí)行模塊的編譯优构。
回頁首
編譯 Android 系統(tǒng)
執(zhí)行編譯
Android 系統(tǒng)的編譯環(huán)境目前只支持 Ubuntu 以及 Mac OS 兩種操作系統(tǒng)诵叁。關(guān)于編譯環(huán)境的構(gòu)建方法請參見以下路徑:http://source.android.com/source/initializing.html
在完成編譯環(huán)境的準備工作以及獲取到完整的 Android 源碼之后,想要編譯出整個 Android 系統(tǒng)非常的容易:
打開控制臺之后轉(zhuǎn)到 Android 源碼的根目錄钦椭,然后執(zhí)行如清單 1 所示的三條命令即可("$"
是命令提示符拧额,不是命令的一部分。):
完整的編譯時間依賴于編譯主機的配置彪腔,在筆者的 Macbook Pro(OS X 10.8.2, i7 2G CPU侥锦,8G RAM, 120G SSD)上使用 8 個 Job 同時編譯共需要一個半小時左右的時間。
清單 1. 編譯 Android 系統(tǒng)
$ source build/envsetup.sh $ lunch full-eng $ make -j8

這三行命令的說明如下:
第一行命令“source build/envsetup.sh”引入了 build/envsetup.sh
腳本德挣。該腳本的作用是初始化編譯環(huán)境恭垦,并引入一些輔助的 Shell 函數(shù),這其中就包括第二步使用 lunch 函數(shù)。
除此之外署照,該文件中還定義了其他一些常用的函數(shù)祸泪,它們?nèi)绫?1 所示:
表 1. build/envsetup.sh 中定義的常用函數(shù)
名稱
說明

croot
切換到源碼樹的根目錄

m
在源碼樹的根目錄執(zhí)行 make

mm
Build 當(dāng)前目錄下的模塊

mmm
Build 指定目錄下的模塊

cgrep
在所有 C/C++ 文件上執(zhí)行 grep

jgrep
在所有 Java 文件上執(zhí)行 grep

resgrep
在所有 res/*.xml 文件上執(zhí)行 grep

godir
轉(zhuǎn)到包含某個文件的目錄路徑

printconfig
顯示當(dāng)前 Build 的配置信息

add_lunch_combo
在 lunch 函數(shù)的菜單中添加一個條目

第二行命令“l(fā)unch full-eng”是調(diào)用 lunch 函數(shù),并指定參數(shù)為“full-eng”建芙。lunch 函數(shù)的參數(shù)用來指定此次編譯的目標設(shè)備以及編譯類型没隘。在這里,這兩個值分別是“full”和“eng”禁荸∮移眩“full”是 Android 源碼中已經(jīng)定義好的一種產(chǎn)品,是為模擬器而設(shè)置的赶熟。而編譯類型會影響最終系統(tǒng)中包含的模塊瑰妄,關(guān)于編譯類型將在表 7 中詳細講解。
如果調(diào)用 lunch 函數(shù)的時候沒有指定參數(shù)映砖,那么該函數(shù)將輸出列表以供選擇间坐,該列表類似圖 3 中的內(nèi)容(列表的內(nèi)容會根據(jù)當(dāng)前 Build 系統(tǒng)中包含的產(chǎn)品配置而不同,具體參見后文“添加新的產(chǎn)品”)邑退,此時可以通過輸入編號或者名稱進行選擇竹宋。
圖 3. lunch 函數(shù)的輸出


(轉(zhuǎn)載)理解 Android Build 系統(tǒng) Linux 第3張

第三行命令“make -j8”才真正開始執(zhí)行編譯。make 的參數(shù)“-j”指定了同時編譯的 Job 數(shù)量地技,這是個整數(shù)蜈七,該值通常是編譯主機 CPU 支持的并發(fā)線程總數(shù)的 1 倍或 2 倍(例如:在一個 4 核,每個核支持兩個線程的 CPU 上莫矗,可以使用 make -j8 或 make -j16)飒硅。在調(diào)用 make 命令時,如果沒有指定任何目標作谚,則將使用默認的名稱為“droid”目標三娩,該目標會編譯出完整的 Android 系統(tǒng)鏡像。
Build 結(jié)果的目錄結(jié)構(gòu)
所有的編譯產(chǎn)物都將位于 /out 目錄下食磕,該目錄下主要有以下幾個子目錄:
/out/host/:該目錄下包含了針對主機的 Android 開發(fā)工具的產(chǎn)物尽棕。即 SDK 中的各種工具,例如:emulator彬伦,adb滔悉,aapt 等。

/out/target/common/:該目錄下包含了針對設(shè)備的共通的編譯產(chǎn)物单绑,主要是 Java 應(yīng)用代碼和 Java 庫回官。

/out/target/product/<product_name>/:包含了針對特定設(shè)備的編譯結(jié)果以及平臺相關(guān)的 C/C++ 庫和二進制文件。其中搂橙,<product_name>是具體目標設(shè)備的名稱歉提。

/out/dist/:包含了為多種分發(fā)而準備的包,通過“make dist
target”將文件拷貝到該目錄,默認的編譯目標不會產(chǎn)生該目錄苔巨。

Build 生成的鏡像文件
Build 的產(chǎn)物中最重要的是三個鏡像文件版扩,它們都位于 /out/target/product/<product_name>/ 目錄下。
這三個文件是:
system.img:包含了 Android OS 的系統(tǒng)文件侄泽,庫礁芦,可執(zhí)行文件以及預(yù)置的應(yīng)用程序,將被掛載為根分區(qū)悼尾。

ramdisk.img:在啟動時將被 Linux 內(nèi)核掛載為只讀分區(qū)柿扣,它包含了 /init 文件和一些配置文件。它用來掛載其他系統(tǒng)鏡像并啟動 init 進程闺魏。

userdata.img:將被掛載為 /data未状,包含了應(yīng)用程序相關(guān)的數(shù)據(jù)以及和用戶相關(guān)的數(shù)據(jù)。

回頁首
Make 文件說明
整個 Build 系統(tǒng)的入口文件是源碼樹根目錄下名稱為“Makefile”的文件析桥,當(dāng)在源代碼根目錄上調(diào)用 make 命令時司草,make 命令首先將讀取該文件。
Makefile 文件的內(nèi)容只有一行:“include build/core/main.mk
”烹骨。該行代碼的作用很明顯:包含 build/core/main.mk 文件翻伺。在 main.mk 文件中又會包含其他的文件,其他文件中又會包含更多的文件沮焕,這樣就引入了整個 Build 系統(tǒng)。
這些 Make 文件間的包含關(guān)系是相當(dāng)復(fù)雜的拉宗,圖 3 描述了這種關(guān)系峦树,該圖中黃色標記的文件(且除了 $
開頭的文件)都位于 build/core/ 目錄下。
圖 4. 主要的 Make 文件及其包含關(guān)系

(轉(zhuǎn)載)理解 Android Build 系統(tǒng) Linux 第4張
表 2 總結(jié)了圖 4 中提到的這些文件的作用:
表 2. 主要的 Make 文件的說明
文件名
說明

main.mk
最主要的 Make 文件旦事,該文件中首先將對編譯環(huán)境進行檢查魁巩,同時引入其他的 Make 文件。另外姐浮,該文件中還定義了幾個最主要的 Make 目標谷遂,例如 droid,sdk卖鲤,等(參見后文“Make 目標說明”)肾扰。

help.mk
包含了名稱為 help 的 Make 目標的定義,該目標將列出主要的 Make 目標及其說明蛋逾。

pathmap.mk
將許多頭文件的路徑通過名值對的方式定義為映射表集晚,并提供 include-path-for 函數(shù)來獲取。例如区匣,通過 $(call include-path-for, frameworks-native)
便可以獲取到 framework 本地代碼需要的頭文件路徑偷拔。

envsetup.mk
配置 Build 系統(tǒng)需要的環(huán)境變量,例如:TARGET_PRODUCT,TARGET_BUILD_VARIANT莲绰,HOST_OS欺旧,HOST_ARCH 等。當(dāng)前編譯的主機平臺信息(例如操作系統(tǒng)蛤签,CPU 類型等信息)就是在這個文件中確定的辞友。另外,該文件中還指定了各種編譯結(jié)果的輸出路徑顷啼。

combo/select.mk
根據(jù)當(dāng)前編譯器的平臺選擇平臺相關(guān)的 Make 文件踏枣。

dumpvar.mk
在 Build 開始之前,顯示此次 Build 的配置信息钙蒙。

config.mk
整個 Build 系統(tǒng)的配置文件茵瀑,最重要的 Make 文件之一。該文件中主要包含以下內(nèi)容:定義了許多的常量來負責(zé)不同類型模塊的編譯躬厌。

定義編譯器參數(shù)以及常見文件后綴马昨,例如 .zip,.jar.apk。

根據(jù) BoardConfig.mk 文件扛施,配置產(chǎn)品相關(guān)的參數(shù)鸿捧。

設(shè)置一些常用工具的路徑,例如 flex疙渣,e2fsck匙奴,dx。

definitions.mk
最重要的 Make 文件之一妄荔,在其中定義了大量的函數(shù)泼菌。這些函數(shù)都是 Build 系統(tǒng)的其他文件將用到的。例如:my-dir啦租,all-subdir-makefiles哗伯,find-subdir-files,sign-package 等篷角,關(guān)于這些函數(shù)的說明請參見每個函數(shù)的代碼注釋焊刹。

distdir.mk
針對 dist 目標的定義。dist 目標用來拷貝文件到指定路徑恳蹲。

dex_preopt.mk
針對啟動 jar 包的預(yù)先優(yōu)化虐块。

pdk_config.mk
顧名思義,針對 pdk(Platform Developement Kit)的配置文件阱缓。

**${ONE_SHOT_MAKEFILE}
**
ONE_SHOT_MAKEFILE 是一個變量非凌,當(dāng)使用“mm”編譯某個目錄下的模塊時,此變量的值即為當(dāng)前指定路徑下的 Make 文件的路徑荆针。

**${subdir_makefiles}
**
各個模塊的 Android.mk 文件的集合敞嗡,這個集合是通過 Python 腳本掃描得到的颁糟。

post_clean.mk
在前一次 Build 的基礎(chǔ)上檢查當(dāng)前 Build 的配置,并執(zhí)行必要清理工作喉悴。

legacy_prebuilts.mk
該文件中只定義了 GRANDFATHERED_ALL_PREBUILT 變量棱貌。

Makefile
被 main.mk 包含,該文件中的內(nèi)容是輔助 main.mk 的一些額外內(nèi)容箕肃。

Android 源碼中包含了許多的模塊婚脱,模塊的類型有很多種,例如:Java 庫勺像,C/C++ 庫障贸,APK 應(yīng)用,以及可執(zhí)行文件等 吟宦。并且篮洁,Java 或者 C/C++ 庫還可以分為靜態(tài)的或者動態(tài)的,庫或可執(zhí)行文件既可能是針對設(shè)備(本文的“設(shè)備”指的是 Android 系統(tǒng)將被安裝的設(shè)備殃姓,例如某個型號的手機或平板)的也可能是針對主機(本文的“主機”指的是開發(fā) Android 系統(tǒng)的機器袁波,例如裝有 Ubuntu 操作系統(tǒng)的 PC 機或裝有 MacOS 的 iMac 或 Macbook)的。不同類型的模塊的編譯步驟和方法是不一樣蜗侈,為了能夠一致且方便的執(zhí)行各種類型模塊的編譯篷牌,在 config.mk 中定義了許多的常量,這其中的每個常量描述了一種類型模塊的編譯方式踏幻,這些常量有:
BUILD_HOST_STATIC_LIBRARY

BUILD_HOST_SHARED_LIBRARY

BUILD_STATIC_LIBRARY

BUILD_SHARED_LIBRARY

BUILD_EXECUTABLE

BUILD_HOST_EXECUTABLE

BUILD_PACKAGE

BUILD_PREBUILT

BUILD_MULTI_PREBUILT

BUILD_HOST_PREBUILT

BUILD_JAVA_LIBRARY

BUILD_STATIC_JAVA_LIBRARY

BUILD_HOST_JAVA_LIBRARY

通過名稱大概就可以猜出每個變量所對應(yīng)的模塊類型枷颊。(在模塊的 Android.mk 文件中,只要包含進這里對應(yīng)的常量便可以執(zhí)行相應(yīng)類型模塊的編譯该面。對于 Android.mk 文件的編寫請參見后文:“添加新的模塊”偷卧。)
這些常量的值都是另外一個 Make 文件的路徑,詳細的編譯方式都是在對應(yīng)的 Make 文件中定義的吆倦。這些常量和 Make 文件的是一一對應(yīng)的,對應(yīng)規(guī)則也很簡單:常量的名稱是 Make 文件的文件名除去后綴全部改為大寫然后加上“BUILD_”作為前綴坐求。例如常量 BUILD_HOST_PREBUILT 的值對應(yīng)的文件就是 host_prebuilt.mk蚕泽。
這些 Make 文件的說明如表 3 所示:
表 3. 各種模塊的編譯方式的定義文件
文件名
說明

host_static_library.mk
定義了如何編譯主機上的靜態(tài)庫。

host_shared_library.mk
定義了如何編譯主機上的共享庫桥嗤。

static_library.mk
定義了如何編譯設(shè)備上的靜態(tài)庫须妻。

shared_library.mk
定義了如何編譯設(shè)備上的共享庫。

executable.mk
定義了如何編譯設(shè)備上的可執(zhí)行文件泛领。

host_executable.mk
定義了如何編譯主機上的可執(zhí)行文件荒吏。

package.mk
定義了如何編譯 APK 文件。

prebuilt.mk
定義了如何處理一個已經(jīng)編譯好的文件 ( 例如 Jar 包 )渊鞋。

multi_prebuilt.mk
定義了如何處理一個或多個已編譯文件绰更,該文件的實現(xiàn)依賴 prebuilt.mk瞧挤。

host_prebuilt.mk
處理一個或多個主機上使用的已編譯文件,該文件的實現(xiàn)依賴 multi_prebuilt.mk儡湾。

java_library.mk
定義了如何編譯設(shè)備上的共享 Java 庫特恬。

static_java_library.mk
定義了如何編譯設(shè)備上的靜態(tài) Java 庫。

host_java_library.mk
定義了如何編譯主機上的共享 Java 庫徐钠。

不同類型的模塊的編譯過程會有一些相同的步驟癌刽,例如:編譯一個 Java 庫和編譯一個 APK 文件都需要定義如何編譯 Java 文件。因此尝丐,表 3 中的這些 Make 文件的定義中會包含一些共同的代碼邏輯显拜。為了減少代碼冗余,需要將共同的代碼復(fù)用起來爹袁,復(fù)用的方式是將共同代碼放到專門的文件中远荠,然后在其他文件中包含這些文件的方式來實現(xiàn)的。這些包含關(guān)系如圖 5 所示呢簸。由于篇幅關(guān)系矮台,這里就不再對其他文件做詳細描述(其實這些文件從文件名稱中就可以大致猜出其作用)。
圖 5. 模塊的編譯方式定義文件的包含關(guān)系

(轉(zhuǎn)載)理解 Android Build 系統(tǒng) Linux 第5張
回頁首
Make 目標說明
make /make droid
如果在源碼樹的根目錄直接調(diào)用“make”命令而不指定任何目標根时,則會選擇默認目標:“droid”(在 main.mk 中定義)瘦赫。因此,這和執(zhí)行“make droid”效果是一樣的蛤迎。
droid 目標將編譯出整個系統(tǒng)的鏡像确虱。從源代碼到編譯出系統(tǒng)鏡像,整個編譯過程非常復(fù)雜替裆。這個過程并不是在 droid 一個目標中定義的校辩,而是 droid 目標會依賴許多其他的目標,這些目標的互相配合導(dǎo)致了整個系統(tǒng)的編譯辆童。
圖 6 描述了 droid 目標所依賴的其他目標:
圖 6. droid 目標所依賴的其他 Make 目標
(轉(zhuǎn)載)理解 Android Build 系統(tǒng) Linux 第6張
圖 6 中這些目標的說明如表 4 所示:
表 4. droid 所依賴的其他 Make 目標的說明
名稱
說明

apps_only
該目標將編譯出當(dāng)前配置下不包含 user宜咒,userdebug,eng 標簽(關(guān)于標簽把鉴,請參見后文“添加新的模塊”)的應(yīng)用程序故黑。

droidcore
該目標僅僅是所依賴的幾個目標的組合,其本身不做更多的處理庭砍。

dist_files
該目標用來拷貝文件到 /out/dist 目錄场晶。

files
該目標僅僅是所依賴的幾個目標的組合,其本身不做更多的處理怠缸。

prebuilt
該目標依賴于 $(ALL_PREBUILT)
诗轻,$(ALL_PREBUILT)
的作用就是處理所有已編譯好的文件。

**$(modules_to_install)
**
modules_to_install 變量包含了當(dāng)前配置下所有會被安裝的模塊(一個模塊是否會被安裝依賴于該產(chǎn)品的配置文件揭北,模塊的標簽等信息)扳炬,因此該目標將導(dǎo)致所有會被安裝的模塊的編譯吏颖。

**$(modules_to_check)
**
該目標用來確保我們定義的構(gòu)建模塊是沒有冗余的。

$(INSTALLED_ANDROID_INFO_TXT_TARGET)
**
該目標會生成一個關(guān)于當(dāng)前 Build 配置的設(shè)備信息的文件鞠柄,該文件的生成路徑是:out/target/product/<
product_name
>/android-info.txt

systemimage
生成 system.img侦高。

**$(INSTALLED_BOOTIMAGE_TARGET)
**
生成 boot.img。

**$(INSTALLED_RECOVERYIMAGE_TARGET)
**
生成 recovery.img厌杜。

**$(INSTALLED_USERDATAIMAGE_TARGET)
**
生成 userdata.img奉呛。

**$(INSTALLED_CACHEIMAGE_TARGET)
**
生成 cache.img。

$(INSTALLED_FILES_FILE)
**
該目標會生成 out/target/product/<
product_name
>/ installed-files.txt 文件夯尽,該文件中內(nèi)容是當(dāng)前系統(tǒng)鏡像中已經(jīng)安裝的文件列表瞧壮。

其他目標
Build 系統(tǒng)中包含的其他一些 Make 目標說明如表 5 所示:
表 5. 其他主要 Make 目標
Make 目標
說明

make clean
執(zhí)行清理,等同于:rm -rf out/匙握。

make sdk
編譯出 Android 的 SDK咆槽。

make clean-sdk
清理 SDK 的編譯產(chǎn)物。

make update-api
更新 API圈纺。在 framework API 改動之后秦忿,需要首先執(zhí)行該命令來更新 API,公開的 API 記錄在 frameworks/base/api 目錄下蛾娶。

make dist
執(zhí)行 Build灯谣,并將 MAKECMDGOALS 變量定義的輸出文件拷貝到 /out/dist 目錄。

make all
編譯所有內(nèi)容蛔琅,不管當(dāng)前產(chǎn)品的定義中是否會包含胎许。

make help
幫助信息,顯示主要的 make 目標罗售。

make snod
從已經(jīng)編譯出的包快速重建系統(tǒng)鏡像辜窑。

make libandroid_runtime
編譯所有 JNI framework 內(nèi)容。

make****framework
編譯所有 Java framework 內(nèi)容寨躁。

make****services
編譯系統(tǒng)服務(wù)和相關(guān)內(nèi)容穆碎。

make <local_target>
編譯一個指定的模塊,local_target 為模塊的名稱职恳。

make clean-<local_target>
清理一個指定模塊的編譯結(jié)果惨远。

make****dump-products
顯示所有產(chǎn)品的編譯配置信息,例如:產(chǎn)品名话肖,產(chǎn)品支持的地區(qū)語言,產(chǎn)品中會包含的模塊等信息葡幸。

make****PRODUCT-xxx-yyy
編譯某個指定的產(chǎn)品最筒。

make****bootimage
生成 boot.img

make****recoveryimage
生成 recovery.img

make****userdataimage
生成 userdata.img

make****cacheimage
生成 cache.img

回頁首
在 Build 系統(tǒng)中添加新的內(nèi)容
添加新的產(chǎn)品
當(dāng)我們要開發(fā)一款新的 Android 產(chǎn)品的時候,我們首先就需要在 Build 系統(tǒng)中添加對于該產(chǎn)品的定義蔚叨。
在 Android Build 系統(tǒng)中對產(chǎn)品定義的文件通常位于 device 目錄下(另外還有一個可以定義產(chǎn)品的目錄是 vender 目錄床蜘,這是個歷史遺留目錄辙培,Google 已經(jīng)建議不要在該目錄中進行定義,而應(yīng)當(dāng)選擇 device 目錄)邢锯。device 目錄下根據(jù)公司名以及產(chǎn)品名分為二級目錄扬蕊,這一點我們在概述中已經(jīng)提到過。
通常丹擎,對于一個產(chǎn)品的定義通常至少會包括四個文件:AndroidProducts.mk尾抑,產(chǎn)品版本定義文件,BoardConfig.mk 以及 verndorsetup.sh蒂培。下面我們來詳細說明這幾個文件再愈。
AndroidProducts.mk:該文文件中的內(nèi)容很簡單,其中只需要定義一個變量护戳,名稱為“PRODUCT_MAKEFILES”翎冲,該變量的值為產(chǎn)品版本定義文件名的列表,例如:

PRODUCT_MAKEFILES := \ $(LOCAL_DIR)/full_stingray.mk \ $(LOCAL_DIR)/stingray_emu.mk \ $(LOCAL_DIR)/generic_stingray.mk

產(chǎn)品版本定義文件:顧名思義媳荒,該文件中包含了對于特定產(chǎn)品版本的定義抗悍。該文件可能不只一個,因為同一個產(chǎn)品可能會有多種版本(例如钳枕,面向中國地區(qū)一個版本缴渊,面向美國地區(qū)一個版本)。該文件中可以定義的變量以及含義說明如表 6 所示:

表 6. 產(chǎn)品版本定義文件中的變量及其說明
常量
說明

PRODUCT_NAME
最終用戶將看到的完整產(chǎn)品名么伯,會出現(xiàn)在“關(guān)于手機”信息中疟暖。

PRODUCT_MODEL
產(chǎn)品的型號,這也是最終用戶將看到的田柔。

PRODUCT_LOCALES
該產(chǎn)品支持的地區(qū)俐巴,以空格分格,例如:en_GB de_DE es_ES fr_CA硬爆。

PRODUCT_PACKAGES
該產(chǎn)品版本中包含的 APK 應(yīng)用程序欣舵,以空格分格,例如:Calendar Contacts缀磕。

PRODUCT_DEVICE
該產(chǎn)品的工業(yè)設(shè)計的名稱缘圈。

PRODUCT_MANUFACTURER
制造商的名稱。

PRODUCT_BRAND
該產(chǎn)品專門定義的商標(如果有的話)袜蚕。

PRODUCT_PROPERTY_OVERRIDES
對于商品屬性的定義糟把。

PRODUCT_COPY_FILES
編譯該產(chǎn)品時需要拷貝的文件,以“源路徑 : 目標路徑”的形式牲剃。

PRODUCT_OTA_PUBLIC_KEYS
對于該產(chǎn)品的 OTA 公開 key 的列表遣疯。

PRODUCT_POLICY
產(chǎn)品使用的策略。

PRODUCT_PACKAGE_OVERLAYS
指出是否要使用默認的資源或添加產(chǎn)品特定定義來覆蓋凿傅。

PRODUCT_CONTRIBUTORS_FILE
HTML 文件缠犀,其中包含項目的貢獻者数苫。

PRODUCT_TAGS
該產(chǎn)品的標簽,以空格分格辨液。

通常情況下虐急,我們并不需要定義所有這些變量。Build 系統(tǒng)的已經(jīng)預(yù)先定義好了一些組合滔迈,它們都位于 /build/target/product 下止吁,每個文件定義了一個組合,我們只要繼承這些預(yù)置的定義亡鼠,然后再覆蓋自己想要的變量定義即可赏殃。例如:

繼承 full_base.mk 文件中的定義 $(call inherit-product, $(SRC_TARGET_DIR)/product/full_base.mk) # 覆蓋其中已經(jīng)定義的一些變量 PRODUCT_NAME := full_lt26 PRODUCT_DEVICE := lt26 PRODUCT_BRAND := Android PRODUCT_MODEL := Full Android on LT26

BoardConfig.mk:該文件用來配置硬件主板,它其中定義的都是設(shè)備底層的硬件特性间涵。例如:該設(shè)備的主板相關(guān)信息仁热,Wifi 相關(guān)信息,還有 bootloader勾哩,內(nèi)核抗蠢,radioimage 等信息。對于該文件的示例思劳,請參看 Android 源碼樹已經(jīng)有的文件迅矛。

vendorsetup.sh:該文件中作用是通過 add_lunch_combo 函數(shù)在 lunch 函數(shù)中添加一個菜單選項。該函數(shù)的參數(shù)是產(chǎn)品名稱加上編譯類型潜叛,中間以“-”連接秽褒,例如:add_lunch_combo full_lt26-userdebug。/build/envsetup.sh 會掃描所有 device 和 vender 二 級目 錄下的名稱 為"vendorsetup.sh"文件威兜,并根據(jù)其中的內(nèi)容來確定 lunch 函數(shù)的 菜單選項销斟。

在配置了以上的文件之后,便可以編譯出我們新添加的設(shè)備的系統(tǒng)鏡像了椒舵。
首先蚂踊,調(diào)用“source build/envsetup.sh
”該命令的輸出中會看到 Build 系統(tǒng)已經(jīng)引入了剛剛添加的 vendorsetup.sh 文件。
然后再調(diào)用“l(fā)unch”函數(shù)笔宿,該函數(shù)輸出的列表中將包含新添加的 vendorsetup.sh 中添加的條目犁钟。然后通過編號或名稱選擇即可。
最后泼橘,調(diào)用“make -j8”來執(zhí)行編譯即可涝动。
添加新的模塊
關(guān)于“模塊”的說明在上文中已經(jīng)提到過,這里不再贅述炬灭。
在源碼樹中捧存,一個模塊的所有文件通常都位于同一個文件夾中。為了將當(dāng)前模塊添加到整個 Build 系統(tǒng)中,每個模塊都需要一個專門的 Make 文件昔穴,該文件的名稱為“Android.mk”。Build 系統(tǒng)會掃描名稱為“Android.mk”的文件提前,并根據(jù)該文件中內(nèi)容編譯出相應(yīng)的產(chǎn)物吗货。
需要注意的是:在 Android Build 系統(tǒng)中,編譯是以模塊(而不是文件)作為單位的狈网,每個模塊都有一個唯一的名稱宙搬,一個模塊的依賴對象只能是另外一個模塊,而不能是其他類型的對象拓哺。對于已經(jīng)編譯好的二進制庫勇垛,如果要用來被當(dāng)作是依賴對象,那么應(yīng)當(dāng)將這些已經(jīng)編譯好的庫作為單獨的模塊士鸥。對于這些已經(jīng)編譯好的庫使用 BUILD_PREBUILT 或 BUILD_MULTI_PREBUILT闲孤。例如:當(dāng)編譯某個 Java 庫需要依賴一些 Jar 包時,并不能直接指定 Jar 包的路徑作為依賴烤礁,而必須首先將這些 Jar 包定義為一個模塊讼积,然后在編譯 Java 庫的時候通過模塊的名稱來依賴這些 Jar 包。
下面脚仔,我們就來講解 Android.mk 文件的編寫:
Android.mk 文件通常以以下兩行代碼作為開頭:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS)

這兩行代碼的作用是:
設(shè)置當(dāng)前模塊的編譯路徑為當(dāng)前文件夾路徑勤众。

清理(可能由其他模塊設(shè)置過的)編譯環(huán)境中用到的變量。

為了方便模塊的編譯鲤脏,Build 系統(tǒng)設(shè)置了很多的編譯環(huán)境變量们颜。要編譯一個模塊,只要在編譯之前根據(jù)需要設(shè)置這些變量然后執(zhí)行編譯即可猎醇。它們包括:
LOCAL_SRC_FILES:當(dāng)前模塊包含的所有源代碼文件窥突。

LOCAL_MODULE:當(dāng)前模塊的名稱,這個名稱應(yīng)當(dāng)是唯一的姑食,模塊間的依賴關(guān)系就是通過這個名稱來引用的波岛。

LOCAL_C_INCLUDES:C 或 C++ 語言需要的頭文件的路徑。

LOCAL_STATIC_LIBRARIES:當(dāng)前模塊在靜態(tài)鏈接時需要的庫的名稱音半。

LOCAL_SHARED_LIBRARIES:當(dāng)前模塊在運行時依賴的動態(tài)庫的名稱则拷。

LOCAL_CFLAGS:提供給 C/C++ 編譯器的額外編譯參數(shù)。

LOCAL_JAVA_LIBRARIES:當(dāng)前模塊依賴的 Java 共享庫曹鸠。

LOCAL_STATIC_JAVA_LIBRARIES:當(dāng)前模塊依賴的 Java 靜態(tài)庫煌茬。

LOCAL_PACKAGE_NAME:當(dāng)前 APK 應(yīng)用的名稱。

LOCAL_CERTIFICATE:簽署當(dāng)前應(yīng)用的證書名稱彻桃。

LOCAL_MODULE_TAGS:當(dāng)前模塊所包含的標簽坛善,一個模塊可以包含多個標簽。標簽的值可能是 debug, eng, user,development 或者 optional眠屎。其中剔交,optional 是默認標簽。標簽是提供給編譯類型使用的改衩。不同的編譯類型會安裝包含不同標簽的模塊岖常,關(guān)于編譯類型的說明如表 7 所示:

表 7. 編譯類型的說明
名稱
說明

eng
默認類型,該編譯類型適用于開發(fā)階段葫督。當(dāng)選擇這種類型時竭鞍,編譯結(jié)果將:安裝包含 eng, debug, user,development 標簽的模塊

安裝所有沒有標簽的非 APK 模塊

安裝所有產(chǎn)品定義文件中指定的 APK 模塊

user
該編譯類型適合用于最終發(fā)布階段橄镜。當(dāng)選擇這種類型時偎快,編譯結(jié)果將:安裝所有帶有 user 標簽的模塊

安裝所有沒有標簽的非 APK 模塊

安裝所有產(chǎn)品定義文件中指定的 APK 模塊,APK 模塊的標簽將被忽略

userdebug
該編譯類型適合用于 debug 階段。該類型和 user 一樣,除了:會安裝包含 debug 標簽的模塊

編譯出的系統(tǒng)具有 root 訪問權(quán)限

表 3 中的文件已經(jīng)定義好了各種類型模塊的編譯方式启盛。所以要執(zhí)行編譯,只需要引入表 3 中對應(yīng)的 Make 文件即可(通過常量的方式)惋戏。例如,要編譯一個 APK 文件他膳,只需要在 Android.mk 文件中响逢,加入“include $(BUILD_PACKAGE)

除此以外,Build 系統(tǒng)中還定義了一些便捷的函數(shù)以便在 Android.mk 中使用棕孙,包括:
$(call my-dir)
:獲取當(dāng)前文件夾路徑舔亭。

$(call all-java-files-under, <src>)
:獲取指定目錄下的所有 Java 文件。

$(call all-c-files-under, <src>)
:獲取指定目錄下的所有 C 語言文件蟀俊。

$(call all-Iaidl-files-under, <src>)
:獲取指定目錄下的所有 AIDL 文件钦铺。

$(call all-makefiles-under, <folder>)
:獲取指定目錄下的所有 Make 文件。

$(call intermediates-dir-for, <class>, <app_name>, <host or target>, <common?> )
:獲取 Build 輸出的目標文件夾路徑肢预。

清單 2 和清單 3 分別是編譯 APK 文件和編譯 Java 靜態(tài)庫的 Make 文件示例:
清單 2. 編譯一個 APK 文件
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) # 獲取所有子目錄中的 Java 文件 LOCAL_SRC_FILES := $(call all-subdir-java-files) # 當(dāng)前模塊依賴的靜態(tài) Java 庫矛洞,如果有多個以空格分隔 LOCAL_STATIC_JAVA_LIBRARIES := static-library # 當(dāng)前模塊的名稱 LOCAL_PACKAGE_NAME := LocalPackage # 編譯 APK 文件 include $(BUILD_PACKAGE)

清單 3. 編譯一個 Java 的靜態(tài)庫
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) # 獲取所有子目錄中的 Java 文件 LOCAL_SRC_FILES := $(call all-subdir-java-files) # 當(dāng)前模塊依賴的動態(tài) Java 庫名稱 LOCAL_JAVA_LIBRARIES := android.test.runner # 當(dāng)前模塊的名稱 LOCAL_MODULE := sample # 將當(dāng)前模塊編譯成一個靜態(tài)的 Java 庫 include $(BUILD_STATIC_JAVA_LIBRARY)

回頁首
結(jié)束語
整個 Build 系統(tǒng)包含了非常多的內(nèi)容,由于篇幅所限烫映,本文只能介紹其中最主要內(nèi)容沼本。
由于 Build 系統(tǒng)本身也是在隨著 Android 平臺不斷的開發(fā)過程中,所以不同的版本其中的內(nèi)容和定義可能會發(fā)生變化锭沟。網(wǎng)絡(luò)上關(guān)于該部分的資料很零碎抽兆,并且很多資料中的一些內(nèi)容已經(jīng)過時不再適用,再加上缺少官方文檔族淮,所以該部分的學(xué)習(xí)存在一定的難度辫红。
這就要求我們要有很強的代碼閱讀能力凭涂,畢竟代碼是不會說謊的。 要知道贴妻,對于我們這些開發(fā)人員來說切油,源代碼就是我們最忠實的朋友。 Use the Source,Luke!

參考資料
學(xué)習(xí)
Android Open Source Project:Android Source 官方網(wǎng)站名惩。

Android Build System:Build 系統(tǒng)中包含的說明文檔白翻。

GNU `make':GNU make 官方手冊。

Android Device:大致介紹了 Build 系統(tǒng)中的一些文件。

Build System:另一個關(guān)于 Build 系統(tǒng)的說明資料槐瑞。

Add new target:該文檔描述了如何添加一個新的產(chǎn)品目標。

原文地址:https://www.ibm.com/developerworks/cn/opensource/os-cn-android-build/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子慌植,更是在濱河造成了極大的恐慌,老刑警劉巖交汤,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件邀摆,死亡現(xiàn)場離奇詭異施逾,居然都是意外死亡曹仗,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來祥山,“玉大人斧散,你說我怎么就攤上這事栈暇。” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵馏慨,是天一觀的道長。 經(jīng)常有香客問我痪蝇,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上蹋宦,老公的妹妹穿的比我還像新娘守屉。我一直安慰自己,他們只是感情好蒿辙,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布俺叭。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天玩祟,我揣著相機與錄音腹缩,去河邊找鬼。 笑死转锈,一個胖子當(dāng)著我的面吹牛盘寡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播撮慨,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼竿痰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了砌溺?” 一聲冷哼從身側(cè)響起影涉,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎规伐,沒想到半個月后蟹倾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡猖闪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年鲜棠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片培慌。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡豁陆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出吵护,到底是詐尸還是另有隱情献联,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布何址,位于F島的核電站里逆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏用爪。R本人自食惡果不足惜原押,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望偎血。 院中可真熱鬧诸衔,春花似錦盯漂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谒亦,卻和暖如春竭宰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背份招。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工切揭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人锁摔。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓廓旬,卻偏偏與公主長得像,于是被迫代替她去往敵國和親谐腰。 傳聞我的和親對象是個殘疾皇子孕豹,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容