Obsługa systemu kompilacji VNDK

W Androidzie 8.1 i nowszych system kompilacji ma wbudowaną obsługę VNDK. Gdy włączysz obsługę VNDK, system kompilacji sprawdza zależności między modułami, tworzy wersję dla dostawcy dla 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 frameworku i moduły dostawcy mogą zależeć od:libexample:

libexampleVendor_available:true i vndk.enabled:true

Rysunek 1. Obsługa włączona.

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

Jeśli libexample jest używany zarówno przez moduły frameworku, jak i moduły dostawcy, tworzone są 2 warianty libexample. Wersja podstawowa (o nazwie libexample) jest używana przez moduły platformy, a wersja dostawcy (o nazwie libexample.vendor) jest używana przez moduły dostawcy. Te 2 wersje są instalowane w różnych katalogach:

  • Wersja podstawowa jest instalowana w /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 globalny wpływ: gdy jest zdefiniowane w BoardConfig.mk, sprawdzane są wszystkie moduły. Nie ma mechanizmu, który pozwalałby umieścić dany moduł na czarnej lub białej liście. Przed dodaniem BOARD_VNDK_VERSION musisz usunąć wszystkie niepotrzebne zależności. Aby przetestować i skompilować moduł, ustaw BOARD_VNDK_VERSION w swoich zmiennych środowiskowych:

$ BOARD_VNDK_VERSION=current m module_name.vendor

Gdy włączona jest funkcja BOARD_VNDK_VERSION, usuniętych jest kilka domyślnych ścieżek wyszukiwania nagłówków globalnych. Należą do nich:

  • 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 wyraźnie określić zależności za pomocą atrybutów header_libs, static_libs lub shared_libs.

VNDK APEX

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

VNDK APEX

Rysunek 2. VNDK APEX.

Definicja modułu

Aby skompilować Androida z BOARD_VNDK_VERSION, musisz zmienić definicję modułu w pliku Android.mk lub Android.bp. W tej sekcji znajdziesz różne rodzaje definicji modułów, kilka właściwości modułów związanych z VNDK oraz testy zależności wdrożone w systemie kompilacji.

Moduły dostawców

Moduły dostawcy to specyficzne dla dostawcy pliki wykonywalne lub biblioteki udostępnione, które muszą być zainstalowane na partycji dostawcy. W plikach Android.bp moduły dostawcy muszą ustawić własność dostawcy lub własność 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 parametr BOARD_VNDK_VERSION jest zdefiniowany, system kompilacji nie zezwala na zależności między modułami dostawcy a modułami frameworku i wypisuje błędy, jeśli:

  • moduł bez vendor:true zależy od modułu z vendor:true,
  • moduł z wartością vendor:true jest zależny od modułu, który nie jest modułem llndk_library i nie zawiera ani wartości vendor:true, ani vendor_available:true;

Sprawdzanie zależności dotyczy header_libs, static_libsshared_libsAndroid.bp oraz LOCAL_HEADER_LIBRARIES, LOCAL_STATIC_LIBRARIESLOCAL_SHARED_LIBRARIESAndroid.mk.

LL-NDK

Biblioteki udostępnione LL-NDK to biblioteki udostępnione ze stabilnymi ABI. Moduły platformy i dostawcy korzystają z tej samej i najnowszej implementacji. W przypadku każdej biblioteki udostępnionej LL-NDK obiekt cc_library zawiera właściwość llndk z plikiem symboli:

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

Plik symboli zawiera opis symboli widocznych dla modułów dostawców. 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ólną typu stub dla modułów dostawców, które są łączone z tymi bibliotekami, gdy włączona jest opcja BOARD_VNDK_VERSION. Symbol jest umieszczony w zasobach wspólnych tylko wtedy, gdy:

  • nie jest zdefiniowana w sekcji kończącej się na _PRIVATE lub _PLATFORM,
  • Nie zawiera tagu #platform-only,
  • Nie ma tagów #introduce* lub tag pasuje do miejsca docelowego.

VNDK

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

