Hỗ trợ hệ thống xây dựng VNDK

Trong Android 8.1 trở lên, hệ thống xây dựng đã tích hợp tính năng hỗ trợ VNDK. Khi bạn bật tính năng hỗ trợ VNDK, hệ thống xây dựng sẽ kiểm tra các phần phụ thuộc giữa các mô-đun, tạo một biến thể dành riêng cho nhà cung cấp cho các mô-đun của nhà cung cấp và tự động cài đặt các mô-đun đó vào các thư mục được chỉ định.

Ví dụ về tính năng hỗ trợ bản dựng VNDK

Trong ví dụ này, định nghĩa mô-đun Android.bp sẽ xác định một thư viện có tên là libexample. Thuộc tính vendor_available cho biết các mô-đun khung và mô-đun nhà cung cấp có thể phụ thuộc vào libexample:

libexample vendor_available:true và vndk.enabled:true

Hình 1. đã bật tính năng hỗ trợ.

Cả /system/bin/foo có thể thực thi khung và /vendor/bin/bar có thể thực thi của nhà cung cấp đều phụ thuộc vào libexample và có libexample trong thuộc tính shared_libs.

Nếu cả mô-đun khung và mô-đun nhà cung cấp đều sử dụng libexample, thì hai biến thể của libexample sẽ được tạo. Biến thể cốt lõi (được đặt tên theo libexample) được các mô-đun khung sử dụng và biến thể nhà cung cấp (được đặt tên theo libexample.vendor) được các mô-đun nhà cung cấp sử dụng. Hai biến thể được cài đặt vào các thư mục khác nhau:

  • Biến thể cốt lõi được cài đặt vào /system/lib[64]/libexample.so.
  • Biến thể của nhà cung cấp được cài đặt vào VNDK APEX vì vndk.enabledtrue.

Để biết thêm thông tin chi tiết, hãy xem phần Định nghĩa mô-đun.

Định cấu hình tính năng hỗ trợ bản dựng

Để bật tính năng hỗ trợ đầy đủ cho hệ thống xây dựng cho một thiết bị sản phẩm, hãy thêm BOARD_VNDK_VERSION vào BoardConfig.mk:

BOARD_VNDK_VERSION := current

Chế độ cài đặt này có hiệu lực chung: Khi được xác định trong BoardConfig.mk, tất cả các mô-đun sẽ được kiểm tra. Vì không có cơ chế nào để đưa một mô-đun vi phạm vào danh sách đen hoặc danh sách trắng, nên bạn nên xoá tất cả các phần phụ thuộc không cần thiết trước khi thêm BOARD_VNDK_VERSION. Bạn có thể kiểm thử và biên dịch một mô-đun bằng cách đặt BOARD_VNDK_VERSION trong các biến môi trường:

$ BOARD_VNDK_VERSION=current m module_name.vendor

Khi BOARD_VNDK_VERSION được bật, một số đường dẫn tìm kiếm tiêu đề chung mặc định sẽ bị xoá. bao gồm:

  • 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

Nếu một mô-đun phụ thuộc vào tiêu đề của các thư mục này, thì bạn phải chỉ định (rõ ràng) các phần phụ thuộc bằng header_libs, static_libs và/hoặc shared_libs.

VNDK APEX

Trong Android 10 trở xuống, các mô-đun có vndk.enabled được cài đặt trong /system/lib[64]/vndk[-sp]-${VER}. Trong Android 11 trở lên, thư viện VNDK được đóng gói ở định dạng APEX và tên của VNDK APEX là com.android.vndk.v${VER}. Tuỳ thuộc vào cấu hình thiết bị, VNDK APEX sẽ được làm phẳng hoặc không làm phẳng và có sẵn trong đường dẫn chính tắc /apex/com.android.vndk.v${VER}.

VNDK APEX

Hình 2. VNDK APEX.

Định nghĩa mô-đun

Để tạo Android bằng BOARD_VNDK_VERSION, bạn phải sửa đổi định nghĩa mô-đun trong Android.mk hoặc Android.bp. Phần này mô tả nhiều loại định nghĩa mô-đun, một số thuộc tính mô-đun liên quan đến VNDK và các bước kiểm tra phần phụ thuộc được triển khai trong hệ thống xây dựng.

