VNDK 構建系統支持

在 Android 8.1 及更高版本中,建置系統具有內建的 VNDK 支援。啟用 VNDK 支援後,建置系統會檢查模組之間的依賴關係,為供應商模組建置特定於供應商的變體,並自動將這些模組安裝到指定目錄中。

VNDK 建置支援範例

在此範例中, Android.bp模組定義了一個名為libexample的函式庫。 vendor_available屬性指示框架模組和供應商模組可能依賴libexample

libexamplevendor_available:true 和 vndk.enabled:true

圖 1.啟用 VNDK 支持

框架執行檔/system/bin/foo和供應商執行/vendor/bin/bar依賴libexample ,並且在其shared_libs屬性中包含libexample

如果框架模組和供應商模組都使用libexample ,則會建構libexample的兩個變體。核心變體(以libexample命名)由框架模組使用,供應商變體(以libexample.vendor命名)由供應商模組使用。這兩個變體安裝到不同的目錄中:

  • 核心變體安裝到/system/lib[64]/libexample.so
  • 供應商變體安裝到 VNDK APEX 中,因為vndk.enabledtrue

有關更多詳細信息,請參閱模組定義

配置建置支援

要為產品設備啟用完整的建置系統支援,請將BOARD_VNDK_VERSION新增至BoardConfig.mk

BOARD_VNDK_VERSION := current

此設定具有全局效果:在BoardConfig.mk中定義時,將檢查所有模組。由於沒有將違規模組列入黑名單或白名單的機制,因此您應該在新增BOARD_VNDK_VERSION之前清除所有不必要的依賴項。您可以透過在環境變數中設定BOARD_VNDK_VERSION來測試和編譯模組:

$ BOARD_VNDK_VERSION=current m module_name.vendor

當啟用BOARD_VNDK_VERSION時,多個預設的全域標頭搜尋路徑將被刪除。這些包括:

  • frameworks/av/include
  • frameworks/native/include
  • frameworks/native/opengl/include
  • hardware/libhardware/include
  • hardware/libhardware_legacy/include
  • hardware/ril/include
  • libnativehelper/include
  • libnativehelper/include_deprecated
  • system/core/include
  • system/media/audio/include

如果模組依賴這些目錄中的標頭,則必須(明確)使用header_libsstatic_libs和/或shared_libs指定相依性。

VNDK 頂點

在 Android 10 及更低版本中,帶有vndk.enabled的模組安裝在/system/lib[64]/vndk[-sp]-${VER}中。在 Android 11 及更高版本中,VNDK 函式庫以 APEX 格式打包,VNDK APEX 的名稱為com.android.vndk.v${VER} 。根據裝置配置,VNDK APEX 是扁平化或非扁平化的,可從規範路徑/apex/com.android.vndk.v${VER}取得。

VNDK 頂點

圖 2.VNDK APEX

模組定義

若要使用BOARD_VNDK_VERSION建立 Android,您必須修改Android.mkAndroid.bp中的模組定義。本節介紹不同類型的模組定義、幾個與 VNDK 相關的模組屬性以及建置系統中實現的依賴項檢查。

供應商模組

供應商模組是供應商特定的可執行檔或共用庫,必須安裝到供應商分區。在Android.bp檔案中,供應商模組必須將供應商或專有屬性設為true 。在Android.mk檔案中,供應商模組必須將LOCAL_VENDOR_MODULELOCAL_PROPRIETARY_MODULE設定為true

如果定義了BOARD_VNDK_VERSION ,則建置系統不允許供應商模組和框架模組之間存在依賴關係,並在下列情況下發出錯誤:

  • 沒有vendor:true模組依賴有vendor:true的模組,或者
  • 具有vendor:true模組依賴既沒有vendor:true也沒有vendor_available:truellndk_library模組。

依賴性檢查適用於Android.bp中的header_libsstatic_libsshared_libs ,以及Android.mk中的LOCAL_HEADER_LIBRARIESLOCAL_STATIC_LIBRARIESLOCAL_SHARED_LIBRARIES

NDK

LL-NDK 共享庫是具有穩定 ABI 的共享庫。框架和供應商模組共享相同且最新的實作。對於每個 LL-NDK 共享庫, cc_library包含一個帶有符號檔案的llndk屬性:

cc_library {
    name: "libvndksupport",
    llndk: {
        symbol_file: "libvndksupport.map.txt",
    },
}

符號檔案描述了供應商模組可見的符號。例如:

LIBVNDKSUPPORT {
  global:
    android_load_sphal_library; # llndk
    android_unload_sphal_library; # llndk
  local:
    *;
};

