ב-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
משמש גם מודולים של מסגרת וגם מודולים של ספקים, נוצרות שתי וריאציות של 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 מטפלת באפשרויות של הספקים:
vendor_available | vndk enabled |
vndk support_same_process |
תיאורים של וריאנטים של ספקים |
---|---|---|---|
true |
false |
false |
הווריאציות של הספק הן VND בלבד. ספריות משותפות מותקנות ב-/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 |
הדרישות לגבי הסמלים המיוצאים
בודק ה-ABI של VNDK משווה את ה-ABI של הוריאנטים של ספקי VNDK ושל תוספים של VNDK ל-dumps של ה-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