在 Android 8.1 及更高版本中,建置系統具有內建的 VNDK 支援。啟用 VNDK 支援後,建置系統會檢查模組之間的依賴關係,為供應商模組建置特定於供應商的變體,並自動將這些模組安裝到指定目錄中。
VNDK 建置支援範例
在此範例中, Android.bp
模組定義了一個名為libexample
的函式庫。 vendor_available
屬性指示框架模組和供應商模組可能依賴libexample
:
框架執行檔/system/bin/foo
和供應商執行/vendor/bin/bar
依賴libexample
,並且在其shared_libs
屬性中包含libexample
。
如果框架模組和供應商模組都使用libexample
,則會建構libexample
的兩個變體。核心變體(以libexample
命名)由框架模組使用,供應商變體(以libexample.vendor
命名)由供應商模組使用。這兩個變體安裝到不同的目錄中:
- 核心變體安裝到
/system/lib[64]/libexample.so
。 - 供應商變體安裝到 VNDK APEX 中,因為
vndk.enabled
為true
。
有關更多詳細信息,請參閱模組定義。
配置建置支援
要為產品設備啟用完整的建置系統支援,請將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_libs
、 static_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}
取得。
模組定義
若要使用BOARD_VNDK_VERSION
建立 Android,您必須修改Android.mk
或Android.bp
中的模組定義。本節介紹不同類型的模組定義、幾個與 VNDK 相關的模組屬性以及建置系統中實現的依賴項檢查。
供應商模組
供應商模組是供應商特定的可執行檔或共用庫,必須安裝到供應商分區。在Android.bp
檔案中,供應商模組必須將供應商或專有屬性設為true
。在Android.mk
檔案中,供應商模組必須將LOCAL_VENDOR_MODULE
或LOCAL_PROPRIETARY_MODULE
設定為true
。
如果定義了BOARD_VNDK_VERSION
,則建置系統不允許供應商模組和框架模組之間存在依賴關係,並在下列情況下發出錯誤:
- 沒有
vendor:true
模組依賴有vendor:true
的模組,或者 - 具有
vendor:true
模組依賴既沒有vendor:true
也沒有vendor_available:true
非llndk_library
模組。
依賴性檢查適用於Android.bp
中的header_libs
、 static_libs
和shared_libs
,以及Android.mk
中的LOCAL_HEADER_LIBRARIES
、 LOCAL_STATIC_LIBRARIES
和LOCAL_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_library
、 cc_library_static
、 cc_library_shared
和cc_library_headers
模組定義支援三個與 VNDK 相關的屬性: vendor_available
、 vndk.enabled
和vndk.support_system_process
。
如果vendor_available
或vndk.enabled
為true
,則可以建立兩個變體( core和vendor )。核心變體應被視為框架模組,供應商變體應被視為供應商模組。如果某些框架模組依賴於該模組,則會建立核心變體。如果某些供應商模組依賴此模組,則會建立供應商變體。建置系統強制執行以下依賴性檢查:
- 核心變體始終僅為框架,供應商模組無法存取。
- 框架模組始終無法存取供應商變體。
- 在
header_libs
、static_libs
和/或shared_libs
中指定的供應商變體的所有依賴項必須是llndk_library
或具有vendor_available
或vndk.enabled
的模組。 - 如果
vendor_available
為true
,則所有供應商模組都可以存取供應商變體。 - 如果
vendor_available
為false
,則供應商變體只能由其他VNDK 或VNDK-SP 模組存取(即具有vendor:true
模組無法連結vendor_available:false
模組)。
cc_library
或cc_library_shared
的預設安裝路徑由下列規則決定:
- 核心變體安裝到
/system/lib[64]
。 - 供應商變體安裝路徑可能會有所不同:
- 如果
vndk.enabled
為false
,則供應商變體將安裝到/vendor/lib[64]
中。 - 如果
vndk.enabled
為true
,則供應商變體將安裝到 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。 | ||
| | | 沒有供應商變體。此模組僅限 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:true
和extends
屬性定義另一個模組:
cc_library { name: "libvndk", vendor_available: true, vndk: { enabled: true, }, } cc_library { name: "libvndk_ext", vendor: true, vndk: { enabled: true, extends: "libvndk", }, }
具有vendor:true
、 vndk.enabled:true
和extends
屬性的模組定義了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:true
和vendor_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
中指定不同的cflags
或cppflags
。 target.vendor
中指定的cflags
或cppflags
特定於供應商變體。
例如,下列Android.bp
定義了libexample
和libexample_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 | all , framework_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.c
和both.c
中的程式碼,並依賴共用庫libfwk_only
和libboth
。 libexample_cond_exclude
的供應商變體僅包含both.c
中的程式碼,因為fwk.c
已被exclude_srcs
屬性排除。同樣,它只依賴共享庫libboth
,因為libfwk_only
被exclude_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
同時匯出include
和include-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_defaults
的cc_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_available
或vndk.enabled
模組將被特殊處理。如果框架模組依賴具有vendor_available
或vndk.enabled
模組,則核心變體將包含在傳遞安裝集中。如果供應商模組依賴具有vendor_available
模組,則供應商變體將包含在傳遞安裝集中。但是,無論供應商模組是否使用它們,都會安裝具有vndk.enabled
的模組的供應商變體。
當依賴項對建置系統不可見時(例如,可以在執行時間使用dlopen()
開啟的共用程式庫),您應該在PRODUCT_PACKAGES
中指定模組名稱以明確安裝這些模組。
如果模組具有vendor_available
或vndk.enabled
,則模組名稱代表其核心變體。若要在PRODUCT_PACKAGES
中明確指定供應商變體,請將.vendor
後綴附加到模組名稱。例如:
cc_library { name: "libexample", srcs: ["example.c"], vendor_available: true, }
在此範例中, libexample
代表/system/lib[64]/libexample.so
, libexample.vendor
代表/vendor/lib[64]/libexample.so
。若要安裝/vendor/lib[64]/libexample.so
,請將libexample.vendor
新增至PRODUCT_PACKAGES
:
PRODUCT_PACKAGES += libexample.vendor