基於符號文件,建置系統為供應商模組產生一個存根共享庫,當啟用BOARD_VNDK_VERSION時,該庫會與這些庫連結。只有當符號滿足以下條件時,才會包含在存根共享庫中:

  • 未在以_PRIVATE_PLATFORM結尾的部分中定義,
  • 沒有#platform-only標籤,且
  • 沒有#introduce*標籤或標籤與目標相符。

越南國家發展局

Android.bp檔案中, cc_librarycc_library_staticcc_library_sharedcc_library_headers模組定義支援三個與 VNDK 相關的屬性: vendor_availablevndk.enabledvndk.support_system_process

如果vendor_availablevndk.enabledtrue ,則可以建立兩個變體( corevendor )。核心變體應被視為框架模組,供應商變體應被視為供應商模組。如果某些框架模組依賴於該模組,則會建立核心變體。如果某些供應商模組依賴此模組,則會建立供應商變體。建置系統強制執行以下依賴性檢查:

  • 核心變體始終僅為框架,供應商模組無法存取。
  • 框架模組始終無法存取供應商變體。
  • header_libsstatic_libs和/或shared_libs中指定的供應商變體的所有依賴項必須是llndk_library或具有vendor_availablevndk.enabled的模組。
  • 如果vendor_availabletrue ,則所有供應商模組都可以存取供應商變體。
  • 如果vendor_availablefalse ,則供應商變體只能由其他VNDK 或VNDK-SP 模組存取(即具有vendor:true模組無法連結vendor_available:false模組)。

cc_librarycc_library_shared的預設安裝路徑由下列規則決定:

  • 核心變體安裝到/system/lib[64]
  • 供應商變體安裝路徑可能會有所不同:
    • 如果vndk.enabledfalse ,則供應商變體將安裝到/vendor/lib[64]中。
    • 如果vndk.enabledtrue ,則供應商變體將安裝到 VNDK APEX ( com.android.vndk.v${VER} ) 中。

下表總結了建置系統如何處理供應商變體:

供應商可用文德克
已啟用
文德克
支援相同進程
供應商變體描述
true false false供應商變體僅適用於 VND 。共用程式庫安裝到/vendor/lib[64]中。
true無效(建置錯誤)
true false供應商變體是VNDK 。共用程式庫安裝到 VNDK APEX。
true供應商變體是VNDK-SP 。共用程式庫安裝到 VNDK APEX。

false

false

false

沒有供應商變體。此模組僅限 FWK

true無效(建置錯誤)
true false供應商變體是VNDK-Private 。共用程式庫安裝到 VNDK APEX。這些不得由供應商模組直接使用。
true供應商變體是VNDK-SP-Private 。共用程式庫安裝到 VNDK APEX。這些不得由供應商模組直接使用。

VNDK 擴展

VNDK 擴充功能是具有附加 API 的 VNDK 共用程式庫。擴展安裝到/vendor/lib[64]/vndk[-sp] (無版本後綴)並在運行時覆蓋原始 VNDK 共享庫。

定義 VNDK 擴展

在 Android 9 及更高版本中, Android.bp原生支援 VNDK 擴充。若要建置 VNDK 擴展,請使用vendor:trueextends屬性定義另一個模組:

cc_library {
    name: "libvndk",
    vendor_available: true,
    vndk: {
        enabled: true,
    },
}

cc_library {
    name: "libvndk_ext",
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libvndk",
    },
}

具有vendor:truevndk.enabled:trueextends屬性的模組定義了VNDK擴充:

  • extends屬性必須指定基本 VNDK 共享庫名稱(或 VNDK-SP 共享庫名稱)。
  • VNDK 擴充(或 VNDK-SP 擴充)以其擴充的基本模組名稱命名。例如, libvndk_ext的輸出二進位檔案是libvndk.so而不是libvndk_ext.so
  • VNDK 擴充安裝到/vendor/lib[64]/vndk中。
  • VNDK-SP 擴充安裝到/vendor/lib[64]/vndk-sp
  • 基礎共享庫必須同時具有vndk.enabled:truevendor_available:true

VNDK-SP 擴充功能必須從 VNDK-SP 共享庫擴展( vndk.support_system_process必須相等):

cc_library {
    name: "libvndk_sp",
    vendor_available: true,
    vndk: {
        enabled: true,
        support_system_process: true,
    },
}

cc_library {
    name: "libvndk_sp_ext",
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libvndk_sp",
        support_system_process: true,
    },
}

VNDK 擴充(或 VNDK-SP 擴充)可能依賴其他供應商共用程式庫:

cc_library {
    name: "libvndk",
    vendor_available: true,
    vndk: {
        enabled: true,
    },
}

cc_library {
    name: "libvndk_ext",
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libvndk",
    },
    shared_libs: [
        "libvendor",
    ],
}

