Android.bp簡介
Android 7.0之後希望用Android.bp替換Android.mk,bp簡單的配置更方便Ninja 文件的產生,而Blueprint和Soong 就此產生。Android 利用Blueprint和Soong 來解析bp文件,經過最終轉換爲ninja files。 Blueprint和Soong都是由Golang寫的項目。 從Android Nougat開始,prebuilts/go/目錄下新增了Golang所需的運行環境,在編譯時使用。Android.bp以及相關支持,從Android Nougat開始加入,從 Android Oreo(8.0)開始默認開啓。 如果需要在Android Nougat的版本使用,需要在執行編譯時添加變量: make ‘USE_SOONG=true’ Soong是以前Android基於make的編譯系統的替代品。它以Android.bp文件替代Android.mk,Android.bp文件用類似JSON的簡潔聲明來描述需要構建的模塊。
參考文檔:
再來說一說跟着Android版本相應的發展演變過程:
- Android 7.0引入ninja和kati
- Android 8.0使用Android.bp來替換Android.mk,引入Soong
- Android 9.0強制使用Android.bp
轉換關係
通過Kati將Android.mk轉換成ninja格式的文件,通過Blueprint+Soong將Android.bp轉換成ninja格式的文件,通過androidmk將將Android.mk轉換成Android.bp,但針對沒有分支、循環等流程控制的Android.mk纔有效。
這裏涉及到Ninja, kati, Soong, bp概念,接下來分別簡單介紹一下。
- Ninja
ninja是一個編譯框架,會根據相應的ninja格式的配置文件進行編譯,但是ninja文件一般不會手動修改,而是通過將Android.bp文件轉換成ninja格文件來編譯。
- Android.bp
Android.bp的出現就是爲了替換Android.mk文件。bp跟mk文件不同,它是純粹的配置,沒有分支、循環等流程控制,不能做算數邏輯運算。如果需要控制邏輯,那麼只能通過Go語言編寫。
- Soong
Soong類似於之前的Makefile編譯系統的核心,負責提供Android.bp語義解析,並將之轉換成Ninja文件。Soong還會編譯生成一個androidmk命令,用於將Android.mk文件轉換爲Android.bp文件,不過這個轉換功能僅限於沒有分支、循環等流程控制的Android.mk纔有效。
- Blueprint
Blueprint是生成、解析Android.bp的工具,是Soong的一部分。Soong負責Android編譯而設計的工具,而Blueprint只是解析文件格式,Soong解析內容的具體含義。Blueprint和Soong都是由Golang寫的項目,從Android 7.0,prebuilts/go/目錄下新增Golang所需的運行環境,在編譯時使用。
- Kati
kati是專爲Android開發的一個基於Golang和C++的工具,主要功能是把Android中的Android.mk文件轉換成Ninja文件。代碼路徑是build/kati/,編譯後的產物是ckati。
Android.bp語法初識
- 例子1
cc_library_shared { //編譯成動態庫,類似於Android.mk中的BUILD_SHARED_LIBRARY
name: "libbluetooth_jni", //編譯出的模塊的名稱,類似於Android.mk中的LOCAL_MODULE
srcs: [ //源文件,類似於Android.mk中的LOCAL_SRC_FILES
"com_android_bluetooth_btservice_AdapterService.cpp",
"com_android_bluetooth_hfp.cpp",
"com_android_bluetooth_hfpclient.cpp",
"com_android_bluetooth_a2dp.cpp",
"com_android_bluetooth_a2dp_sink.cpp",
"com_android_bluetooth_avrcp.cpp",
"com_android_bluetooth_avrcp_controller.cpp",
"com_android_bluetooth_hid.cpp",
"com_android_bluetooth_hidd.cpp",
"com_android_bluetooth_hdp.cpp",
"com_android_bluetooth_pan.cpp",
"com_android_bluetooth_gatt.cpp",
"com_android_bluetooth_sdp.cpp",
],
include_dirs: [ //用戶指定的頭文件查找路徑,類似於Android.mk中的LOCAL_C_INCLUDES
"libnativehelper/include/nativehelper",
"system/bt/types",
],
shared_libs: [ //編譯所依賴的動態庫,類似於Android.mk中的LOCAL_SHARED_LIBRARIES
"libandroid_runtime",
"libchrome",
"libnativehelper",
"libcutils",
"libutils",
"liblog",
"libhardware",
],
static_libs: [ //編譯所依賴的靜態庫,類似於Android.mk中的LOCAL_STATIC_LIBRARIES
"libbluetooth-types",
],
cflags: [ ///編譯flag,類似於Android.mk中的LOCAL_CFLAGS
"-Wall",
"-Wextra",
"-Wno-unused-parameter",
],
}
- 例子2
cc_binary { //編譯成可執行文件,cc_library_shared編譯成動態庫
name: "bt_test", //編譯出的可執行文件的名字爲bt_test
vendor: true, //編譯出來放在/vendor目錄下(默認是放在/system目錄下)
srcs: ["bt_test.cpp"], //源文件,格式["a.cpp", "b.cpp"]
cflags: [
"-Wall",
"-Werror",
"-Wno-unused-parameter",
], //編譯flag
include_dirs: [
"hardware/interfaces/halnode/1.0/default", //用戶值定的頭文件查找路徑
],
shared_libs: [ //編譯依賴的動態庫
"liblog",
"libutils",
"libhidltransport",
"[email protected]",
"libhidlbase",
"libbase",
],
}
- 例子3
srcs 屬性以字符串列表的形式指定用於編譯模塊的源文件。您可以使用模塊引用語法 “:” 來引用生成源文件的其他模塊的輸出,如 genrule 或 filegroup。是不是感覺一頭霧水,好嗎,還是上實例說明:
~/ssd/qcom_64/msm8953-9$ cd frameworks/base/core/java/
~/ssd/qcom_64/msm8953-9/frameworks/base/core/java$ vi Android.bp
filegroup {
name: "IKeyAttestationApplicationIdProvider.aidl",
srcs: ["android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl"],
}
filegroup {
name: "IDropBoxManagerService.aidl",
srcs: ["com/android/internal/os/IDropBoxManagerService.aidl"],
~/ssd/qcom_64/msm8953-9/frameworks/base/core$ cd ..
~/ssd/qcom_64/msm8953-9/frameworks/base$ vi libs/services/Android.bp
cc_library_shared {
name: "libservices",
srcs: [
":IDropBoxManagerService.aidl",
"src/os/DropBoxManager.cpp",
"src/os/StatsDimensionsValue.cpp",
"src/os/StatsLogEventWrapper.cpp",
],
shared_libs: [
"libbinder",
"liblog",
"libcutils",
"libutils",
],
模塊
從前面的列子可以看出定義一個模塊從模塊的類型開始,模塊有不同的類型,如前面例子中的cc_library_shared,當然類型還有很多種,譬如cc_binary android_app cc_library_static等等。模塊包含一些屬性格式爲“property-name:property-value”,其中name屬性必須指定,其屬性值必須是全局唯一的。
其中默認模塊可用於在多個模塊中重複相同的屬性,是不是用文字表達很模糊,好嗎,上實例:
cc_defaults {// //默認模塊名稱
name: "default_module",
shared_libs: ["libz"],
stl: "none",
}
cc_binary {
name: "test1",
defaults: ["default_module"], //引用默認模塊名稱
srcs: ["src/test/test.c"],
}
類型
變量和屬性是強類型,變量根據第一項賦值動態變化,屬性由模塊類型靜態設置。支持的類型爲:
- 布爾值(true 或 false)
- 整數 (int)
- 字符串 (“string”)
- 字符串列表 ([“string1”, “string2”])
- 映射 ({key1: “value1”, key2: [“value2”]})
- 映射可以包含任何類型的值,包括嵌套映射。列表和映射可能在最後一個值後面有終止逗號。
其它可用選項
-
subdirs = [“ndk”] //指定進入下層ndk目錄進行編譯
-
export_include_dirs: [ //從其他模塊添加頭文件檢索路徑 “include”, “include/camera” ],
-
export_shared_lib_headers: [“libcamera_metadata”],
-
local_include_dirs: [“aidl”], //如果從當前目錄添加路徑時使用,將./aidl目錄加入頭文件的搜索路徑
-
static_libs: [“libupdater”], //指定對庫libupdater進行靜態鏈接 static_executable: true,
變量
Android.bp文件可包含頂級變量賦值:
test_srcs = ["src/test.c"],
cc_binary {
name: "test",
srcs: test_srcs,
}
變量範圍限定爲聲明它們的文件的其餘部分,以及任何子藍圖文件。變量是不可變的,但有一個例外 —— 它們可以附上+= 賦值,但僅在變量被引用之前。
註釋
我們知道Android.mk中可以進行註釋,當然Android.bp裏面也可以,Android.mk中使用"#"然後添加註釋,Android.bp使用單行註釋//和多行註釋/* */兩種方式。
運算符
可以使用 + 運算符附加字符串、字符串列表和映射。可以使用 + 運算符對整數求和。附加映射會生成兩個映射中鍵的並集,並附加在兩個映射中都存在的所有鍵的值。
###條件語句
Soong 不支持 Android.bp 文件中的條件語句。但是,編譯規則中需要條件語句的複雜問題將在 Go(在這種語言中,您可以使用高級語言功能,並且可以跟蹤條件語句引入的隱式依賴項)中處理。大多數條件語句都會轉換爲映射屬性,其中選擇了映射中的某個值並將其附加到頂級屬性。
例如,要支持特定於架構的文件,請使用以下命令:
cc_library {
...
srcs: ["generic.cpp"],
arch: {
arm: {
srcs: ["arm.cpp"],
},
x86: {
srcs: ["x86.cpp"],
},
},
}
system/core/libusbhost$ cat Android.bp
cc_library {
name: "libusbhost",
vendor_available: true,
vndk: {
enabled: true,
},
host_supported: true,
srcs: ["usbhost.c"],
cflags: ["-Werror"],
export_include_dirs: ["include"],
target: { //相當於if
android: { //編譯Android上運行的程序 相當於if
cflags: [
"-g",
"-DUSE_LIBLOG",
],
shared_libs: ["liblog"],
},
darwin: { //編譯darwin上運行的程序
enabled: false,
},
},
}
支持模塊類型
Android.bp可以支持android_app、cc_binary、cc_binary_host等多種類型,具體定義在Android源碼的build/soong/androidmk/cmd/androidmk/android.go可以查看,具體如下:
var moduleTypes = map[string]string{
"BUILD_SHARED_LIBRARY": "cc_library_shared",
"BUILD_STATIC_LIBRARY": "cc_library_static",
"BUILD_HOST_SHARED_LIBRARY": "cc_library_host_shared",
"BUILD_HOST_STATIC_LIBRARY": "cc_library_host_static",
"BUILD_HEADER_LIBRARY": "cc_library_headers",
"BUILD_EXECUTABLE": "cc_binary",
"BUILD_HOST_EXECUTABLE": "cc_binary_host",
"BUILD_NATIVE_TEST": "cc_test",
"BUILD_HOST_NATIVE_TEST": "cc_test_host",
"BUILD_NATIVE_BENCHMARK": "cc_benchmark",
"BUILD_HOST_NATIVE_BENCHMARK": "cc_benchmark_host",
"BUILD_JAVA_LIBRARY": "java_library",
"BUILD_STATIC_JAVA_LIBRARY": "java_library_static",
"BUILD_HOST_JAVA_LIBRARY": "java_library_host",
"BUILD_HOST_DALVIK_JAVA_LIBRARY": "java_library_host_dalvik",
"BUILD_PACKAGE": "android_app",
}
支持預編譯類型
Android.bp可以支持多種預編譯類型,具體定義在Android源碼的build/soong/androidmk/cmd/androidmk/android.go可以查看,如下圖所示:
var prebuiltTypes = map[string]string{
"SHARED_LIBRARIES": "cc_prebuilt_library_shared",
"STATIC_LIBRARIES": "cc_prebuilt_library_static",
"EXECUTABLES": "cc_prebuilt_binary",
"JAVA_LIBRARIES": "prebuilt_java_library",
}
常用工具
(1)bpfmt 一個是格式化工具bpfmt。 與gofmt類似,可以格式化Blueprint文件。 (其實,代碼基本上都是從gofmt複製而來。)eg 格式化當前目錄及其遞歸子目錄下的所有Android.bp:
wugn # bpfmt -w .
(2)androidmk 另一個是androidmk,負責轉換Android.mk爲Android.bp。
wugn: # androidmk Android.mk > Android.bp