Mô-đun nhà cung cấp

Mô-đun nhà cung cấp là các tệp thực thi dành riêng cho nhà cung cấp hoặc thư viện dùng chung phải được cài đặt vào phân vùng nhà cung cấp. Trong tệp Android.bp, các mô-đun của nhà cung cấp phải đặt thuộc tính của nhà cung cấp hoặc thuộc tính độc quyền thành true. Trong tệp Android.mk, các mô-đun của nhà cung cấp phải đặt LOCAL_VENDOR_MODULE hoặc LOCAL_PROPRIETARY_MODULE thành true.

Nếu BOARD_VNDK_VERSION được xác định, hệ thống xây dựng sẽ không cho phép các phần phụ thuộc giữa mô-đun nhà cung cấp và mô-đun khung, đồng thời phát lỗi nếu:

  • mô-đun không có vendor:true phụ thuộc vào mô-đun có vendor:true, hoặc
  • một mô-đun có vendor:true phụ thuộc vào một mô-đun không phải llndk_library, không có vendor:true hoặc vendor_available:true.

Quy trình kiểm tra phần phụ thuộc áp dụng cho header_libs, static_libsshared_libs trong Android.bp, cũng như LOCAL_HEADER_LIBRARIES, LOCAL_STATIC_LIBRARIESLOCAL_SHARED_LIBRARIES trong Android.mk.

LL-NDK

Thư viện dùng chung LL-NDK là thư viện dùng chung có ABI ổn định. Cả mô-đun khung và nhà cung cấp đều có chung cách triển khai và là cách triển khai mới nhất. Đối với mỗi thư viện dùng chung LL-NDK, cc_library chứa một thuộc tính llndk có tệp biểu tượng:

cc_library {
    name: "libvndksupport",
    llndk: {
        symbol_file: "libvndksupport.map.txt",
    },
}

Tệp biểu tượng mô tả các biểu tượng hiển thị cho các mô-đun của nhà cung cấp. Ví dụ:

LIBVNDKSUPPORT {
  global:
    android_load_sphal_library; # llndk
    android_unload_sphal_library; # llndk
  local:
    *;
};

Dựa trên tệp biểu tượng, hệ thống xây dựng sẽ tạo một thư viện dùng chung mã giả lập cho các mô-đun nhà cung cấp. Các mô-đun này liên kết với các thư viện này khi BOARD_VNDK_VERSION được bật. Một biểu tượng chỉ được đưa vào thư viện chia sẻ mã giả lập nếu:

  • Không được xác định ở cuối phần bằng _PRIVATE hoặc _PLATFORM,
  • Không có thẻ #platform-only
  • Không có thẻ #introduce* hoặc thẻ này không khớp với mục tiêu.

VNDK

Trong tệp Android.bp, các định nghĩa mô-đun cc_library, cc_library_static, cc_library_sharedcc_library_headers hỗ trợ ba thuộc tính liên quan đến VNDK: vendor_available, vndk.enabledvndk.support_system_process.

Nếu vendor_available hoặc vndk.enabledtrue, thì bạn có thể tạo hai biến thể (corevendor). Biến thể cốt lõi phải được coi là mô-đun khung và biến thể nhà cung cấp phải được coi là mô-đun nhà cung cấp. Nếu một số mô-đun khung phụ thuộc vào mô-đun này, thì biến thể chính sẽ được tạo. Nếu một số mô-đun của nhà cung cấp phụ thuộc vào mô-đun này, thì biến thể của nhà cung cấp sẽ được tạo. Hệ thống xây dựng thực thi các bước kiểm tra phần phụ thuộc sau:

  • Biến thể cốt lõi luôn chỉ dành cho khung và không thể truy cập được các mô-đun của nhà cung cấp.
  • Các mô-đun khung luôn không thể truy cập vào biến thể của nhà cung cấp.
  • Tất cả phần phụ thuộc của biến thể nhà cung cấp được chỉ định trong header_libs, static_libs và/hoặc shared_libs phải là llndk_library hoặc mô-đun có vendor_available hoặc vndk.enabled.
  • Nếu vendor_availabletrue, thì tất cả các mô-đun của nhà cung cấp đều có thể truy cập vào biến thể của nhà cung cấp.
  • Nếu vendor_availablefalse, thì chỉ các mô-đun VNDK hoặc VNDK-SP khác mới có thể truy cập vào biến thể của nhà cung cấp (tức là các mô-đun có vendor:true không thể liên kết các mô-đun vendor_available:false).