Jeśli vendor_available lub vndk.enabled jest true, można utworzyć 2 warianty (core i vendor). Wariant podstawowy należy traktować jako moduł frameworku, a wariant dostawcy jako moduł dostawcy. Jeśli niektóre moduły platformy zależą od tego modułu, tworzony jest wariant podstawowy. Jeśli niektóre moduły dostawcy zależą od tego modułu, wariant dostawcy jest tworzony. System kompilacji wymusza te kontrole zależności:

  • Główny wariant działa zawsze tylko w ramach platformy i jest niedostępny dla modułów dostawcy.
  • Wersja dostawcy jest zawsze niedostępna 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ć albo modułem llndk_library, albo modułem vendor_available lub vndk.enabled.
  • Jeśli vendor_available ma wartość true, wariant dostawcy jest dostępny dla wszystkich modułów dostawcy.
  • Jeśli vendor_available to 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 dla cc_library lub cc_library_shared jest określana według tych reguł:

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

W tabeli poniżej podano, jak system kompilacji obsługuje warianty dostawcy:

vendor_available vndk
włączono
vndk
support_same_process
Opisy wariantów dostawcy
true false false Wersje dostawcy są dostępne WYŁĄCZNIE. Biblioteki udostępnione są instalowane w /vendor/lib[64].
true Nieprawidłowa (błąd kompilacji)
true false Wersje dostawcy to VNDK. Biblioteki udostępnione są instalowane w VNDK APEX.
true Wersje dostawcy to VNDK-SP. Biblioteki współdzielone są instalowane w VNDK APEX.

false

false

false

Brak wersji dostawcy. Ten moduł jest przeznaczony TYLKO DO UŻYTKU Z FRAMEWORKIEM.

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

Rozszerzenia VNDK

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

Definiowanie rozszerzeń VNDK

W Androidzie 9 i nowszych Android.bp natywnie obsługuje rozszerzenia VNDK. Aby utworzyć rozszerzenie VNDK, zdefiniuj inny moduł za pomocą właściwości vendor:true i extends:

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 nazwę biblioteki współdzielonej VNDK-SP).
  • Rozszerzenia VNDK (lub rozszerzenia VNDK-SP) są nazywane według nazwy modułu podstawowego, z którego korzystają. Na przykład binarny kod wyjściowy funkcji 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 udostępnione muszą mieć zarówno vndk.enabled:true, jak i vendor_available:true.

Rozszerzenie VNDK-SP musi być rozszerzeniem biblioteki współdzielonej 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 dostawców:

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

Używanie rozszerzeń VNDK

Jeśli moduł dostawcy zależy od dodatkowych interfejsów API zdefiniowanych przez rozszerzenia VNDK, musi on określać nazwę rozszerzenia VNDK w swojej 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 zostaną automatycznie zainstalowane 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 sobie radzić z subtelnymi różnicami (np. dodawanie lub usuwanie funkcji z jednej z wersji) między tymi 3 bibliotekami udostępnionymi 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 warunkowego kompilatora

System kompilacji Androida domyślnie definiuje __ANDROID_VNDK__ dla wersji dostawcy i rozszerzeń VNDK. Kod możesz chronić za pomocą zabezpieczeń preprocesora C:

void all() { }

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

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

Oprócz __ANDROID_VNDK__ w parametrie Android.bp można podać inną wartość cflags lub cppflags. Wartość cflags lub cppflags podana w target.vendor jest specyficzna dla wersji dostawcy.

Na przykład ta definicja Android.bp definiuje libexample i 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",
    ],
}

A tak wygląda kod pliku 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 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

Sprawdzanie ABI VNDK porównuje ABI wariantów dostawcy VNDK i rozszerzeń VNDK z pobranymi referencyjnymi zrzutami ABI w prebuilts/abi-dumps/vndk.

  • Symbole wyeksportowane przez warianty dostawcy VNDK (np. /apex/com.android.vndk.v${VER}/lib[64]/libexample.so) muszą być identyczne z definiowanymi w zbiorach danych ABI (nie mogą być ich superzbiorami).
  • Symbole eksportowane przez rozszerzenia VNDK (np. /vendor/lib[64]/vndk/libexample.so) muszą być superzbiorami symboli zdefiniowanych w zrzutach ABI.

