תמיכה במערכת build של VNDK

ב-Android מגרסה 8.1 ואילך, למערכת הבנייה יש תמיכה מובנית ב-VNDK. כשמופעלת תמיכה ב-VNDK, מערכת ה-build בודקת את התלות בין המודולים, יוצרת וריאציה ספציפית לספק עבור מודולים של ספקים ומתקינה את המודולים האלה באופן אוטומטי בספריות ייעודיות.

דוגמה לתמיכה ב-VNDK build

בדוגמה הזו, הגדרת המודול Android.bp מגדירה ספרייה בשם libexample. המאפיין vendor_available מציין שמודולים של מסגרות ומודולים של ספקים עשויים להיות תלויים ב-libexample:

‪libexample vendor_available:true and vndk.enabled:true

התמיכה באיור 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 למכשיר מוצר, מוסיפים את 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}.

‫VNDK APEX

איור 2. ‫VNDK APEX.

הגדרת המודול

כדי ליצור Android באמצעות BOARD_VNDK_VERSION, צריך לשנות את הגדרת המודול ב-Android.mk או ב-Android.bp. בקטע הזה מתוארים סוגים שונים של הגדרות מודולים, כמה מאפיינים של מודולים שקשורים ל-VNDK ובדיקות תלות שמוטמעות במערכת הבנייה.

Vendor modules

מודולים של ספקים הם קבצים הפעלה או ספריות משותפות שספציפיים לספק, וצריך להתקין אותם במחיצה של הספק. בקובצי Android.bp, מודולים של ספקים צריכים להגדיר את המאפיין vendor או proprietary לערך 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 יוצרת ספרייה משותפת של stub למודולים של ספקים על סמך קובץ הסמלים, שמקושרת לספריות האלה כשמופעלת האפשרות BOARD_VNDK_VERSION. סמל נכלל בספרייה המשותפת של ה-stub רק אם:

  • לא מוגדר בסוף הקטע עם _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). צריך להתייחס לגרסת הליבה כאל מודול מסגרת ולגרסת הספק כאל מודול ספק. אם מודולים מסוימים של המסגרת תלויים במודול הזה, נוצרת וריאציה של ליבה. אם מודולים מסוימים של ספקים תלויים במודול הזה, הגרסה של הספק תיבנה. מערכת הבנייה אוכפת את בדיקות התלות הבאות:

  • גרסת הליבה היא תמיד framework-only ואין למודולים של ספקים גישה אליה.
  • מודולים של מסגרת אף פעם לא יכולים לגשת לגרסת הספק.
  • כל יחסי התלות של וריאציית הספק, שמצוינים ב-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_system_process
תיאורי וריאנטים של ספקים
true false false הווריאציות של הספק הן VND-ONLY. הספריות המשותפות מותקנות ב-/vendor/lib[64].
true לא תקין (שגיאת בנייה)
true false הווריאציות של הספק הן VNDK. הספריות המשותפות מותקנות ב-VNDK APEX.
true הווריאציות של הספק הן VNDK-SP. הספריות המשותפות מותקנות ב-VNDK APEX.

false

false

false

אין וריאציות של ספקים. המודול הזה הוא FWK-ONLY.

true לא תקין (שגיאת בנייה)
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 לבין קובצי ה-dump של ה-ABI של ההפניה בתיקייה prebuilts/abi-dumps/vndk.

  • הסמלים שמיוצאים על ידי וריאציות של ספקים ב-VNDK (לדוגמה: /apex/com.android.vndk.v${VER}/lib[64]/libexample.so) צריכים להיות זהים לסמלים שמוגדרים ב-ABI dumps (ולא קבוצות-על שלהם).
  • סמלים שמיוצאים על ידי תוספי VNDK (למשל, /vendor/lib[64]/vndk/libexample.so) צריכים להיות קבוצות-על של הסמלים שמוגדרים בקובצי ה-dump של ה-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. מומלץ להשאיר את ההצהרות האלה בכותרות נפרדות ולא לשנות את הכותרות הקיימות.

לדוגמה, קובץ כותרת חדש include-ext/example/ext/feature_name.h נוצר עבור התוסף VNDK libexample_ext:

  • 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. כך לא יכללו את feature_name.h בטעות המשתמשים של libexample:

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_

הוגדר 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,
}

חבילות מוצרים

במערכת הבנייה של 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