Đường dẫn cài đặt mặc định cho cc_library hoặc cc_library_shared được xác định theo các quy tắc sau:

  • Biến thể cốt lõi được cài đặt vào /system/lib[64].
  • Đường dẫn cài đặt biến thể của nhà cung cấp có thể khác nhau:
    • Nếu vndk.enabledfalse, thì biến thể của nhà cung cấp sẽ được cài đặt vào /vendor/lib[64].
    • Nếu vndk.enabledtrue, thì biến thể của nhà cung cấp sẽ được cài đặt vào VNDK APEX(com.android.vndk.v${VER}).

Bảng dưới đây tóm tắt cách hệ thống xây dựng xử lý các biến thể của nhà cung cấp:

vendor_available đã bật vndk
vndk
support_same_process
Nội dung mô tả biến thể của nhà cung cấp
true false false Các biến thể của nhà cung cấp là CHỈ VND. Thư viện dùng chung được cài đặt vào /vendor/lib[64].
true Không hợp lệ (Lỗi bản dựng)
true false Các biến thể của nhà cung cấp là VNDK. Thư viện dùng chung được cài đặt vào VNDK APEX.
true Các biến thể của nhà cung cấp là VNDK-SP. Thư viện dùng chung được cài đặt vào VNDK APEX.

false

false

false

Không có biến thể của nhà cung cấp. Mô-đun này CHỈ DÀNH CHO FWK.

true Không hợp lệ (Lỗi bản dựng)
true false Các biến thể của nhà cung cấp là VNDK-Private. Thư viện dùng chung được cài đặt vào VNDK APEX. Các mô-đun của nhà cung cấp không được sử dụng trực tiếp các mô-đun này.
true Các biến thể của nhà cung cấp là VNDK-SP-Private. Thư viện dùng chung được cài đặt vào VNDK APEX. Các mô-đun nhà cung cấp không được trực tiếp sử dụng những thông tin này.

Phần mở rộng VNDK

Tiện ích VNDK là các thư viện dùng chung VNDK có các API bổ sung. Các tiện ích được cài đặt vào /vendor/lib[64]/vndk[-sp] (không có hậu tố phiên bản) và ghi đè thư viện chia sẻ VNDK gốc trong thời gian chạy.

Xác định các tiện ích VNDK

Trên Android 9 trở lên, Android.bp hỗ trợ các tiện ích VNDK ngay từ đầu. Để tạo một tiện ích VNDK, hãy xác định một mô-đun khác có thuộc tính vendor:trueextends:

cc_library {
    name: "libvndk",
    vendor_available: true,
    vndk: {
        enabled: true,
    },
}

cc_library {
    name: "libvndk_ext",
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libvndk",
    },
}

Mô-đun có các thuộc tính vendor:true, vndk.enabled:trueextends xác định tiện ích VNDK:

  • Thuộc tính extends phải chỉ định tên thư viện dùng chung VNDK cơ sở (hoặc tên thư viện dùng chung VNDK-SP).
  • Các tiện ích VNDK (hoặc tiện ích VNDK-SP) được đặt tên theo tên mô-đun cơ sở mà các tiện ích đó mở rộng. Ví dụ: tệp nhị phân đầu ra của libvndk_extlibvndk.so thay vì libvndk_ext.so.
  • Các tiện ích VNDK được cài đặt vào /vendor/lib[64]/vndk.
  • Các tiện ích VNDK-SP được cài đặt vào /vendor/lib[64]/vndk-sp.
  • Thư viện dùng chung cơ sở phải có cả vndk.enabled:truevendor_available:true.

Tiện ích VNDK-SP phải mở rộng từ thư viện dùng chung VNDK-SP (vndk.support_system_process phải bằng nhau):

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

Các tiện ích VNDK (hoặc tiện ích VNDK-SP) có thể phụ thuộc vào các thư viện dùng chung khác của nhà cung cấp:

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

Sử dụng tiện ích VNDK

