Android.bp快速入門

xiangzhihong發表於2022-05-20

一、Soong 編譯系統

在 Android 7.0 釋出之前,Android 僅使用 GNU Make 描述和執行其構建規則。在Android系統級編譯中,Make 構建系統得到了廣泛的支援和使用,但它有一些缺點:編譯緩慢、容易出錯、無法擴充套件且難以測試,而Soong 構建系統正好提供了 Android build 所需的靈活性。

Soong 構建系統是在 Android 7.0 (Nougat) 中引入的,旨在取代 Make。它利用 Kati GNU Make 克隆工具和 Ninja 構建系統元件來加速 Android 的構建,Soong的編譯流程圖如下。

image.png

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"
    ],
}

參考:
Soong 編譯系統
Android 編譯之android.bp