Obsługa systemu kompilacji VNDK

W Androidzie 8.1 i nowszych system kompilacji ma wbudowaną obsługę VNDK. Gdy obsługa VNDK jest włączona, system kompilacji sprawdza zależności między modułami, tworzy wariant specyficzny dla dostawcy w przypadku modułów dostawcy i automatycznie instaluje te moduły w wyznaczonych katalogach.

Przykład obsługi kompilacji VNDK

W tym przykładzie definicja modułu Android.bp definiuje bibliotekę o nazwie libexample. Właściwość vendor_available wskazuje, że moduły platformy i moduły dostawcy mogą zależeć od libexample:

libexample vendor_available:true and vndk.enabled:true

Włączono obsługę rysunku 1.

Zarówno plik wykonywalny platformy /system/bin/foo, jak i plik wykonywalny dostawcy /vendor/bin/bar zależą od libexample i mają libexample we właściwościach shared_libs.

Jeśli libexample jest używany zarówno przez moduły struktury, jak i moduły dostawcy, tworzone są 2 warianty libexample. Wariant podstawowy (nazwany na cześć libexample) jest używany przez moduły struktury, a wariant dostawcy (nazwany na cześć libexample.vendor) jest używany przez moduły dostawcy. Obie wersje są instalowane w różnych katalogach:

  • Wariant podstawowy jest instalowany w lokalizacji /system/lib[64]/libexample.so.
  • Wariant dostawcy jest instalowany w VNDK APEX, ponieważ vndk.enabled ma wartość true.

Więcej informacji znajdziesz w artykule Definicja modułu.

Konfigurowanie obsługi kompilacji

Aby włączyć pełną obsługę systemu kompilacji dla urządzenia, dodaj BOARD_VNDK_VERSION do BoardConfig.mk:

BOARD_VNDK_VERSION := current

To ustawienie ma wpływ globalny: gdy jest zdefiniowane w BoardConfig.mk, sprawdzane są wszystkie moduły. Nie ma mechanizmu umieszczania na czarnej ani białej liście modułu, który narusza zasady, więc przed dodaniem BOARD_VNDK_VERSION należy usunąć wszystkie niepotrzebne zależności. Aby przetestować i skompilować moduł, ustaw w zmiennych środowiskowych wartość BOARD_VNDK_VERSION:

$ BOARD_VNDK_VERSION=current m module_name.vendor

Gdy opcja BOARD_VNDK_VERSION jest włączona, kilka domyślnych globalnych ścieżek wyszukiwania nagłówków jest usuwanych. Są to między innymi:

  • 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

Jeśli moduł zależy od nagłówków z tych katalogów, musisz (jawnie) określić zależności za pomocą atrybutów header_libs, static_libs lub shared_libs.

VNDK APEX

W Androidzie 10 i starszych wersjach moduły oznaczone symbolem vndk.enabled były instalowane w /system/lib[64]/vndk[-sp]-${VER}. W Androidzie 11 i nowszych biblioteki VNDK są pakowane w formacie APEX, a nazwa pakietu APEX VNDK to com.android.vndk.v${VER}. W zależności od konfiguracji urządzenia moduł VNDK APEX jest spłaszczony lub niespłaszczony i jest dostępny pod ścieżką kanoniczną /apex/com.android.vndk.v${VER}.

VNDK APEX

Rysunek 2. VNDK APEX.

Definicja modułu

Aby utworzyć Androida z BOARD_VNDK_VERSION, musisz zmodyfikować definicję modułu w Android.mk lub Android.bp. W tej sekcji opisujemy różne rodzaje definicji modułów, kilka właściwości modułów związanych z VNDK oraz sprawdzanie zależności zaimplementowane w systemie kompilacji.

Moduły dostawców

Moduły dostawcy to pliki wykonywalne lub biblioteki współdzielone specyficzne dla dostawcy, które muszą być zainstalowane na partycji dostawcy. W plikach Android.bp moduły dostawców muszą ustawić właściwość dostawcy lub właściwość zastrzeżoną na wartość true. W plikach Android.mk moduły dostawcy muszą ustawić LOCAL_VENDOR_MODULE lub LOCAL_PROPRIETARY_MODULE na true.

Jeśli zdefiniowano BOARD_VNDK_VERSION, system kompilacji nie zezwala na zależności między modułami dostawcy a modułami platformy i generuje błędy, jeśli:

  • moduł bez vendor:true zależy od modułu z vendor:true lub
  • moduł z vendor:true zależy od modułu innego niż llndk_library, który nie ma ani vendor:true, ani vendor_available:true.