Nếu mô-đun của nhà cung cấp phụ thuộc vào các API bổ sung do tiện ích VNDK xác định, thì mô-đun đó phải chỉ định tên của tiện ích VNDK trong thuộc tính 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",
    ],
}

Nếu một mô-đun nhà cung cấp phụ thuộc vào các tiện ích VNDK, thì các tiện ích VNDK đó sẽ tự động được cài đặt vào /vendor/lib[64]/vndk[-sp]. Nếu một mô-đun không còn phụ thuộc vào tiện ích VNDK, hãy thêm một bước dọn dẹp vào CleanSpec.mk để xoá thư viện dùng chung. Ví dụ:

$(call add-clean-step, rm -rf $(TARGET_OUT_VENDOR)/lib/libvndk.so)

Biên dịch có điều kiện

Phần này mô tả cách xử lý những điểm khác biệt nhỏ (ví dụ: thêm hoặc xoá một tính năng khỏi một trong các biến thể) giữa 3 thư viện dùng chung VNDK sau:

  • Biến thể chính (ví dụ: /system/lib[64]/libexample.so)
  • Biến thể của nhà cung cấp (ví dụ: /apex/com.android.vndk.v${VER}/lib[64]/libexample.so)
  • Tiện ích VNDK (ví dụ: /vendor/lib[64]/vndk[-sp]/libexample.so)

Cờ trình biên dịch có điều kiện

Theo mặc định, hệ thống xây dựng Android xác định __ANDROID_VNDK__ cho các biến thể của nhà cung cấp và tiện ích VNDK. Bạn có thể bảo vệ mã bằng các trình bảo vệ trình xử lý trước C:

void all() { }

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

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

Ngoài __ANDROID_VNDK__, bạn có thể chỉ định nhiều cflags hoặc cppflags trong Android.bp. cflags hoặc cppflags được chỉ định trong target.vendor dành riêng cho biến thể nhà cung cấp.

Ví dụ: Android.bp sau đây xác định libexamplelibexample_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",
    ],
}

Và đây là danh sách mã của 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

Theo 2 tệp này, hệ thống xây dựng tạo ra các thư viện dùng chung có các biểu tượng đã xuất như sau:

Đường dẫn cài đặt Biểu tượng đã xuất
/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

Yêu cầu đối với các ký hiệu được xuất

Trình kiểm tra ABI VNDK so sánh ABI của các biến thể nhà cung cấp VNDKcác tiện ích VNDK với tệp kết xuất ABI tham chiếu trong prebuilts/abi-dumps/vndk.

  • Các ký hiệu do các biến thể nhà cung cấp VNDK xuất (ví dụ: /apex/com.android.vndk.v${VER}/lib[64]/libexample.so) phải giống hệt (không phải tập hợp con của) các ký hiệu được xác định trong tệp báo lỗi ABI.
  • Các ký hiệu do tiện ích VNDK xuất (ví dụ: /vendor/lib[64]/vndk/libexample.so) phải là tập hợp con của các ký hiệu được xác định trong tệp báo lỗi ABI.

Nếu các biến thể nhà cung cấp VNDK hoặc các tiện ích VNDK không tuân thủ các yêu cầu ở trên, thì trình kiểm tra ABI VNDK sẽ phát lỗi bản dựng và dừng bản dựng.

Loại trừ tệp nguồn hoặc thư viện dùng chung khỏi các biến thể của nhà cung cấp

Để loại trừ các tệp nguồn khỏi biến thể của nhà cung cấp, hãy thêm các tệp đó vào thuộc tính exclude_srcs. Tương tự, để đảm bảo các thư viện dùng chung không được liên kết với biến thể của nhà cung cấp, hãy thêm các thư viện đó vào thuộc tính exclude_shared_libs. Ví dụ:

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"],
        },
    },
}

Trong ví dụ này, biến thể chính của libexample_cond_exclude bao gồm mã từ fwk.cboth.c, đồng thời phụ thuộc vào thư viện dùng chung libfwk_onlylibboth. Biến thể nhà cung cấp của libexample_cond_exclude chỉ bao gồm mã của both.cfwk.c bị thuộc tính exclude_srcs loại trừ. Tương tự, lớp này chỉ phụ thuộc vào thư viện dùng chung libbothlibfwk_only bị thuộc tính exclude_shared_libs loại trừ.