cc_library {
    name: "libvendor",
    vendor: true,
}

使用 VNDK 擴展

如果供應商模組依賴 VNDK 擴充功能定義的其他 API,則該模組必須在其shared_libs庫屬性中指定 VNDK 擴充功能的名稱:

// A vendor shared library example
cc_library {
    name: "libvendor",
    vendor: true,
    shared_libs: [
        "libvndk_ext",
    ],
}

// A vendor executable example
cc_binary {
    name: "vendor-example",
    vendor: true,
    shared_libs: [
        "libvndk_ext",
    ],
}

如果供應商模組依賴 VNDK 擴展,這些 VNDK 擴展將自動安裝到/vendor/lib[64]/vndk[-sp] 。如果模組不再依賴 VNDK 擴展,請在CleanSpec.mk新增清理步驟以刪除共用程式庫。例如:

$(call add-clean-step, rm -rf $(TARGET_OUT_VENDOR)/lib/libvndk.so)

條件編譯

本節介紹如何處理以下三個 VNDK 共用程式庫之間的細微差異(例如,在其中一個變體中新增或刪除功能):

  • 核心變體(例如/system/lib[64]/libexample.so
  • 供應商變體(例如/apex/com.android.vndk.v${VER}/lib[64]/libexample.so
  • VNDK 擴充(例如/vendor/lib[64]/vndk[-sp]/libexample.so

條件編譯器標誌

Android 建置系統預設為供應商變體和 VNDK 擴充定義__ANDROID_VNDK__ 。您可以使用 C 預處理器保護來保護程式碼:

void all() { }

#if !defined(__ANDROID_VNDK__)
void framework_only() { }
#endif

#if defined(__ANDROID_VNDK__)
void vndk_only() { }
#endif

除了__ANDROID_VNDK__之外,還可以在Android.bp中指定不同的cflagscppflagstarget.vendor中指定的cflagscppflags特定於供應商變體。

例如,下列Android.bp定義了libexamplelibexample_ext

cc_library {
    name: "libexample",
    srcs: ["src/example.c"],
    vendor_available: true,
    vndk: {
        enabled: true,
    },
    target: {
        vendor: {
            cflags: ["-DLIBEXAMPLE_ENABLE_VNDK=1"],
        },
    },
}

cc_library {
    name: "libexample_ext",
    srcs: ["src/example.c"],
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libexample",
    },
    cflags: [
        "-DLIBEXAMPLE_ENABLE_VNDK=1",
        "-DLIBEXAMPLE_ENABLE_VNDK_EXT=1",
    ],
}

這是src/example.c的程式碼清單:

void all() { }

#if !defined(LIBEXAMPLE_ENABLE_VNDK)
void framework_only() { }
#endif

#if defined(LIBEXAMPLE_ENABLE_VNDK)
void vndk() { }
#endif

#if defined(LIBEXAMPLE_ENABLE_VNDK_EXT)
void vndk_ext() { }
#endif

根據這兩個文件,建構系統產生具有以下導出符號的共享庫:

安裝路徑導出的符號
/system/lib[64]/libexample.so allframework_only
/apex/com.android.vndk.v${VER}/lib[64]/libexample.so all , vndk
/vendor/lib[64]/vndk/libexample.so all , vndk , vndk_ext

對導出符號的要求

VNDK ABI 檢查器VNDK 供應商變體VNDK 擴展的 ABI 與prebuilts/abi-dumps/vndk下的參考 ABI 轉儲進行比較。

  • VNDK 供應商變體(例如/apex/com.android.vndk.v${VER}/lib[64]/libexample.so )導出的符號必須與ABI 轉儲中定義的符號相同(不是其超集) 。
  • VNDK 擴展導出的符號(例如/vendor/lib[64]/vndk/libexample.so )必須是 ABI 轉儲中定義的符號的超集。

如果VNDK 供應商變體VNDK 擴充功能未能遵循上述要求,VNDK ABI 檢查器會發出建置錯誤並停止建置。

從供應商變體中排除原始檔案或共用庫

若要從供應商變體中排除來源文件,請將它們新增至exclude_srcs屬性。同樣,為了確保共享庫不會與供應商變體鏈接,請將這些庫添加到exclude_shared_libs屬性。例如:

cc_library {
    name: "libexample_cond_exclude",
    srcs: ["fwk.c", "both.c"],
    shared_libs: ["libfwk_only", "libboth"],
    vendor_available: true,
    target: {
        vendor: {
            exclude_srcs: ["fwk.c"],
            exclude_shared_libs: ["libfwk_only"],
        },
    },
}

在此範例中, libexample_cond_exclude的核心變體包括fwk.cboth.c中的程式碼,並依賴共用庫libfwk_onlylibbothlibexample_cond_exclude的供應商變體僅包含both.c中的程式碼,因為fwk.c已被exclude_srcs屬性排除。同樣,它只依賴共享庫libboth ,因為libfwk_onlyexclude_shared_libs屬性排除。

從 VNDK 擴充匯出標頭

VNDK 擴充功能可以為 VNDK 共享庫新增類別或新函數。建議將這些聲明保留在獨立的標頭中,並避免更改現有標頭。

例如,為 VNDK 擴充libexample_ext建立一個新的頭檔include-ext/example/ext/feature_name.h

  • Android.bp
  • include-ext/example/ext/feature_name.h
  • 包含/範例/範例.h
  • src/範例.c
  • src/ext/feature_name.c

在下列Android.bp中, libexample僅匯出include ,而libexample_ext同時匯出includeinclude-ext 。這可以確保libexample的使用者不會錯誤地包含feature_name.h

cc_library {
    name: "libexample",
    srcs: ["src/example.c"],
    export_include_dirs: ["include"],
    vendor_available: true,
    vndk: {
        enabled: true,
    },
}

cc_library {
    name: "libexample_ext",
    srcs: [
        "src/example.c",
        "src/ext/feature_name.c",
    ],
    export_include_dirs: [
        "include",
        "include-ext",
    ],
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libexample",
    },
}

如果將副檔名與獨立頭檔分開不可行,則另一種方法是添加#ifdef防護。但是,請確保所有 VNDK 擴充使用者都新增定義標誌。您可以定義cc_defaults以將定義標誌新增至cflags並將共用庫與shared_libs連結。

例如,要將新成員函數Example2::get_b()新增至 VNDK 擴充libexample2_ext ,您必須修改現有頭檔並新增#ifdef保護:

#ifndef LIBEXAMPLE2_EXAMPLE_H_
#define LIBEXAMPLE2_EXAMPLE_H_

class Example2 {
 public:
  Example2();

  void get_a();

#ifdef LIBEXAMPLE2_ENABLE_VNDK_EXT
  void get_b();
#endif

 private:
  void *impl_;
};

#endif  // LIBEXAMPLE2_EXAMPLE_H_

libexample2_ext的使用者定義了名為libexample2_ext_defaultscc_defaults

cc_library {
    name: "libexample2",
    srcs: ["src/example2.cpp"],
    export_include_dirs: ["include"],
    vendor_available: true,
    vndk: {
        enabled: true,
    },
}

cc_library {
    name: "libexample2_ext",
    srcs: ["src/example2.cpp"],
    export_include_dirs: ["include"],
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libexample2",
    },
    cflags: [
        "-DLIBEXAMPLE2_ENABLE_VNDK_EXT=1",
    ],
}