Sprawdzanie zależności dotyczy aplikacji header_libs, static_libsshared_libs w trybie Android.bp oraz aplikacji LOCAL_HEADER_LIBRARIES, LOCAL_STATIC_LIBRARIESLOCAL_SHARED_LIBRARIES w trybie Android.mk.

LL-NDK

Biblioteki udostępnione LL-NDK to biblioteki udostępnione ze stabilnymi interfejsami ABI. Zarówno moduły platformy, jak i moduły dostawcy mają tę samą i najnowszą implementację. W przypadku każdej biblioteki współdzielonej LL-NDK element cc_library zawiera właściwość llndk z plikiem symboli:

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

Plik symboli opisuje symbole widoczne dla modułów dostawcy. Na przykład:

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

Na podstawie pliku symboli system kompilacji generuje bibliotekę współdzieloną typu stub dla modułów dostawcy, które są z nią łączone, gdy włączona jest opcja BOARD_VNDK_VERSION. Symbol jest uwzględniany w bibliotece udostępnionej stub tylko wtedy, gdy:

  • nie jest zdefiniowany w sekcji kończącej się na _PRIVATE lub _PLATFORM,
  • Nie zawiera tagu #platform-only,
  • Nie zawiera tagów #introduce* lub tag jest zgodny z elementem docelowym.

VNDK

W plikach Android.bp definicje modułów cc_library, cc_library_static, cc_library_sharedcc_library_headers obsługują 3 właściwości związane z VNDK: vendor_available, vndk.enabledvndk.support_system_process.

Jeśli vendor_available lub vndk.enabled ma wartość true, mogą zostać utworzone 2 warianty (podstawowydostawcy). Wariant podstawowy powinien być traktowany jako moduł platformy, a wariant dostawcy – jako moduł dostawcy. Jeśli niektóre moduły platformy zależą od tego modułu, zostanie utworzona podstawowa wersja. Jeśli niektóre moduły dostawcy zależą od tego modułu, tworzona jest wersja dostawcy. System kompilacji wymusza te sprawdzenia zależności:

  • Wariant podstawowy jest zawsze tylko szkieletem i jest niedostępny dla modułów dostawcy.
  • Wariant dostawcy jest zawsze niedostępny dla modułów platformy.
  • Wszystkie zależności wariantu dostawcy, które są określone w header_libs, static_libs lub shared_libs, muszą być llndk_library lub modułem z vendor_available lub vndk.enabled.
  • Jeśli wartość vendor_available to true, wersja dostawcy jest dostępna dla wszystkich modułów dostawcy.
  • Jeśli vendor_available ma wartość false, wariant dostawcy jest dostępny tylko dla innych modułów VNDK lub VNDK-SP (czyli moduły z vendor:true nie mogą łączyć modułów vendor_available:false).

Domyślna ścieżka instalacji cc_library lub cc_library_shared jest określana przez te reguły:

  • Wariant podstawowy jest zainstalowany w /system/lib[64].
  • Ścieżka instalacji wariantu dostawcy może się różnić:
    • Jeśli vndk.enabled ma wartość false, wersja dostawcy jest instalowana w /vendor/lib[64].
    • Jeśli vndk.enabled ma wartość true, wariant dostawcy jest instalowany w VNDK APEX(com.android.vndk.v${VER}).

W tabeli poniżej znajdziesz podsumowanie sposobu, w jaki system kompilacji obsługuje warianty dostawcy:

vendor_available vndk
enabled
vndk
support_system_process
Opisy wariantów dostawcy
true false false Warianty sprzedawcy są TYLKO W VND. Biblioteki udostępnione są instalowane w /vendor/lib[64].
true Nieprawidłowy (błąd kompilacji)
true false Warianty dostawcy to VNDK. Biblioteki udostępnione są instalowane w VNDK APEX.
true Warianty dostawcy to VNDK-SP. Biblioteki współdzielone są instalowane w VNDK APEX.

false

false

false

Brak wariantów dostawcy. Ten moduł jest FWK-ONLY.

true Nieprawidłowy (błąd kompilacji)
true false Warianty dostawcy to VNDK-Private. Biblioteki udostępnione są instalowane w VNDK APEX. Nie mogą być bezpośrednio używane przez moduły dostawcy.
true Warianty dostawcy to VNDK-SP-Private. Biblioteki udostępnione są instalowane w VNDK APEX. Nie mogą być bezpośrednio używane przez moduły dostawcy.

Rozszerzenia VNDK

