原文連結:https://www.ibm.com/developerworks/cn/opensource/os-cn-android-build/
Android Build 系統是 Android 源碼的一部分。關于如何擷取 Android 源碼,請參照 Android Source 官方網站:
http://source.android.com/source/downloading.html。
Android Build 系統用來編譯 Android 系統,Android SDK 以及相關文檔。該系統主要由 Make 檔案,Shell 腳本以及 Python 腳本組成,其中最主要的是 Make 檔案。
衆所周知,Android 是一個開源的作業系統。Android 的源碼中包含了大量的開源項目以及許多的子產品。不同産商的不同裝置對于 Android 系統的定制都是不一樣的。
如何将這些項目和子產品的編譯統一管理起來,如何能夠在不同的作業系統上進行編譯,如何在編譯時能夠支援面向不同的硬體裝置,不同的編譯類型,且還要提供面向各個産商的定制擴充,是非常有難度的。
但 Android Build 系統很好的解決了這些問題,這裡面有很多值得我們開發人員學習的地方。
對于 Android 平台開發人員來說,本文可以幫助你熟悉你每天接觸到的建構環境。
對于其他開發人員來說,本文可以作為一個 GNU Make 的使用案例,學習這些成功案例,可以提升我們的開發經驗。
概述
Build 系統中最主要的處理邏輯都在 Make 檔案中,而其他的腳本檔案隻是起到一些輔助作用,由于篇幅所限,本文隻探讨 Make 檔案中的内容。
整個 Build 系統中的 Make 檔案可以分為三類:
第一類是 Build 系統核心檔案,此類檔案定義了整個 Build 系統的架構,而其他所有 Make 檔案都是在這個架構的基礎上編寫出來的。
圖 1 是 Android 源碼樹的目錄結構,Build 系統核心檔案全部位于 /build/core(本文所提到的所有路徑都是以 Android 源碼樹作為背景的,“/”指的是源碼樹的根目錄,與檔案系統無關)目錄下。
圖 1. Android 源碼樹的目錄結構

