پشتیبانی از سیستم ساخت VNDK

در اندروید 8.1 و بالاتر، سیستم ساخت دارای پشتیبانی داخلی VNDK است. وقتی پشتیبانی VNDK فعال است، سیستم ساخت وابستگی‌های بین ماژول‌ها را بررسی می‌کند، یک نوع خاص فروشنده برای ماژول‌های فروشنده می‌سازد و به طور خودکار آن ماژول‌ها را در فهرست‌های مشخص شده نصب می‌کند.

نمونه پشتیبانی ساخت VNDK

در این مثال، تعریف ماژول Android.bp کتابخانه ای به نام libexample را تعریف می کند. ویژگی vendor_available نشان می‌دهد که ماژول‌های چارچوب و ماژول‌های فروشنده ممکن است به libexample وابسته باشند:

libexample vendor_available:true و vndk.enabled:true

شکل 1. پشتیبانی VNDK فعال است

هر دو چارچوب اجرایی /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 است.

برای جزئیات بیشتر، به تعریف ماژول مراجعه کنید.

پیکربندی پشتیبانی ساخت

برای فعال کردن پشتیبانی کامل سیستم ساخت برای یک دستگاه محصول، 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

در اندروید 10 و پایین‌تر، ماژول‌هایی با vndk.enabled در /system/lib[64]/vndk[-sp]-${VER} نصب شدند. در اندروید 11 و بالاتر، کتابخانه های VNDK در قالب APEX بسته بندی می شوند و نام VNDK APEX com.android.vndk.v${VER} است. بسته به پیکربندی دستگاه، VNDK APEX صاف یا بدون مسطح است و از مسیر متعارف /apex/com.android.vndk.v${VER} در دسترس است.

VNDK APEX

شکل 2. VNDK APEX

تعریف ماژول

برای ساخت اندروید با BOARD_VNDK_VERSION ، باید تعریف ماژول را در 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 به یک ماژول غیر 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:
    *;
};

بر اساس فایل نماد، سیستم ساخت یک کتابخانه مشترک خرد برای ماژول‌های فروشنده ایجاد می‌کند که با فعال شدن 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 ) ساخته شود. نوع اصلی باید به عنوان یک ماژول چارچوب و نوع فروشنده باید به عنوان یک ماژول فروشنده در نظر گرفته شود. اگر برخی از ماژول های چارچوب به این ماژول وابسته باشند، نوع اصلی ساخته می شود. اگر برخی از ماژول های فروشنده به این ماژول وابسته باشند، نوع فروشنده ساخته می شود. سیستم ساخت، بررسی های وابستگی زیر را اعمال می کند:

  • نوع اصلی همیشه فقط چارچوبی است و برای ماژول های فروشنده غیرقابل دسترسی است.
  • نوع فروشنده همیشه برای ماژول های چارچوب غیرقابل دسترسی است.
  • همه وابستگی‌های نوع فروشنده، که در 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} ) نصب می‌شود.

جدول زیر نحوه عملکرد سیستم ساخت با انواع فروشنده را خلاصه می کند:

vendor_available vndk
فعال شد
vndk
support_same_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_ext.so libvndk.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 )

پرچم های کامپایلر مشروط

سیستم ساخت اندروید __ANDROID_VNDK__ را برای انواع فروشنده و افزونه های VNDK به طور پیش فرض تعریف می کند. می توانید کد را با محافظ های پیش پردازنده C محافظت کنید:

void all() { }

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

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

علاوه بر __ANDROID_VNDK__ ، cflags یا cppflags مختلف ممکن است در Android.bp مشخص شود. 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

با توجه به این دو فایل، سیستم ساخت کتابخانه های مشترک با نمادهای صادر شده زیر تولید می کند:

مسیر نصب نمادهای صادراتی
/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 ) باید ابرمجموعه نمادهای تعریف شده در dumps ABI باشند.

اگر انواع VNDK فروشنده یا افزونه های VNDK از الزامات بالا پیروی نکنند، VNDK ABI checker خطاهای ساخت منتشر می کند و ساخت را متوقف می کند.

حذف فایل‌های منبع یا کتابخانه‌های مشترک از انواع فروشنده

برای حذف فایل های منبع از نوع فروشنده، آنها را به ویژگی 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 guard اضافه کنید:

#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,
}

بسته های محصول

در سیستم ساخت اندروید، متغیر 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