Rozszerzenia VNDK to biblioteki udostępnione VNDK z dodatkowymi interfejsami API. Rozszerzenia są instalowane w /vendor/lib[64]/vndk[-sp] (bez sufiksu wersji) i zastępują oryginalne biblioteki współdzielone VNDK w czasie działania.

Definiowanie rozszerzeń VNDK

W Androidzie 9 i nowszych wersjach Android.bp natywnie obsługuje rozszerzenia VNDK. Aby utworzyć rozszerzenie VNDK, zdefiniuj kolejny moduł z właściwościami vendor:trueextends:

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

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

Moduł z właściwościami vendor:true, vndk.enabled:trueextends definiuje rozszerzenie VNDK:

  • Właściwość extends musi określać nazwę podstawowej biblioteki współdzielonej VNDK (lub biblioteki współdzielonej VNDK-SP).
  • Rozszerzenia VNDK (lub VNDK-SP) są nazwane na podstawie nazw modułów bazowych, z których pochodzą. Na przykład binarny plik wyjściowy libvndk_ext to libvndk.so, a nie libvndk_ext.so.
  • Rozszerzenia VNDK są instalowane w /vendor/lib[64]/vndk.
  • Rozszerzenia VNDK-SP są instalowane w /vendor/lib[64]/vndk-sp.
  • Podstawowe biblioteki współdzielone muszą mieć zarówno vndk.enabled:true, jak i vendor_available:true.

Rozszerzenie VNDK-SP musi rozszerzać bibliotekę współdzieloną VNDK-SP (vndk.support_system_process musi być równe):

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

Rozszerzenia VNDK (lub rozszerzenia VNDK-SP) mogą zależeć od innych bibliotek współdzielonych dostawcy:

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

Korzystanie z rozszerzeń VNDK

Jeśli moduł dostawcy zależy od dodatkowych interfejsów API zdefiniowanych przez rozszerzenia VNDK, musi on określić nazwę rozszerzenia VNDK we właściwości 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",
    ],
}

Jeśli moduł dostawcy zależy od rozszerzeń VNDK, te rozszerzenia VNDK są automatycznie instalowane w /vendor/lib[64]/vndk[-sp]. Jeśli moduł nie zależy już od rozszerzenia VNDK, dodaj krok czyszczenia do CleanSpec.mk, aby usunąć bibliotekę współdzieloną. Na przykład:

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

Kompilacja warunkowa

W tej sekcji opisano, jak radzić sobie z subtelnymi różnicami (np. dodawaniem lub usuwaniem funkcji z jednego z wariantów) między tymi 3 bibliotekami współdzielonymi VNDK:

  • Wariant podstawowy (np. /system/lib[64]/libexample.so)
  • Wariant dostawcy (np. /apex/com.android.vndk.v${VER}/lib[64]/libexample.so)
  • Rozszerzenie VNDK (np. /vendor/lib[64]/vndk[-sp]/libexample.so)

Flagi kompilatora warunkowego

System kompilacji Androida domyślnie definiuje __ANDROID_VNDK__ dla wariantów dostawców i rozszerzeń VNDK. Kod możesz zabezpieczyć za pomocą dyrektyw preprocesora języka C:

void all() { }

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

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

Oprócz __ANDROID_VNDK__ w parametrze Android.bp można określić inne wartości cflags lub cppflags. Wartość cflags lub cppflags określona w polu target.vendor jest specyficzna dla wersji dostawcy.

Na przykład poniższy kod Android.bp definiuje 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",
    ],
}

Oto lista kodu 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

Na podstawie tych 2 plików system kompilacji generuje biblioteki współdzielone z tymi eksportowanymi symbolami:

Ścieżka instalacji Wyeksportowane symbole
/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

Wymagania dotyczące eksportowanych symboli

Narzędzie do sprawdzania interfejsu ABI VNDK porównuje interfejs ABI wariantów VNDK dostawcyrozszerzeń VNDK z referencyjnymi zrzutami interfejsu ABI w prebuilts/abi-dumps/vndk.

  • Symbole eksportowane przez warianty dostawców VNDK (np. /apex/com.android.vndk.v${VER}/lib[64]/libexample.so) muszą być identyczne z symbolami zdefiniowanymi w zrzutach ABI (nie mogą być ich nadzbiorami).
  • Symbole eksportowane przez rozszerzenia VNDK (np. /vendor/lib[64]/vndk/libexample.so) muszą być nadzbiorami symboli zdefiniowanych w zrzutach ABI.