第二類是針對某個産品(一個産品可能是某個型号的手機或者平闆電腦)的 Make 檔案,這些檔案通常位于 device 目錄下,該目錄下又以公司名以及産品名分為兩級目錄,圖 2 是 device 目錄下子目錄的結構。對于一個産品的定義通常需要一組檔案,這些檔案共同構成了對于這個産品的定義。例如,/device/sony/it26 目錄下的檔案共同構成了對于 Sony LT26 型号手機的定義。
圖 2. device 目錄下子目錄的結構
第三類是針對某個子產品(關于子產品後文會詳細讨論)的 Make 檔案。整個系統中,包含了大量的子產品,每個子產品都有一個專門的 Make 檔案,這類檔案的名稱統一為“Android.mk”,該檔案中定義了如何編譯目前子產品。Build 系統會在整個源碼樹中掃描名稱為“Android.mk”的檔案并根據其中的内容執行子產品的編譯。
編譯 Android 系統
執行編譯
Android 系統的編譯環境目前隻支援 Ubuntu 以及 Mac OS 兩種作業系統。關于編譯環境的建構方法請參見以下路徑:http://source.android.com/source/initializing.html
在完成編譯環境的準備工作以及擷取到完整的 Android 源碼之後,想要編譯出整個 Android 系統非常的容易:
打開控制台之後轉到 Android 源碼的根目錄,然後執行如清單 1 所示的三條指令即可(
"$"
是指令提示符,不是指令的一部分。):
完整的編譯時間依賴于編譯主機的配置,在筆者的 Macbook Pro(OS X 10.8.2, i7 2G CPU,8G RAM, 120G SSD)上使用 8 個 Job 同時編譯共需要一個半小時左右的時間。
清單 1. 編譯 Android 系統
1 2 3 | |
這三行指令的說明如下:
第一行指令“source build/envsetup.sh”引入了
build/envsetup.sh
腳本。該腳本的作用是初始化編譯環境,并引入一些輔助的 Shell 函數,這其中就包括第二步使用 lunch 函數。
除此之外,該檔案中還定義了其他一些常用的函數,它們如表 1 所示:
表 1. build/envsetup.sh 中定義的常用函數
名稱 | 說明 |
---|---|
croot | 切換到源碼樹的根目錄 |
m | 在源碼樹的根目錄執行 make |
mm | Build 目前目錄下的子產品 |
mmm | Build 指定目錄下的子產品 |
cgrep | 在所有 C/C++ 檔案上執行 grep |
jgrep | 在所有 Java 檔案上執行 grep |
resgrep | 在所有 res/*.xml 檔案上執行 grep |
godir | 轉到包含某個檔案的目錄路徑 |
printconfig | 顯示目前 Build 的配置資訊 |
add_lunch_combo | 在 lunch 函數的菜單中添加一個條目 |
第二行指令“lunch full-eng”是調用 lunch 函數,并指定參數為“full-eng”。lunch 函數的參數用來指定此次編譯的目标裝置以及編譯類型。在這裡,這兩個值分别是“full”和“eng”。“full”是 Android 源碼中已經定義好的一種産品,是為模拟器而設定的。而編譯類型會影響最終系統中包含的子產品,關于編譯類型将在表 7 中詳細講解。
如果調用 lunch 函數的時候沒有指定參數,那麼該函數将輸出清單以供選擇,該清單類似圖 3 中的内容(清單的内容會根據目前 Build 系統中包含的産品配置而不同,具體參見後文“添加新的産品”),此時可以通過輸入編号或者名稱進行選擇。
圖 3. lunch 函數的輸出
第三行指令“make -j8”才真正開始執行編譯。make 的參數“-j”指定了同時編譯的 Job 數量,這是個整數,該值通常是編譯主機 CPU 支援的并發線程總數的 1 倍或 2 倍(例如:在一個 4 核,每個核支援兩個線程的 CPU 上,可以使用 make -j8 或 make -j16)。在調用 make 指令時,如果沒有指定任何目标,則将使用預設的名稱為“droid”目标,該目标會編譯出完整的 Android 系統鏡像。
Build 結果的目錄結構
所有的編譯産物都将位于 /out 目錄下,該目錄下主要有以下幾個子目錄:
- /out/host/:該目錄下包含了針對主機的 Android 開發工具的産物。即 SDK 中的各種工具,例如:emulator,adb,aapt 等。
- /out/target/common/:該目錄下包含了針對裝置的共通的編譯産物,主要是 Java 應用代碼和 Java 庫。
- /out/target/product/<product_name>/:包含了針對特定裝置的編譯結果以及平台相關的 C/C++ 庫和二進制檔案。其中,<product_name>是具體目标裝置的名稱。
- /out/dist/:包含了為多種分發而準備的包,通過“
target”将檔案拷貝到該目錄,預設的編譯目标不會産生該目錄。make dist
Build 生成的鏡像檔案
Build 的産物中最重要的是三個鏡像檔案,它們都位于 /out/target/product/<product_name>/ 目錄下。
這三個檔案是:
- system.img:包含了 Android OS 的系統檔案,庫,可執行檔案以及預置的應用程式,将被挂載為根分區。
- ramdisk.img:在啟動時将被 Linux 核心挂載為隻讀分區,它包含了 /init 檔案和一些配置檔案。它用來挂載其他系統鏡像并啟動 init 程序。
- userdata.img:将被挂載為 /data,包含了應用程式相關的資料以及和使用者相關的資料。
Make 檔案說明
整個 Build 系統的入口檔案是源碼樹根目錄下名稱為“Makefile”的檔案,當在源代碼根目錄上調用 make 指令時,make 指令首先将讀取該檔案。
Makefile 檔案的内容隻有一行:“
include build/core/main.mk
”。該行代碼的作用很明顯:包含 build/core/main.mk 檔案。在 main.mk 檔案中又會包含其他的檔案,其他檔案中又會包含更多的檔案,這樣就引入了整個 Build 系統。
這些 Make 檔案間的包含關系是相當複雜的,圖 3 描述了這種關系,該圖中黃色标記的檔案(且除了
$
開頭的檔案)都位于 build/core/ 目錄下。
圖 4. 主要的 Make 檔案及其包含關系
表 2 總結了圖 4 中提到的這些檔案的作用:
表 2. 主要的 Make 檔案的說明
檔案名 | 說明 |
---|---|
main.mk | 最主要的 Make 檔案,該檔案中首先将對編譯環境進行檢查,同時引入其他的 Make 檔案。另外,該檔案中還定義了幾個最主要的 Make 目标,例如 droid,sdk,等(參見後文“Make 目标說明”)。 |
help.mk | 包含了名稱為 help 的 Make 目标的定義,該目标将列出主要的 Make 目标及其說明。 |
pathmap.mk | 将許多頭檔案的路徑通過名值對的方式定義為映射表,并提供 include-path-for 函數來擷取。例如,通過 便可以擷取到 framework 本地代碼需要的頭檔案路徑。 |
envsetup.mk | 配置 Build 系統需要的環境變量,例如:TARGET_PRODUCT,TARGET_BUILD_VARIANT,HOST_OS,HOST_ARCH 等。 目前編譯的主機平台資訊(例如作業系統,CPU 類型等資訊)就是在這個檔案中确定的。 另外,該檔案中還指定了各種編譯結果的輸出路徑。 |
combo/select.mk | 根據目前編譯器的平台選擇平台相關的 Make 檔案。 |
dumpvar.mk | 在 Build 開始之前,顯示此次 Build 的配置資訊。 |
config.mk | 整個 Build 系統的配置檔案,最重要的 Make 檔案之一。該檔案中主要包含以下内容:
|
definitions.mk | 最重要的 Make 檔案之一,在其中定義了大量的函數。這些函數都是 Build 系統的其他檔案将用到的。例如:my-dir,all-subdir-makefiles,find-subdir-files,sign-package 等,關于這些函數的說明請參見每個函數的代碼注釋。 |
distdir.mk | 針對 dist 目标的定義。dist 目标用來拷貝檔案到指定路徑。 |
dex_preopt.mk | 針對啟動 jar 包的預先優化。 |
pdk_config.mk | 顧名思義,針對 pdk(Platform Developement Kit)的配置檔案。 |
| ONE_SHOT_MAKEFILE 是一個變量,當使用“mm”編譯某個目錄下的子產品時,此變量的值即為目前指定路徑下的 Make 檔案的路徑。 |
| 各個子產品的 Android.mk 檔案的集合,這個集合是通過 Python 腳本掃描得到的。 |
post_clean.mk | 在前一次 Build 的基礎上檢查目前 Build 的配置,并執行必要清理工作。 |
legacy_prebuilts.mk | 該檔案中隻定義了 GRANDFATHERED_ALL_PREBUILT 變量。 |
Makefile | 被 main.mk 包含,該檔案中的内容是輔助 main.mk 的一些額外内容。 |
Android 源碼中包含了許多的子產品,子產品的類型有很多種,例如:Java 庫,C/C++ 庫,APK 應用,以及可執行檔案等 。并且,Java 或者 C/C++ 庫還可以分為靜态的或者動态的,庫或可執行檔案既可能是針對裝置(本文的“裝置”指的是 Android 系統将被安裝的裝置,例如某個型号的手機或平闆)的也可能是針對主機(本文的“主機”指的是開發 Android 系統的機器,例如裝有 Ubuntu 作業系統的 PC 機或裝有 MacOS 的 iMac 或 Macbook)的。不同類型的子產品的編譯步驟和方法是不一樣,為了能夠一緻且友善的執行各種類型子產品的編譯,在 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
通過名稱大概就可以猜出每個變量所對應的子產品類型。(在子產品的 Android.mk 檔案中,隻要包含進這裡對應的常量便可以執行相應類型子產品的編譯。對于 Android.mk 檔案的編寫請參見後文:“添加新的子產品”。)
這些常量的值都是另外一個 Make 檔案的路徑,詳細的編譯方式都是在對應的 Make 檔案中定義的。這些常量和 Make 檔案的是一一對應的,對應規則也很簡單:常量的名稱是 Make 檔案的檔案名除去字尾全部改為大寫然後加上“BUILD_”作為字首。例如常量 BUILD_HOST_PREBUILT 的值對應的檔案就是 host_prebuilt.mk。
這些 Make 檔案的說明如表 3 所示:
表 3. 各種子產品的編譯方式的定義檔案
檔案名 | 說明 |
---|---|
host_static_library.mk | 定義了如何編譯主機上的靜态庫。 |
host_shared_library.mk | 定義了如何編譯主機上的共享庫。 |
static_library.mk | 定義了如何編譯裝置上的靜态庫。 |
shared_library.mk | 定義了如何編譯裝置上的共享庫。 |
executable.mk | 定義了如何編譯裝置上的可執行檔案。 |
host_executable.mk | 定義了如何編譯主機上的可執行檔案。 |
package.mk | 定義了如何編譯 APK 檔案。 |
prebuilt.mk | 定義了如何處理一個已經編譯好的檔案 ( 例如 Jar 包 )。 |
multi_prebuilt.mk | 定義了如何處理一個或多個已編譯檔案,該檔案的實作依賴 prebuilt.mk。 |
host_prebuilt.mk | 處理一個或多個主機上使用的已編譯檔案,該檔案的實作依賴 multi_prebuilt.mk。 |
java_library.mk | 定義了如何編譯裝置上的共享 Java 庫。 |
static_java_library.mk | 定義了如何編譯裝置上的靜态 Java 庫。 |
host_java_library.mk | 定義了如何編譯主機上的共享 Java 庫。 |
不同類型的子產品的編譯過程會有一些相同的步驟,例如:編譯一個 Java 庫和編譯一個 APK 檔案都需要定義如何編譯 Java 檔案。是以,表 3 中的這些 Make 檔案的定義中會包含一些共同的代碼邏輯。為了減少代碼備援,需要将共同的代碼複用起來,複用的方式是将共同代碼放到專門的檔案中,然後在其他檔案中包含這些檔案的方式來實作的。這些包含關系如圖 5 所示。由于篇幅關系,這裡就不再對其他檔案做較長的描述(其實這些檔案從檔案名稱中就可以大緻猜出其作用)。
圖 5. 子產品的編譯方式定義檔案的包含關系
Make 目标說明
make /make droid
如果在源碼樹的根目錄直接調用“make”指令而不指定任何目标,則會選擇預設目标:“droid”(在 main.mk 中定義)。是以,這和執行“make droid”效果是一樣的。
droid 目标将編譯出整個系統的鏡像。從源代碼到編譯出系統鏡像,整個編譯過程非常複雜。這個過程并不是在 droid 一個目标中定義的,而是 droid 目标會依賴許多其他的目标,這些目标的互相配合導緻了整個系統的編譯。
圖 6 描述了 droid 目标所依賴的其他目标:
圖 6. droid 目标所依賴的其他 Make 目标
圖 6 中這些目标的說明如表 4 所示:
表 4. droid 所依賴的其他 Make 目标的說明
名稱 | 說明 |
---|---|
apps_only | 該目标将編譯出目前配置下不包含 user,userdebug,eng 标簽(關于标簽,請參見後文“添加新的子產品”)的應用程式。 |
droidcore | 該目标僅僅是所依賴的幾個目标的組合,其本身不做更多的處理。 |
dist_files | 該目标用來拷貝檔案到 /out/dist 目錄。 |
files | 該目标僅僅是所依賴的幾個目标的組合,其本身不做更多的處理。 |
prebuilt | 該目标依賴于 , 的作用就是處理所有已編譯好的檔案。 |
| modules_to_install 變量包含了目前配置下所有會被安裝的子產品(一個子產品是否會被安裝依賴于該産品的配置檔案,子產品的标簽等資訊),是以該目标将導緻所有會被安裝的子產品的編譯。 |
| 該目标用來確定我們定義的構模組化塊是沒有備援的。 |
| 該目标會生成一個關于目前 Build 配置的裝置資訊的檔案,該檔案的生成路徑是:out/target/product/<product_name>/android-info.txt |
systemimage | 生成 system.img。 |
| 生成 boot.img。 |
| 生成 recovery.img。 |
| 生成 userdata.img。 |
| 生成 cache.img。 |
| 該目标會生成 out/target/product/<product_name>/ installed-files.txt 檔案,該檔案中内容是目前系統鏡像中已經安裝的檔案清單。 |
其他目标
Build 系統中包含的其他一些 Make 目标說明如表 5 所示:
表 5. 其他主要 Make 目标
Make 目标 | 說明 |
---|---|
make clean | 執行清理,等同于:rm -rf out/。 |
make sdk | 編譯出 Android 的 SDK。 |
make clean-sdk | 清理 SDK 的編譯産物。 |
make update-api | 更新 API。在 framework API 改動之後,需要首先執行該指令來更新 API,公開的 API 記錄在 frameworks/base/api 目錄下。 |
make dist | 執行 Build,并将 MAKECMDGOALS 變量定義的輸出檔案拷貝到 /out/dist 目錄。 |
make all | 編譯所有内容,不管目前産品的定義中是否會包含。 |
make help | 幫助資訊,顯示主要的 make 目标。 |
make snod | 從已經編譯出的包快速重建系統鏡像。 |
make libandroid_runtime | 編譯所有 JNI framework 内容。 |
makeframework | 編譯所有 Java framework 内容。 |
makeservices | 編譯系統服務和相關内容。 |
make <local_target> | 編譯一個指定的子產品,local_target 為子產品的名稱。 |
make clean-<local_target> | 清理一個指定子產品的編譯結果。 |
makedump-products | 顯示所有産品的編譯配置資訊,例如:産品名,産品支援的地區語言,産品中會包含的子產品等資訊。 |
makePRODUCT-xxx-yyy | 編譯某個指定的産品。 |
makebootimage | 生成 boot.img |
makerecoveryimage | 生成 recovery.img |
makeuserdataimage | 生成 userdata.img |
makecacheimage | 生成 cache.img |
在 Build 系統中添加新的内容
添加新的産品
當我們要開發一款新的 Android 産品的時候,我們首先就需要在 Build 系統中添加對于該産品的定義。
在 Android Build 系統中對産品定義的檔案通常位于 device 目錄下(另外還有一個可以定義産品的目錄是 vender 目錄,這是個曆史遺留目錄,Google 已經建議不要在該目錄中進行定義,而應當選擇 device 目錄)。device 目錄下根據公司名以及産品名分為二級目錄,這一點我們在概述中已經提到過。
通常,對于一個産品的定義通常至少會包括四個檔案:AndroidProducts.mk,産品版本定義檔案,BoardConfig.mk 以及 verndorsetup.sh。下面我們來詳細說明這幾個檔案。
- AndroidProducts.mk:該文檔案中的内容很簡單,其中隻需要定義一個變量,名稱為“PRODUCT_MAKEFILES”,該變量的值為産品版本定義檔案名的清單,例如:
1 2 3 4 | |
- 産品版本定義檔案:顧名思義,該檔案中包含了對于特定産品版本的定義。該檔案可能不隻一個,因為同一個産品可能會有多種版本(例如,面向中國地區一個版本,面向美國地區一個版本)。該檔案中可以定義的變量以及含義說明如表 6 所示:
表 6. 産品版本定義檔案中的變量及其說明
常量 | 說明 |
---|---|
PRODUCT_NAME | 最終使用者将看到的完整産品名,會出現在“關于手機”資訊中。 |
PRODUCT_MODEL | 産品的型号,這也是最終使用者将看到的。 |
PRODUCT_LOCALES | 該産品支援的地區,以空格分格,例如:en_GB de_DE es_ES fr_CA。 |
PRODUCT_PACKAGES | 該産品版本中包含的 APK 應用程式,以空格分格,例如:Calendar Contacts。 |
PRODUCT_DEVICE | 該産品的工業設計的名稱。 |
PRODUCT_MANUFACTURER | 制造商的名稱。 |
PRODUCT_BRAND | 該産品專門定義的商标(如果有的話)。 |
PRODUCT_PROPERTY_OVERRIDES | 對于商品屬性的定義。 |
PRODUCT_COPY_FILES | 編譯該産品時需要拷貝的檔案,以“源路徑 : 目标路徑”的形式。 |
PRODUCT_OTA_PUBLIC_KEYS | 對于該産品的 OTA 公開 key 的清單。 |
PRODUCT_POLICY | 産品使用的政策。 |
PRODUCT_PACKAGE_OVERLAYS | 指出是否要使用預設的資源或添加産品特定定義來覆寫。 |
PRODUCT_CONTRIBUTORS_FILE | HTML 檔案,其中包含項目的貢獻者。 |
PRODUCT_TAGS | 該産品的标簽,以空格分格。 |
通常情況下,我們并不需要定義所有這些變量。Build 系統的已經預先定義好了一些組合,它們都位于 /build/target/product 下,每個檔案定義了一個組合,我們隻要繼承這些預置的定義,然後再覆寫自己想要的變量定義即可。例如:
1 2 3 4 5 6 7 | |
- BoardConfig.mk:該檔案用來配置硬體主機闆,它其中定義的都是裝置底層的硬體特性。例如:該裝置的主機闆相關資訊,Wifi 相關資訊,還有 bootloader,核心,radioimage 等資訊。對于該檔案的示例,請參看 Android 源碼樹已經有的檔案。
- vendorsetup.sh:該檔案中作用是通過 add_lunch_combo 函數在 lunch 函數中添加一個菜單選項。該函數的參數是産品名稱加上編譯類型,中間以“-”連接配接,例如:add_lunch_combo full_lt26-userdebug。/build/envsetup.sh 會掃描所有 device 和 vender 二 級目 錄下的名稱 為"vendorsetup.sh"檔案,并根據其中的内容來确定 lunch 函數的 菜單選項。
在配置了以上的檔案之後,便可以編譯出我們新添加的裝置的系統鏡像了。
首先,調用“
source build/envsetup.sh
”該指令的輸出中會看到 Build 系統已經引入了剛剛添加的 vendorsetup.sh 檔案。
然後再調用“lunch”函數,該函數輸出的清單中将包含新添加的 vendorsetup.sh 中添加的條目。然後通過編号或名稱選擇即可。
最後,調用“make -j8”來執行編譯即可。
添加新的子產品
關于“子產品”的說明在上文中已經提到過,這裡不再贅述。
在源碼樹中,一個子產品的所有檔案通常都位于同一個檔案夾中。為了将目前子產品添加到整個 Build 系統中,每個子產品都需要一個專門的 Make 檔案,該檔案的名稱為“Android.mk”。Build 系統會掃描名稱為“Android.mk”的檔案,并根據該檔案中内容編譯出相應的産物。
需要注意的是:在 Android Build 系統中,編譯是以子產品(而不是檔案)作為機關的,每個子產品都有一個唯一的名稱,一個子產品的依賴對象隻能是另外一個子產品,而不能是其他類型的對象。對于已經編譯好的二進制庫,如果要用來被當作是依賴對象,那麼應當将這些已經編譯好的庫作為單獨的子產品。對于這些已經編譯好的庫使用 BUILD_PREBUILT 或 BUILD_MULTI_PREBUILT。例如:當編譯某個 Java 庫需要依賴一些 Jar 包時,并不能直接指定 Jar 包的路徑作為依賴,而必須首先将這些 Jar 包定義為一個子產品,然後在編譯 Java 庫的時候通過子產品的名稱來依賴這些 Jar 包。
下面,我們就來講解 Android.mk 檔案的編寫:
Android.mk 檔案通常以以下兩行代碼作為開頭:
1 2 | |
這兩行代碼的作用是:
- 設定目前子產品的編譯路徑為目前檔案夾路徑。
- 清理(可能由其他子產品設定過的)編譯環境中用到的變量。
為了友善子產品的編譯,Build 系統設定了很多的編譯環境變量。要編譯一個子產品,隻要在編譯之前根據需要設定這些變量然後執行編譯即可。它們包括:
- LOCAL_SRC_FILES:目前子產品包含的所有源代碼檔案。
- LOCAL_MODULE:目前子產品的名稱,這個名稱應當是唯一的,子產品間的依賴關系就是通過這個名稱來引用的。
- LOCAL_C_INCLUDES:C 或 C++ 語言需要的頭檔案的路徑。
- LOCAL_STATIC_LIBRARIES:目前子產品在靜态連結時需要的庫的名稱。
- LOCAL_SHARED_LIBRARIES:目前子產品在運作時依賴的動态庫的名稱。
- LOCAL_CFLAGS:提供給 C/C++ 編譯器的額外編譯參數。
- LOCAL_JAVA_LIBRARIES:目前子產品依賴的 Java 共享庫。
- LOCAL_STATIC_JAVA_LIBRARIES:目前子產品依賴的 Java 靜态庫。
- LOCAL_PACKAGE_NAME:目前 APK 應用的名稱。
- LOCAL_CERTIFICATE:簽署目前應用的證書名稱。
- LOCAL_MODULE_TAGS:目前子產品所包含的标簽,一個子產品可以包含多個标簽。标簽的值可能是 debug, eng, user,development 或者 optional。其中,optional 是預設标簽。标簽是提供給編譯類型使用的。不同的編譯類型會安裝包含不同标簽的子產品,關于編譯類型的說明如表 7 所示:
表 7. 編譯類型的說明
名稱 | 說明 |
---|---|
eng | 預設類型,該編譯類型适用于開發階段。 當選擇這種類型時,編譯結果将:
|
user | 該編譯類型适合用于最終釋出階段。 當選擇這種類型時,編譯結果将:
|
userdebug | 該編譯類型适合用于 debug 階段。 該類型和 user 一樣,除了:
|
表 3 中的檔案已經定義好了各種類型子產品的編譯方式。是以要執行編譯,隻需要引入表 3 中對應的 Make 檔案即可(通過常量的方式)。例如,要編譯一個 APK 檔案,隻需要在 Android.mk 檔案中,加入“
include $(BUILD_PACKAGE)
除此以外,Build 系統中還定義了一些便捷的函數以便在 Android.mk 中使用,包括:
-
:擷取目前檔案夾路徑。$(call my-dir)
-
:擷取指定目錄下的所有 Java 檔案。$(call all-java-files-under, <src>)
-
:擷取指定目錄下的所有 C 語言檔案。$(call all-c-files-under, <src>)
-
:擷取指定目錄下的所有 AIDL 檔案。$(call all-Iaidl-files-under, <src>)
-
:擷取指定目錄下的所有 Make 檔案。$(call all-makefiles-under, <folder>)
-
:擷取 Build 輸出的目标檔案夾路徑。$(call intermediates-dir-for, <class>, <app_name>, <host or target>, <common?> )
清單 2 和清單 3 分别是編譯 APK 檔案和編譯 Java 靜态庫的 Make 檔案示例:
清單 2. 編譯一個 APK 檔案
1 2 3 4 5 6 7 8 9 10 | |
清單 3. 編譯一個 Java 的靜态庫
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
結束語
整個 Build 系統包含了非常多的内容,由于篇幅所限,本文隻能介紹其中最主要内容。
由于 Build 系統本身也是在随着 Android 平台不斷的開發過程中,是以不同的版本其中的内容和定義可能會發生變化。網絡上關于該部分的資料很零碎,并且很多資料中的一些内容已經過時不再适用,再加上缺少官方文檔,是以該部分的學習存在一定的難度。
這就要求我們要有很強的代碼閱讀能力,畢竟代碼是不會說謊的。 要知道,對于我們這些開發人員來說,源代碼就是我們最忠實的朋友。 Use the Source,Luke!