刨根問底 - 安卓編譯 Android.bp 使用方法

簡介 - 這東西是從哪里來的呢?

Android.bp 是 Android 編譯系統(tǒng)從 make 系統(tǒng)切換到 ninja 系統(tǒng)過程中的產(chǎn)物,Android 7.0 和 8.0 是該編譯系統(tǒng)切換的過渡期,所以在 Android 7.0 和 8.0 時既可以看到 Android.mk,也可以看到 Android.bp,但是從 Android 9.0 開始,在Android 源碼里面 Android.mk 已經(jīng)看不到了,只有少數(shù) Vendor 廠家的代碼里面還可以看到。

這次切換的背后原因是,Android 要提升編譯效率,顯著提升的是第一次編譯后增量編譯的速度,但是由于 Android 工程越來越龐大,這個效率很難被大家感受到。

內(nèi)容 - 這東西內(nèi)容長啥樣呢?

以 hardware/libhardware/Android.bp 為例,其內(nèi)容與 json 文件非常類似,實際上也是一種更加簡潔的編譯配置文件,相比 Android.mk,可閱讀性更強,但是前提是有很好的 Android.bp 語法基礎(chǔ)。

cc_library_shared {
     name: “l(fā)ibxmlrpc++”,

     rtti: true,
     cppflags: [
           “-Wall”,
           “-Werror”,
           “-fexceptions”,
     ],
     export_include_dirs: [“src”],
     srcs: [“src/**/*.cpp”],

     target: {
           darwin: {
                enabled: false,
           },
     },
}

相同的 Android.mk 長下面這樣:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := libxmlrpc++
LOCAL_MODULE_HOST_OS := linux

LOCAL_RTTI_FLAG := -frtti
LOCAL_CPPFLAGS := -Wall -Werror -fexceptions
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/src

LOCAL_SRC_FILES := $(call \
     all-cpp-files-under,src)
include $(BUILD_SHARED_LIBRARY)

語法 - 怎么閱讀呢?

語法是這樣的(多數(shù)內(nèi)容來自 Android 官網(wǎng)介紹):

模塊

Android.bp 文件中的模塊以模塊類型開頭,后跟一組 name: "value", 格式的屬性:

cc_binary {
    name: "gzip",
    srcs: ["src/test/minigzip.c"],
    shared_libs: ["libz"],
    stl: "none",
}

每個模塊都必須具有 name 屬性,并且相應(yīng)值在所有 name 文件中必須是唯一的,僅有兩個例外情況是命名空間和預(yù)構(gòu)建模塊中的 Android.bp 屬性值,這兩個值可能會重復(fù)。

srcs 屬性以字符串列表的形式指定用于構(gòu)建模塊的源文件。您可以使用模塊引用語法 ":<module-name>" 來引用生成源文件的其他模塊的輸出,如 genrulefilegroup

如需有效模塊類型及其屬性的列表,請參閱 Soong 模塊參考。

類型

變量和屬性是強類型,變量根據(jù)第一項賦值動態(tài)變化,屬性由模塊類型靜態(tài)設(shè)置。支持的類型為:

布爾值(true 或 false)
整數(shù) (int)
字符串 ("string")
字符串列表 (["string1", "string2"])
映射 ({key1: "value1", key2: ["value2"]})
映射可以包含任何類型的值,包括嵌套映射。列表和映射可能在最后一個值后面有終止逗號。

Glob

接受文件列表的屬性(例如 srcs)也可以采用 glob 模式。glob 模式可以包含普通的 UNIX 通配符 ,例如 .java。glob 模式還可以包含單個 ** 通配符作為路徑元素,與零個或多個路徑元素匹配。例如,java//*.java 同時匹配 java/Main.java 和 java/com/android/Main.java 模式。

變量

Android.bp 文件可能包含頂級變量賦值:

gzip_srcs = ["src/test/minigzip.c"],
cc_binary {
    name: "gzip",
    srcs: gzip_srcs,
    shared_libs: ["libz"],
    stl: "none",
}