Xuất tiêu đề từ các tiện ích VNDK

Tiện ích VNDK có thể thêm các lớp mới hoặc hàm mới vào thư viện VNDK dùng chung. Bạn nên giữ lại các nội dung khai báo đó trong các tiêu đề độc lập và tránh thay đổi các tiêu đề hiện có.

Ví dụ: hệ thống sẽ tạo một tệp tiêu đề mới include-ext/example/ext/feature_name.h cho phần mở rộng libexample_extK của VND:

  • Android.bp
  • include-ext/example/ext/feature_name.h
  • include/example/example.h
  • src/example.c
  • src/ext/feature_name.c

Trong Android.bp sau đây, libexample chỉ xuất include, trong khi libexample_ext xuất cả includeinclude-ext. Điều này đảm bảo rằng người dùng libexample sẽ không đưa feature_name.h vào không chính xác:

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

Nếu không thể tách các phần mở rộng thành các tệp tiêu đề độc lập, bạn có thể thêm các trình bảo vệ #ifdef. Tuy nhiên, hãy đảm bảo rằng tất cả người dùng tiện ích VNDK đều thêm cờ xác định. Bạn có thể xác định cc_defaults để thêm cờ xác định vào cflags và liên kết các thư viện dùng chung với shared_libs.

Ví dụ: để thêm hàm thành phần mới Example2::get_b() vào phần mở rộng libexample2_ext của VNDK, bạn phải sửa đổi tệp tiêu đề hiện có và thêm lớp bảo vệ #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_

Một cc_defaults có tên là libexample2_ext_defaults được xác định cho người dùng của 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",
    ],
}

Người dùng libexample2_ext chỉ cần đưa libexample2_ext_defaults vào thuộc tính defaults:

cc_binary {
    name: "example2_user_executable",
    defaults: ["libexample2_ext_defaults"],
    vendor: true,
}

Gói sản phẩm

Trong hệ thống xây dựng Android, biến PRODUCT_PACKAGES chỉ định các tệp thực thi, thư viện dùng chung hoặc gói cần được cài đặt vào thiết bị. Các phần phụ thuộc bắc cầu của mô-đun được chỉ định cũng được cài đặt ngầm vào thiết bị.

Nếu bạn bật BOARD_VNDK_VERSION, các mô-đun có vendor_available hoặc vndk.enabled sẽ được xử lý đặc biệt. Nếu một mô-đun khung phụ thuộc vào một mô-đun có vendor_available hoặc vndk.enabled, thì biến thể cốt lõi sẽ được đưa vào nhóm cài đặt bắc cầu. Nếu một mô-đun của nhà cung cấp phụ thuộc vào một mô-đun có vendor_available, thì biến thể nhà cung cấp sẽ được đưa vào tập hợp cài đặt bắc cầu. Tuy nhiên, các biến thể của nhà cung cấp mô-đun có vndk.enabled sẽ được cài đặt cho dù các mô-đun của nhà cung cấp có sử dụng các biến thể đó hay không.

Khi hệ thống xây dựng không nhìn thấy các phần phụ thuộc (ví dụ: thư viện dùng chung có thể được mở bằng dlopen() trong thời gian chạy), bạn nên chỉ định tên mô-đun trong PRODUCT_PACKAGES để cài đặt các mô-đun đó một cách rõ ràng.

Nếu một mô-đun có vendor_available hoặc vndk.enabled, thì tên mô-đun sẽ đại diện cho biến thể cốt lõi của mô-đun đó. Để chỉ định rõ biến thể nhà cung cấp trong PRODUCT_PACKAGES, hãy thêm hậu tố .vendor vào tên mô-đun. Ví dụ:

cc_library {
    name: "libexample",
    srcs: ["example.c"],
    vendor_available: true,
}

Trong ví dụ này, libexample là viết tắt của /system/lib[64]/libexample.solibexample.vendor là viết tắt của /vendor/lib[64]/libexample.so. Để cài đặt /vendor/lib[64]/libexample.so, hãy thêm libexample.vendor vào PRODUCT_PACKAGES:

PRODUCT_PACKAGES += libexample.vendor