一、Soong 編譯系統
在 Android 7.0 釋出之前,Android 僅使用 GNU Make 描述和執行其構建規則。在Android系統級編譯中,Make 構建系統得到了廣泛的支援和使用,但它有一些缺點:編譯緩慢、容易出錯、無法擴充套件且難以測試,而Soong 構建系統正好提供了 Android build 所需的靈活性。
Soong 構建系統是在 Android 7.0 (Nougat) 中引入的,旨在取代 Make。它利用 Kati GNU Make 克隆工具和 Ninja 構建系統元件來加速 Android 的構建,Soong的編譯流程圖如下。
Soong編譯系統下,原本打算輸入是.bp檔案,輸出是.ninja檔案,但是由於系統中的.mk檔案還沒有被完全消除掉,因此提供kati和ckati工具將.mk/Makefile檔案轉換為.ninja檔案。.ninja成為編譯系統的彙編檔案,是不需要人為去修改的,相當於配置檔案來呼叫gcc、java、clang等編譯器去編譯。
Soong編譯系統的設計思想是消除.mk檔案中的if/else等邏輯,使.bp檔案只是一個簡單的編譯邏輯,這些複雜的選擇配置邏輯應該放在更高層,比如使用更好除錯的Python來編寫。
二、Android.bp
Android.bp的出現就是為了替換Android.mk檔案。bp跟mk檔案不同,它是純粹的配置,沒有分支、迴圈等流程控制,不能做算數邏輯運算。如果需要控制邏輯,那麼可以使用Android.mk或者Go語言進行編寫。
2.1 示例
Android.bp 檔案中的模組以模組型別開頭,然後是一組格式屬性:name: value,在一點上Android.bp的語法結構與JSON的語法結構相似,都是以鍵值對的形式編寫,比如。
android_app {
name: "Provision",
srcs: ["**/*.java"],
platform_apis: true,
product_specific: true,
certificate: "platform",
}
每個模組都必須具有 name 屬性,並且相應值在所有 name 檔案中必須是唯一的,僅有兩個例外情況是名稱空間和預構建模組中的 Android.bp 屬性值,這兩個值可能會重複。
srcs 屬性以字串列表的形式指定用於構建模組的原始檔。可以使用模組引用語法 ":<module-name>" 來引用生成原始檔的其他模組的輸出,如 genrule 或 filegroup。
android_app{}
上面程式碼的意思是,該模組用於構建一個apk。如果要給模組指定一個名字,可以使用下面的方式。
name: "Provision",
如果指定了 android_app 模組型別(在程式碼塊的開頭),就需要設定該模組的name 。此設定會為模組命名,生成的 APK 將與模組名稱相同,不過帶有 .apk 字尾。例如,在本例中,生成的 APK 將命名為 Provision.apk
srcs: ["**/*.java"],
上面程式碼的srcs用於指定當前的模組編譯的原始碼位置,*表示萬用字元 。
platform_apis: true,
設定該標記後會使用sdk的hide的api來編譯。編譯的APK中使用了系統級API,必須設定該值。和Android.mk中的LOCAL_PRIVATE_PLATFORM_APIS的作用相同 。
product_specific: true,
設定該標記後,生成的apk會被安裝在Android系統的product分割槽,和Android.mk中LOCAL_PRODUCT_MODULE作用相同。
certificate 用於指定APK的簽名方式。如果不指定,預設使用testkey簽名。與LOCAL_CERTIFICATE作用相同。 Android中共有四中籤名方式:
- testkey:普通apk,預設使用該簽名。
- platform:該apk完成一些系統的核心功能。經過對系統中存在的資料夾的訪問測試,這種方式編譯出來的apk所在程式的UID為system。
- shared:該apk需要和home/contacts程式共享資料。
- media:該apk是media/download系統中的一環。
2.2 常見模組型別
在Android.bp中,我們會基於模組型別來構建我們所需要的東西,常用的有以下幾種型別:
- cc_library_shared:編譯成動態庫,類似於Android.mk中的BUILD_SHARED_LIBRARY。
- cc_binary:編譯成可執行檔案,類似於Android.mk中的BUILD_EXECUTABLE。
- name:編譯出的模組的名稱,類似於Android.mk中的LOCAL_MODULE。
- srcs:原始檔,類似於Android.mk中的LOCAL_SRC_FILES。
- local_include_dirs:指定路徑查詢標頭檔案,類似於Android.mk中的LOCAL_C_INCLUDES。
- shared_libs:編譯所依賴的動態庫,類似於Android.mk中的LOCAL_SHARED_LIBRARIES。
- static_libs:編譯所依賴的靜態庫,類似於Android.mk中的LOCAL_STATIC_LIBRARIES。
- cflags:編譯Flag,類似於Android.mk中的LOCAL_CFLAGS。
android_app
用於構建Android 應用程式安裝包,是Android系統應用開發中常用的模組型別,與Android.mk中的BUILD_PACKAGE作用相同。
java_library
java_library用於將原始碼構建並連結到裝置的.jar檔案中。預設情況下,java_library只有一個變數,它生成一個包含根據裝置引導類路徑編譯的.class檔案的.jar包。生成的jar不適合直接安裝在裝置上,通常會用作另一個模組的static_libs依賴項。
如果指定installable:true 將生成一個包含classes.dex 檔案的.jar 檔案,適合在裝置上安裝。指定含host_supported:true 將產生兩個變數,一個根據device的bootclasspath編譯,另一個根據host的bootclasspath編譯。
android_library
android_library將原始碼與android資原始檔一起構建並連結到裝置的“.jar”檔案中。android_library有一個單獨的變體,它生成一個包含根據device的bootclasspath編譯的.class檔案的.jar檔案,以及一個包含使用aapt2編譯的android資源的.package-res.apk檔案。生成的apk檔案不能直接安裝在裝置上,但可以用作android_app模組的static_libs依賴項。
2.3 設定變數
在bp中可以通過=號來設定一個全域性變數。
src_path = ["**/*.java"]
android_app {
name: "Provision",
srcs: src_path,
platform_apis: true,
product_specific: true,
certificate: "platform",
}
三、基礎語法
3.1 資料型別
Android.bp中變數和屬性是強型別,變數根據第一項賦值動態變化,屬性由模組型別靜態設定,支援的型別為:
- 布林值(true或 false)
- 整數 (int)
- 字串("string")
- 字串列表 (["string1", "string2"])
- 對映 ({key1: "value1", key2: ["value2"]})
3.2 條件語句
Soong 不支援 Android.bp 檔案中的條件語句。編譯規則中如果需要處理條件語句,那麼需要在 Go中進行處理。大多數條件語句都會轉換為對映屬性,其中選擇了對映中的某個值並將其附加到頂級屬性。
例如,要支援特定的架構檔案,可以使用以下命令:
cc_library {
...
srcs: ["generic.cpp"],
arch: {
arm: {
srcs: ["arm.cpp"],
},
x86: {
srcs: ["x86.cpp"],
},
},
}
3.3 運算子
可以使用 + 運算子附加字串、字串列表和對映。可以使用 + 運算子對整數求和。附加對映會生成兩個對映中鍵的並集,並附加在兩個對映中都存在的所有鍵的值。
3.4 示例
Android.bp位於Android 10 : packages/apps/Car/Notification 目錄下,參考示例如下:
// 構建可執行程式
android_app {
// 設定可執行的程式的名稱,編譯後會生成一個 CarNotification.apk
name: "CarNotification",
// 指定java原始碼的位置
srcs: ["src/**/*.java"],
// 指定資原始檔的位置
resource_dirs: ["res"],
// 允許使用系統hide api
platform_apis: true,
// 設定apk簽名為 platform
certificate: "platform",
// 設定apk安裝路徑為priv-app
privileged: true,
// 是否啟用程式碼優化,android_app中預設為true,java_library中預設為false
optimize: {
enabled: false,
},
// 是否預先生成dex檔案,預設為true。該屬性會影響應用的首次啟動速度以及Android系統的啟動速度
dex_preopt: {
enabled: false,
},
// 引入java靜態庫
static_libs: [
"androidx.cardview_cardview",
"androidx.recyclerview_recyclerview",
"androidx.palette_palette",
"car-assist-client-lib",
"android.car.userlib",
"androidx-constraintlayout_constraintlayout"
],
// 引入java庫
libs: ["android.car"],
product_variables: {
pdk: {
enabled: false,
},
},
// 設定依賴模組。如果安裝了此模組,則要還需要安裝的其他模組的名稱
required: ["privapp_whitelist_com.android.car.notification"]
}
// As Lib
android_library {
name: "CarNotificationLib",
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
manifest: "AndroidManifest-withoutActivity.xml",
platform_apis: true,
optimize: {
enabled: false,
},
dex_preopt: {
enabled: false,
},
static_libs: [
"androidx.cardview_cardview",
"androidx.recyclerview_recyclerview",
"androidx.palette_palette",
"car-assist-client-lib",
"android.car.userlib",
"androidx-constraintlayout_constraintlayout"
],
libs: ["android.car"],
product_variables: {
pdk: {
enabled: false,
},
},
}
四、常見問題
4.1 引入第三方jar
在實際開發中,經常需要在app中引入第三方的jar。在Android.bp中,引入第三方的jar可以按照下面的方式。
首先,在專案的根目錄新建 libs資料夾,放入要匯入的jar包,比如 CarServicelib.jar,然後在Android.bp中引入該jar。
java_import {
name: "CarServicelib.jar",
jars: ["libs/CarServicelib.jar"],
}
android_app {
// 省去其它屬性
static_libs: [
"CarServicelib.jar"
],
}
4.2 引入AndroidX
開發過程中,如果想要引入AndroidX的類庫,可以參考下面的方式進行引入。
android_app {
// 省去其它屬性
// 引入AndroidX庫下的lib
static_libs: [
"androidx.cardview_cardview",
"androidx.recyclerview_recyclerview",
"androidx-constraintlayout_constraintlayout"
],
}