變量的作用域限定在聲明它們的文件的其余部分,以及所有子 Blueprint 文件。變量是不可變的,但有一個例外情況:可以使用 += 賦值將變量附加到別處,但只能在引用它們之前附加。

注釋

Android.bp 文件可以包含 C 樣式的多行 /* */ 注釋以及 C++ 樣式的單行 // 注釋。

運算符

可以使用 + 運算符附加字符串、字符串列表和映射??梢允褂?+ 運算符對整數(shù)求和。附加映射會生成兩個映射中鍵的并集,并附加在兩個映射中都存在的所有鍵的值。

條件語句

Soong 不支持 Android.bp 文件中的條件語句。但是,編譯規(guī)則中需要條件語句的復(fù)雜問題將在 Go(在這種語言中,您可以使用高級語言功能,并且可以跟蹤條件語句引入的隱式依賴項)中處理。大多數(shù)條件語句都會轉(zhuǎn)換為映射屬性,其中選擇了映射中的某個值并將其附加到頂級屬性。

例如,要支持特定于架構(gòu)的文件,請使用以下命令:

cc_library {
    ...
    srcs: ["generic.cpp"],
    arch: {
        arm: {
            srcs: ["arm.cpp"],
        },
        x86: {
            srcs: ["x86.cpp"],
        },
    },
}
格式設(shè)置工具

Soong 包含一個針對 Blueprint 文件的規(guī)范格式設(shè)置工具,類似于 gofmt。如需以遞歸方式重新設(shè)置當(dāng)前目錄中所有 Android.bp 文件的格式,請運行以下命令:

bpfmt -w .

規(guī)范格式包括縮進四個空格、多元素列表的每個元素后面有換行符,以及列表和映射末尾有英文逗號。

特殊模塊

一些特殊模塊組具有獨特的特征。

默認模塊

默認模塊可用于在多個模塊中重復(fù)使用相同的屬性。例如:

cc_defaults {
    name: "gzip_defaults",
    shared_libs: ["libz"],
    stl: "none",
}

cc_binary {
    name: "gzip",
    defaults: ["gzip_defaults"],
    srcs: ["src/test/minigzip.c"],
}
預(yù)編譯的模塊

某些預(yù)構(gòu)建的模塊類型允許模塊與其基于源代碼的對應(yīng)模塊具有相同的名稱。例如,如果已有同名的 cc_binary,也可以將 cc_prebuilt_binary 命名為 foo。這讓開發(fā)者可以靈活地選擇要納入其最終產(chǎn)品中的版本。如果 build 配置包含兩個版本,那么預(yù)編譯模塊定義中的 prefer 標志值會指示哪個版本具有優(yōu)先級。請注意,某些預(yù)編譯模塊的名稱不能prebuilt 開頭,例如 android_app_import。

命名空間模塊

在 Android 完全從 Make 轉(zhuǎn)換為 Soong 之前,Make 產(chǎn)品配置必須指定 PRODUCT_SOONG_NAMESPACES 值。它的值應(yīng)該是一個以空格分隔的列表,其中包含 Soong 導(dǎo)出到 Make 以使用 m 命令進行編譯的命名空間。在 Android 完成到 Soong 的轉(zhuǎn)換之后,啟用命名空間的詳細信息可能會發(fā)生變化。

Soong 可以讓不同目錄中的模塊指定相同的名稱,只要每個模塊都在單獨的命名空間中聲明即可??梢园慈缦路绞铰暶髅臻g:

soong_namespace {
    imports: ["path/to/otherNamespace1", "path/to/otherNamespace2"],
}

請注意,命名空間沒有 name 屬性;其路徑會自動指定為其名稱。

系統(tǒng)會根據(jù)每個 Soong 模塊在樹中的位置為其分配命名空間。每個 Soong 模塊都會被視為處于 Android.bp(位于當(dāng)前目錄或最近的父級目錄中的 soong_namespace 文件內(nèi))定義的命名空間中。如果未找到此類 soong_namespace 模塊,則認為該模塊位于隱式根命名空間中。

