ב-Android מגרסה 8.1 ואילך, מערכת ה-build כוללת תמיכה מובנית ב-VNDK. כשהתמיכה ב-VNDK מופעלת, מערכת ה-build בודקת את יחסי התלות בין המודולים, יוצרת וריאנט ספציפי לספק עבור מודולים של ספקים ומתקינה את המודולים האלה באופן אוטומטי בספריות ייעודיות.
דוגמה לתמיכה ב-VNDK build
בדוגמה הזו, הגדרת המודול Android.bp
מגדירה ספרייה בשם libexample
. המאפיין vendor_available
מציין שמודולים של מסגרת ומודולים של ספקים עשויים להיות תלויים ב-libexample
:
איור 1. התמיכה מופעלת.
קובץ ההפעלה של המסגרת /system/bin/foo
וקובץ ההפעלה של הספק /vendor/bin/bar
תלויים ב-libexample
, ו-libexample
מופיע במאפייני shared_libs
שלהם.
אם משתמשים ב-libexample
גם במודולים של framework וגם במודולים של ספקים, המערכת יוצרת שתי וריאנטים של libexample
. וריאנט הליבה (שנקרא על שם libexample
) משמש את המודולים של המסגרת, ווריאנט הספק (שנקרא על שם libexample.vendor
) משמש את המודולים של הספק. שני הווריאנטים מותקנים בספריות שונות:
- וריאנט הליבה מותקן ב-
/system/lib[64]/libexample.so
. - וריאנט הספק מותקן ב-VNDK APEX כי
vndk.enabled
הואtrue
.
מידע נוסף זמין במאמר הגדרת מודול.
הגדרת תמיכה ב-build
כדי להפעיל תמיכה מלאה במערכת build במכשיר מוצר מסוים, צריך להוסיף את 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 APEX
ב-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}
.
איור 2. VNDK APEX.
הגדרת המודול
כדי ליצור build של Android עם BOARD_VNDK_VERSION
, צריך לשנות את הגדרת המודול ב-Android.mk
או ב-Android.bp
. בקטע הזה מתוארים סוגים שונים של הגדרות של מודולים, כמה מאפייני מודולים שקשורים ל-VNDK ובדיקות תלות שמוטמעות במערכת ה-build.
מודולים של ספקים
מודולים של ספקים הם קובצי הפעלה או ספריות משותפות ספציפיות לספק, שצריך להתקין במחיצה של הספק. בקובצי Android.bp
, מודולים של ספקים חייבים להגדיר את המאפיין של הספק או את המאפיין הקנייני כ-true
.
בקובצי Android.mk
, מודולים של ספקים חייבים להגדיר את הערך של LOCAL_VENDOR_MODULE
או LOCAL_PROPRIETARY_MODULE
בתור true
.
אם הערך של BOARD_VNDK_VERSION
מוגדר, מערכת ה-build אוסרת על יחסי תלות בין מודולים של ספקים לבין מודולים של מסגרת, ומפיקה שגיאות אם:
- מודול ללא
vendor:true
תלוי במודול עםvendor:true
, או - מודול עם
vendor:true
תלוי במודול שאינוllndk_library
שאין בוvendor:true
אוvendor_available:true
.
בדיקת התלות חלה על header_libs
, static_libs
ו-shared_libs
ב-Android.bp
, ועל LOCAL_HEADER_LIBRARIES
, LOCAL_STATIC_LIBRARIES
ו-LOCAL_SHARED_LIBRARIES
ב-Android.mk
.
LL-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: *; };
על סמך קובץ הסמלים, מערכת ה-build יוצרת ספרייה משותפת סטטית למודול של הספק, שמקושרת לספריות האלה כשהאפשרות BOARD_VNDK_VERSION
מופעלת. סמל נכלל בספרייה המשותפת של הסטאב רק אם:
- לא מוגדרת בסוף הקטע עם
_PRIVATE
או_PLATFORM
, - אין לו תג
#platform-only
, וגם - אין בו תגי
#introduce*
או שהתג תואם ליעד.
VNDK
בקבצים מסוג 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). יש להתייחס לגרסה הבסיסית כאל מודול של מסגרת, ולגרסה של הספק כאל מודול של ספק. אם חלק מהמודולים של המסגרת תלויים במודול הזה, וריאנט הליבה נוצר. אם חלק מהמודולים של הספקים תלויים במודול הזה, גרסת הספק תיבנה. מערכת ה-build אוכפת את בדיקות התלות הבאות:
- וריאנט הליבה הוא תמיד מסגרת בלבד, ולא ניתן לגשת אליו באמצעות מודולים של ספקים.
- תמיד אין גישה לאפשרות של הספק במודולים של המסגרת.
- כל יחסי התלות של וריאנט הספק, שמצוינים ב-
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}
).
- אם הערך של
הטבלה הבאה מסכמת את האופן שבו מערכת ה-build מטפלת בווריאציות של הספק:
ספק_זמין | vndk enabled |
vndk support_same_process |
תיאורים של וריאנטים של ספקים |
---|---|---|---|
true |
false |
false |
הווריאציות של הספק הן VND-ONLY. ספריות משותפות מותקנות ב-/vendor/lib[64] . |
true |
לא תקין (שגיאת build) | ||
true |
false |
הווריאציות של הספק הן VNDK. ספריות משותפות מותקנות ב-VNDK APEX. | |
true |
הווריאנטים של הספק הם VNDK-SP. ספריות משותפות מותקנות ב-VNDK APEX. | ||
|
|
|
אין וריאציות של הספק. המודול הזה מיועד ל-FWK בלבד. |
true |
לא תקין (שגיאת build) | ||
true |
false |
הווריאציות של הספק הן VNDK-Private. ספריות משותפות מותקנות ב-VNDK APEX. אסור למודול של הספק להשתמש בהם ישירות. | |
true |
הווריאציות של הספק הן VNDK-SP-Private. ספריות משותפות מותקנות ב-VNDK APEX. המודולים של הספקים לא יכולים להשתמש בהם באופן ישיר. |
תוספי VNDK
תוספים של VNDK הן ספריות משותפות של VNDK עם ממשקי API נוספים. התוספים מותקנים ב-/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
אם מודול הספק תלוי בממשקי API נוספים שהוגדרו על ידי תוספי VNDK, המודול צריך לציין את השם של תוסף VNDK במאפיין shared_libs
שלו:
// 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
)
דגלים מותנים של מהדר
מערכת ה-build של Android מגדירה את __ANDROID_VNDK__
כברירת מחדל עבור וריאנטים של ספקים ותוספים של 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
שונים. הערך של cflags
או cppflags
שצוין ב-target.vendor
הוא ספציפי לווריאנט של הספק.
לדוגמה, ה-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
על סמך שני הקבצים האלה, מערכת ה-build יוצרת ספריות משותפות עם הסמלים הבאים שיוצאו:
נתיב ההתקנה | סמלים שיוצאו |
---|---|
/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 משווה את ה-ABI של וריאציות של ספקים VNDK ותוספי VNDK לקובצי עזר של ABI ב-prebuilts/abi-dumps/vndk
.
- סמלים שמיוצאים על ידי וריאציות של ספק VNDK (למשל
/apex/com.android.vndk.v${VER}/lib[64]/libexample.so
) חייבים להיות זהים לסמלים שמוגדרים ב-Dumps של ABI (ולא לקבוצות העל). - סמלים שיוצאו על ידי תוספים של VNDK (למשל
/vendor/lib[64]/vndk/libexample.so
) חייבים להיות קבוצות רחבות יותר של הסמלים שמוגדרים ב-dumps של ABI.
אם וריאציות של ספק VNDK או תוספי VNDK לא עומדים בדרישות שצוינו למעלה, בודק ה-ABI של VNDK פולט שגיאות build ומפסיק את ה-build.
החרגת קבצי מקור או ספריות משותפות מהגרסאות של הספק
כדי להחריג קבצי מקור מהגרסה של הספק, מוסיפים אותם לנכס 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. מומלץ לשמור את ההצהרות האלה בכותרות עצמאיות ולא לשנות את הכותרות הקיימות.
לדוגמה, נוצר קובץ כותרת חדש include-ext/example/ext/feature_name.h
עבור הסיומת libexample_ext
של VNDK:
- Android.bp
- include-ext/example/ext/feature_name.h
- Include/example/example.h
- src/example.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
guards. עם זאת, חשוב לוודא שכל המשתמשים בתוסף VNDK מוסיפים את דגלים ההגדרה. אפשר להגדיר את cc_defaults
כדי להוסיף דגלים של הגדרה ל-cflags
ולקשר ספריות משותפות באמצעות shared_libs
.
לדוגמה, כדי להוסיף פונקציית חבר חדשה Example2::get_b()
להרחבה libexample2_ext
של VNDK, צריך לשנות את קובץ הכותרת הקיים ולהוסיף שומר #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_
מוגדר cc_defaults
בשם libexample2_ext_defaults
למשתמשים של libexample2_ext
:
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
יכולים פשוט לכלול את libexample2_ext_defaults
בנכס defaults
שלהם:
cc_binary { name: "example2_user_executable", defaults: ["libexample2_ext_defaults"], vendor: true, }
חבילות מוצרים
במערכת ה-build של Android, המשתנה PRODUCT_PACKAGES
מציין את קובצי ההפעלה, הספריות המשותפות או החבילות שצריך להתקין במכשיר. גם יחסי התלות הטרנזיטיביים של המודולים שצוינו מותקנים במכשיר באופן משתמע.
אם BOARD_VNDK_VERSION
מופעל, מודולים עם vendor_available
או vndk.enabled
מקבלים טיפול מיוחד. אם מודול של מסגרת תלוי במודול עם vendor_available
או vndk.enabled
, וריאנט הליבה נכלל בקבוצת ההתקנה הטרנזיטיבית. אם מודול של ספק תלוי במודול עם vendor_available
, וריאנט הספק נכלל בקבוצת ההתקנה הטרנזיטיבית. עם זאת, וריאנטים של מודולים של ספקים עם vndk.enabled
מותקנים גם אם מודולים של ספקים משתמשים בהם וגם אם לא.
כשיחסי התלות לא גלויים למערכת ה-build (למשל, ספריות משותפות שניתן לפתוח באמצעות 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