cc_defaults {
    name: "libexample2_ext_defaults",
    shared_libs: [
        "libexample2_ext",
    ],
    cflags: [
        "-DLIBEXAMPLE2_ENABLE_VNDK_EXT=1",
    ],
}

libexample2_ext的使用者可以簡單地在其defaults屬性中包含libexample2_ext_defaults

cc_binary {
    name: "example2_user_executable",
    defaults: ["libexample2_ext_defaults"],
    vendor: true,
}

產品包

在 Android 建置系統中,變數PRODUCT_PACKAGES指定應安裝到裝置中的執行檔、共用程式庫或套件。指定模組的傳遞依賴項也會隱含安裝到設備中。

如果啟用了BOARD_VNDK_VERSION ,則具有vendor_availablevndk.enabled模組將被特殊處理。如果框架模組依賴具有vendor_availablevndk.enabled模組,則核心變體將包含在傳遞安裝集中。如果供應商模組依賴具有vendor_available模組,則供應商變體將包含在傳遞安裝集中。但是,無論供應商模組是否使用它們,都會安裝具有vndk.enabled的模組的供應商變體。

當依賴項對建置系統不可見時(例如,可以在執行時間使用dlopen()開啟的共用程式庫),您應該在PRODUCT_PACKAGES中指定模組名稱以明確安裝這些模組。

如果模組具有vendor_availablevndk.enabled ,則模組名稱代表其核心變體。若要在PRODUCT_PACKAGES中明確指定供應商變體,請將.vendor後綴附加到模組名稱。例如:

cc_library {
    name: "libexample",
    srcs: ["example.c"],
    vendor_available: true,
}

在此範例中, libexample代表/system/lib[64]/libexample.solibexample.vendor代表/vendor/lib[64]/libexample.so 。若要安裝/vendor/lib[64]/libexample.so ,請將libexample.vendor新增至PRODUCT_PACKAGES

PRODUCT_PACKAGES += libexample.vendor