VNDK 빌드 시스템 지원

Android 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/barlibexample에 종속되며 shared_libs 속성 값으로 libexample을 갖습니다.

프레임워크 모듈과 공급업체 모듈이 모두 libexample을 사용한다면 libexample은 두 가지 변형으로 빌드됩니다. libexample이라는 핵심 변형은 프레임워크 모듈에서 사용하고 libexample.vendor라는 공급업체 변형은 공급업체 모듈에서 사용합니다. 두 변형은 다른 디렉터리에 설치됩니다.

  • 핵심 변형은 /system/lib[64]/libexample.so에 설치됩니다.
  • 공급업체 변형은 vndk.enabledtrue이므로 VNDK APEX에 설치됩니다.

자세한 내용은 모듈 정의를 참조하세요.

빌드 지원 구성

제품 기기에 전체 빌드 시스템을 지원하도록 사용 설정하려면 BoardConfig.mkBOARD_VNDK_VERSION을 추가합니다.

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

모듈 정의

BOARD_VNDK_VERSION으로 Android를 빌드하려면 Android.mk 또는 Android.bp에서 모듈 정의를 수정해야 합니다. 이 섹션에서는 다양한 종류의 모듈 정의, 여러 VNDK 관련 모듈 속성 및 빌드 시스템에 구현된 종속 항목 확인에 관해 설명합니다.

공급업체 모듈

공급업체 모듈은 공급업체 파티션에 설치해야 하는 공급업체별 실행 파일 또는 공유 라이브러리입니다. Android.bp 파일에서 공급업체 모듈은 공급업체 또는 독점 속성을 true로 설정해야 합니다. 또한 공급업체 모듈은 Android.mk 파일의 LOCAL_VENDOR_MODULE 또는 LOCAL_PROPRIETARY_MODULEtrue로 설정해야 합니다.

BOARD_VNDK_VERSION이 정의되면 빌드 시스템은 공급업체 모듈과 프레임워크 모듈 간에 종속 항목을 허용하지 않으며 다음과 같은 경우 오류를 표시합니다.

  • vendor:true가 없는 모듈이 vendor:true가 있는 모듈에 종속되거나
  • vendor:true가 있는 모듈이 vendor:truevendor_available:true가 모두 없는 llndk_library가 아닌 모듈에 종속됨

종속 항목 확인은 Android.bpheader_libs, static_libs, shared_libsAndroid.mkLOCAL_HEADER_LIBRARIES, LOCAL_STATIC_LIBRARIES, LOCAL_SHARED_LIBRARIES에 적용됩니다.

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_sharedcc_library_headers 모듈 정의는 vendor_available, vndk.enabled, vndk.support_system_process 세 가지 VNDK 관련 속성을 지원합니다.

vendor_available 또는 vndk.enabledtrue이면 두 가지 변형(핵심공급업체)으로 빌드될 수 있습니다. 핵심 변형은 프레임워크 모듈로, 공급업체 변형은 공급업체 모듈로 취급되어야 합니다. 일부 프레임워크 모듈이 이 모듈에 종속되는 경우 핵심 변형이 빌드됩니다. 일부 공급업체 모듈이 이 모듈에 종속되는 경우 공급업체 변형이 빌드됩니다. 빌드 시스템은 다음과 같은 종속 항목 확인을 적용합니다.

  • 핵심 변형은 항상 프레임워크 전용이며 공급업체 모듈에 액세스할 수 없습니다.
  • 공급업체 변형은 항상 프레임워크 모듈에 액세스할 수 없습니다.
  • header_libs, static_libs 또는 shared_libs에 지정된 공급업체 변형의 모든 종속 항목은 llndk_library이거나 vendor_available 또는 vndk.enabled가 있는 모듈이어야 합니다.
  • vendor_availabletrue이면 공급업체 변형은 모든 공급업체 모듈에 액세스할 수 있습니다.
  • vendor_availablefalse이면 공급업체 변형은 다른 VNDK 또는 VNDK-SP 모듈에만 액세스할 수 있습니다(즉, vendor:true가 있는 모듈은 vendor_available:false 모듈에 연결할 수 없음).

cc_library 또는 cc_library_shared의 기본 설치 경로는 다음 규칙에 따라 결정됩니다.

  • 핵심 변형은 /system/lib[64]에 설치됩니다.
  • 공급업체 변형의 설치 경로는 다를 수 있습니다.
    • vndk.enabledfalse이면 공급업체 변형은 /vendor/lib[64]에 설치됩니다.
    • vndk.enabledtrue이면 공급업체 변형은 VNDK APEX(com.android.vndk.v${VER})에 설치됩니다.