Jeśli warianty dostawcy VNDK lub rozszerzenia VNDK nie spełniają powyższych wymagań, sprawdzacz ABI VNDK wygeneruje błędy kompilacji i zatrzyma kompilację.

Wyklucz pliki źródłowe lub biblioteki udostępnione z wariantów dostawcy

Aby wykluczyć pliki źródłowe z wariantu dostawcy, dodaj je do właściwości exclude_srcs. Aby mieć pewność, że biblioteki współdzielone nie są powiązane z wariantem dostawcy, dodaj te biblioteki do usługi 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 interfejsu libexample_cond_exclude zawiera kod z usług fwk.c i both.c i zależy od bibliotek udostępnionych libfwk_only i libboth. Wersja dostawcy atrybutu libexample_cond_exclude zawiera tylko kod z atrybutu both.c, ponieważ atrybut fwk.c jest wykluczony przez usługę exclude_srcs. Podobnie zależy ona tylko od biblioteki udostępnionej libboth, ponieważ libfwk_only jest wykluczona przez właściwość exclude_shared_libs.

Eksportuj nagłówki z rozszerzeń VNDK

Rozszerzenie VNDK może dodać nowe klasy lub nowe funkcje do wspólnej biblioteki VNDK. Zalecamy zachowanie tych deklaracji w niezależnych nagłówkach i unikanie zmieniania istniejących nagłówków.

Na przykład został utworzony nowy plik nagłówka include-ext/example/ext/feature_name.h dla rozszerzenia VNDK libexample_ext:

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

W tym przykładzie Android.bp, libexample eksportuje tylko include, a libexample_ext eksportuje zarówno include, jak i include-ext. Dzięki temu użytkownicy domeny libexample nie będą nieprawidłowo uwzględniani w kodzie 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ń do niezależnych plików nagłówków nie jest możliwe, możesz dodać #ifdef. Upewnij się jednak, że wszyscy użytkownicy rozszerzenia VNDK dodają flagi definicji. Możesz zdefiniować cc_defaults, aby dodać flagi do cflags, oraz połączyć biblioteki udostępnione z shared_libs.

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

Pole cc_defaults o nazwie libexample2_ext_defaults jest zdefiniowane dla użytkowników usługi 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",
    ],
}

Użytkownicy usługi libexample2_ext mogą po prostu umieścić wartość libexample2_ext_defaults w 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 udostępnione lub pakiety, które powinny zostać zainstalowane na urządzeniu. Zależność pośrednia określonych modułów jest również domyślnie instalowana na urządzeniu.

Jeśli opcja BOARD_VNDK_VERSION jest włączona, moduły z wartością vendor_available lub vndk.enabled są traktowane w szczególny sposób. Jeśli moduł frameworku zależy od modułu z vendor_available lub vndk.enabled, wariant podstawowy jest uwzględniony w zestawie instalacji przejściowej. Jeśli moduł dostawcy zależy od modułu z vendor_available, wariant dostawcy jest uwzględniony w transytywnym zestawie instalacji. Jednak warianty modułów dostawców z vndk.enabled są instalowane niezależnie od tego, czy są używane przez moduły dostawców.

Jeśli zależności są niewidoczne dla systemu kompilacji (np. biblioteki współdzielone, które można otworzyć za pomocą dlopen() w czasie wykonywania), należy podać nazwy modułów w PRODUCT_PACKAGES, aby zainstalować te moduły w wyraźny sposób.

Jeśli moduł ma parametr vendor_available lub vndk.enabled, jego nazwa oznacza jego podstawowy wariant. Aby bezpośrednio określić wersję dostawcy w PRODUCT_PACKAGES, dołącz do nazwy modułu sufiks .vendor. Na przykład:

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

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

PRODUCT_PACKAGES += libexample.vendor