Jeśli warianty dostawcy VNDK lub rozszerzenia VNDK nie spełniają powyższych wymagań, narzędzie do sprawdzania interfejsu ABI VNDK zgłasza błędy kompilacji i zatrzymuje kompilację.

Wykluczanie plików źródłowych lub bibliotek współdzielonych z wersji dostawcy

Aby wykluczyć pliki źródłowe z wariantu dostawcy, dodaj je do właściwości exclude_srcs. Podobnie, aby mieć pewność, że biblioteki udostępnione nie są połączone z wersją dostawcy, dodaj je do właściwości exclude_shared_libs. Na przykład:

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

W tym przykładzie podstawowy wariant libexample_cond_exclude zawiera kod z fwk.cboth.c oraz jest zależny od bibliotek współdzielonych libfwk_onlylibboth. Wariant dostawcy libexample_cond_exclude zawiera tylko kod z both.c, ponieważ fwk.c jest wykluczony przez właściwość exclude_srcs. Podobnie zależy ona tylko od biblioteki udostępnionej libboth, ponieważ biblioteka libfwk_only jest wykluczona przez właściwość exclude_shared_libs.

Eksportowanie plików nagłówkowych z rozszerzeń VNDK

Rozszerzenie VNDK może dodawać nowe klasy lub nowe funkcje do biblioteki współdzielonej VNDK. Zalecamy umieszczanie tych deklaracji w niezależnych nagłówkach i unikanie zmian w istniejących nagłówkach.

Na przykład dla rozszerzenia VNDK libexample_ext tworzony jest nowy plik nagłówkowy include-ext/example/ext/feature_name.h:

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

W przypadku Android.bp eksportowane są tylko libexample, a w przypadku libexample_ext eksportowane są zarówno include, jak i include-ext.include Dzięki temu użytkownicy libexample nie będą mogli nieprawidłowo uwzględnić 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",
    },
}

Jeśli rozdzielenie rozszerzeń na niezależne pliki nagłówkowe nie jest możliwe, alternatywą jest dodanie #ifdef. Upewnij się jednak, że wszyscy użytkownicy rozszerzenia VNDK dodają flagi definicji. Możesz zdefiniować cc_defaults, aby dodać flagi definicji do cflags i połączyć biblioteki udostępnione z shared_libs.

Aby na przykład dodać nową funkcję elementu Example2::get_b() do rozszerzenia VNDK libexample2_ext, musisz zmodyfikować istniejący plik nagłówkowy i dodać zabezpieczenie #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_

Dla użytkowników libexample2_ext zdefiniowano cc_defaults o nazwie libexample2_ext_defaults:

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

Użytkownicy libexample2_ext mogą po prostu dodać libexample2_ext_defaults do właściwości defaults:

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

Pakiety produktów

W systemie kompilacji Androida zmienna PRODUCT_PACKAGES określa pliki wykonywalne, biblioteki współdzielone lub pakiety, które mają zostać zainstalowane na urządzeniu. Zależności pośrednie określonych modułów są również instalowane na urządzeniu.

Jeśli opcja BOARD_VNDK_VERSION jest włączona, moduły z wartościami vendor_available lub vndk.enabled są traktowane w specjalny sposób. Jeśli moduł platformy zależy od modułu z vendor_available lub vndk.enabled, w przechodnim zestawie instalacyjnym jest uwzględniana podstawowa odmiana. Jeśli moduł dostawcy zależy od modułu z vendor_available, wariant dostawcy jest uwzględniany w przechodnim zestawie instalacyjnym. Jednak warianty modułów dostawcy z vndk.enabled są instalowane niezależnie od tego, czy są używane przez moduły dostawcy.

Jeśli zależności są niewidoczne dla systemu kompilacji (np. biblioteki współdzielone, które można otworzyć za pomocą dlopen() w czasie działania), w PRODUCT_PACKAGES należy podać nazwy modułów, aby zainstalować je jawnie.

Jeśli moduł ma oznaczenie vendor_available lub vndk.enabled, nazwa modułu oznacza jego podstawowy wariant. Aby wyraźnie określić wariant dostawcy w PRODUCT_PACKAGES, dodaj do nazwy modułu sufiks .vendor. Na przykład:

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

W tym przykładzie libexample oznacza /system/lib[64]/libexample.so, a libexample.vendor oznacza /vendor/lib[64]/libexample.so. Aby zainstalować /vendor/lib[64]/libexample.so, dodaj libexample.vendor do PRODUCT_PACKAGES:

PRODUCT_PACKAGES += libexample.vendor