아래 표에는 빌드 시스템이 공급업체 변형을 처리하는 방식이 요약되어 있습니다.

vendor_available vndk
enabled
vndk
support_same_process
공급업체 변형 설명
true false false 공급업체 변형은 VND-ONLY입니다. 공유 라이브러리는 /vendor/lib[64]에 설치됩니다.
true Invalid(빌드 오류)
true false 공급업체 변형은 VNDK입니다. 공유 라이브러리는 VNDK APEX에 설치됩니다.
true 공급업체 변형은 VNDK-SP입니다. 공유 라이브러리는 VNDK APEX에 설치됩니다.

false

false

false

공급업체 변형이 없습니다. 이 모듈은 FWK-ONLY입니다.

true Invalid(빌드 오류)
true false 공급업체 변형은 VNDK-Private입니다. 공유 라이브러리는 VNDK APEX에 설치됩니다. 이러한 라이브러리는 공급업체 모듈에서 직접 사용하면 안 됩니다.
true 공급업체 변형은 VNDK-SP-Private입니다. 공유 라이브러리는 VNDK APEX에 설치됩니다. 이러한 라이브러리는 공급업체 모듈에서 직접 사용하면 안 됩니다.

VNDK 확장 프로그램

VNDK 확장 프로그램은 추가 API가 있는 VNDK 공유 라이브러리입니다. 확장 프로그램은 /vendor/lib[64]/vndk[-sp](버전 접미사 없음)에 설치되고 런타임 시 원본 VNDK 공유 라이브러리를 재정의합니다.

VNDK 확장 프로그램 정의

Android 9 이상에서 Android.bp는 기본적으로 VNDK 확장 프로그램을 지원합니다. VNDK 확장 프로그램을 빌드하려면 vendor:trueextends 속성이 있는 다른 모듈을 정의하면 됩니다.

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:trueextends 속성이 있는 모듈은 다음과 같이 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:truevendor_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 확장 프로그램 사용

공급업체 모듈이 VNDK 확장 프로그램에서 정의하는 추가 API에 종속되면 모듈은 shared_libs 속성에 VNDK 확장 프로그램의 이름을 지정해야 합니다.

// 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 확장 프로그램의 __ANDROID_VNDK__를 기본으로 정의합니다. C 전처리기 가드로 코드를 보호할 수 있습니다.

void all() { }

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

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

__ANDROID_VNDK__ 외에 다른 cflags 또는 cppflagsAndroid.bp에 지정할 수 있습니다. target.vendor에 지정된 cflags 또는 cppflags는 공급업체 변형에 따라 다릅니다.

예를 들어 아래 Android.bplibexamplelibexample_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 검사기VNDK 공급업체 변형의 ABI와 VNDK 확장 프로그램prebuilts/abi-dumps/vndk 아래의 참조 ABI 덤프와 비교합니다.

  • VNDK 공급업체 변형(예: /apex/com.android.vndk.v${VER}/lib[64]/libexample.so)에서 내보낸 기호는 ABI 덤프에 정의된 기호(상위 집합 아님)와 동일해야 합니다.
  • VNDK 확장 프로그램(예: /vendor/lib[64]/vndk/libexample.so)에서 내보낸 기호는 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.cboth.c의 코드를 포함하고 libfwk_onlylibboth 공유 라이브러리에 종속됩니다. fwk.cexclude_srcs 속성에 의해 제외되므로 libexample_cond_exclude의 공급업체 변형은 both.c의 코드만 포함합니다. 마찬가지로 공유 라이브러리 libboth에만 종속됩니다. libfwk_onlyexclude_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_extincludeinclude-ext를 모두 내보내는 반면 libexampleinclude만 내보냅니다. 이렇게 하면 libexample 사용자로 인해 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",
    },
}

확장 프로그램을 독립적인 헤더 파일로 분리할 수 없다면 #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_

libexample2_ext의 사용자를 위해 libexample2_ext_defaults라는 cc_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",
    ],
}

libexample2_ext의 사용자는 defaults 속성에 libexample2_ext_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를 설치하려면 PRODUCT_PACKAGESlibexample.vendor를 추가합니다.

PRODUCT_PACKAGES += libexample.vendor