下面是一個示例:Soong 嘗試解析由模塊 M 在名稱空間 N(導(dǎo)入命名空間 I1、I2、I3…)中聲明的依賴項 D。

  1. 如果 D 是 //namespace:module 格式的完全限定名稱,系統(tǒng)將僅在指定的命名空間中搜索指定的模塊名稱。
  2. 否則,Soong 將首先查找在命名空間 N 中聲明的名為 D 的模塊。
  3. 如果該模塊不存在,Soong 會在命名空間 I1、I2、I3…中查找名為 D 的模塊。
  4. 最后,Soong 在根命名空間中查找。

舉個栗子,實際解析一下

以 frameworks/native/services/surfaceflinger/Android.bp 的前 1/3 內(nèi)容為例。

cc_defaults { // 到這個鏈接里面查一下,
// https://ci.android.com/builds/submitted/9670465/linux/latest/view/soong_build.html,
// cc_defaults provides a set of properties that can be inherited by other cc modules. A module 
// can use the properties from a cc_defaults using `defaults: ["<:default_module_name>"]`. 
// Properties of both modules are merged (when possible) by prepending the default module's values to 
// the depending module's values. 
// cc_defaults 一般是被繼承的類常用的類型。
    name: "surfaceflinger_defaults", // 這個 module 的名字是 surfaceflinger_defaults,字符串類型。
    cflags: [ // 這是 gcc 的編譯選項,不懂的話,可以參考下面鏈接:
              // https://blog.csdn.net/shenhuxi_yu/article/details/79788760。
        "-DLOG_TAG=\"SurfaceFlinger\"",
        "-Wall",
        "-Werror",
        "-Wthread-safety",
        "-Wunused",
        "-Wunreachable-code",
    ],
    cppflags: ["-std=c++1z"], // 這是 g++ 的編譯選項。
}

cc_defaults { // 類型是 cc_defaults。
    name: "libsurfaceflinger_defaults", // 名字是 libsurfaceflinger_defaults
    defaults: ["surfaceflinger_defaults"], // 從 surfaceflinger_defaults 繼承。
    cflags: [ // gcc 編譯選項。
        "-DGL_GLEXT_PROTOTYPES",
        "-DEGL_EGLEXT_PROTOTYPES",
    ],

    shared_libs: [ // list of string, list of shared libraries that provide headers for this binding.
        "android.frameworks.vr.composer@1.0",
        "android.hardware.configstore-utils",
        "android.hardware.configstore@1.0",
        "android.hardware.configstore@1.1",
        "android.hardware.graphics.allocator@2.0",
        "android.hardware.graphics.composer@2.1",
        "android.hardware.graphics.composer@2.2",
        "android.hardware.power@1.0",
        "libbase",
        "libbinder",
        "libbufferhubqueue",
        "libcutils",
        "libdl",
        "libEGL",
        "libfmq",
        "libGLESv1_CM",
        "libGLESv2",
        "libgui",
        "libhardware",
        "libhidlbase",
        "libhidltransport",
        "libhwbinder",
        "liblayers_proto",
        "liblog",
        "libpdx_default_transport",
        "libprotobuf-cpp-lite",
        "libsync",
        "libtimestats_proto",
        "libui",
        "libutils",
        "libvulkan",
    ],
    static_libs: [ // list of string, list of static libraries that provide headers for this binding.
        "libserviceutils",
        "libtrace_proto",
        "libvkjson",
        "libvr_manager",
        "libvrflinger",
    ],
    header_libs: [ // list of string, List of libraries which export include paths required for this module.
        "android.hardware.graphics.composer@2.1-command-buffer",
        "android.hardware.graphics.composer@2.2-command-buffer",
    ],
    export_static_lib_headers: [ // list of string, list of static libraries to re-export include directories from. Entries must be present in static_libs.
        "libserviceutils",
    ],
    export_shared_lib_headers: [ // list of string, list of shared libraries to re-export include directories from. Entries must be present in shared_libs.
        "android.hardware.graphics.allocator@2.0",
        "android.hardware.graphics.composer@2.1",
        "android.hardware.graphics.composer@2.2",
        "libhidlbase",
        "libhidltransport",
        "libhwbinder",
    ],
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容