CMake

Android NDK는 CMake를 사용하여 애플리케이션의 C 및 C++ 코드를 컴파일하도록 지원합니다. 이 페이지에서는 Android Gradle 플러그인의 ExternalNativeBuild를 통해서 또는 CMake를 직접 호출할 때 NDK와 함께 CMake를 사용하는 방법을 설명합니다.

CMake 도구 모음 파일

NDK는 도구 모음 파일을 통해 CMake를 지원합니다. 도구 모음 파일은 크로스 컴파일을 위해 도구 모음의 동작을 맞춤설정하는 CMake 파일입니다. NDK에 사용되는 도구 모음 파일은 NDK의 <NDK>/build/cmake/android.toolchain.cmake에 있습니다.

ABI, minSdkVersion 등과 같은 빌드 매개변수는 cmake를 호출할 때 명령줄에 제공됩니다. 지원되는 인수 목록은 도구 모음 인수 섹션을 참고하세요.

'새로운' 툴체인 파일

이전 NDK에서는 툴체인 파일의 새로운 구현을 실험했습니다. NDK의 툴체인 파일 사용과 할 수 있습니다. 이로 인해 상당한 양의 행동을 개선하지는 못했지만, 더 이상 이 목표를 추구하지 않습니다.

'새로운' 도구 모음 파일에 '레거시' 파일에 비해 동작 회귀가 있음 툴체인 파일에 포함됩니다. 기본 동작은 권장되는 워크플로입니다. 만약 -DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF를 사용하는 경우 해당 플래그를 삭제하는 것이 좋습니다. 삭제할 수 있습니다 새 툴체인 파일이 레거시와 패리티에 도달하지 못함 툴체인 파일을 포함하므로 동작 회귀가 있을 수 있습니다.

새 툴체인 파일의 사용은 권장하지 않지만 NDK에서 삭제할 계획입니다. 그렇게 하면 새 도구 모음 파일과 기존 도구 모음 파일 간의 동작 차이 안타깝게도 '기존'임을 분명히 하도록 옵션 이름을 실제로는 을 사용하는 것이 좋습니다. Google Meet을 사용해 새 툴체인 파일을 교체하느라 새 툴체인 파일을 교체할 수는 있지만, 문제가 수정되지 않을 수 있으며 대신 마이그레이션해야 합니다

사용

Gradle

externalNativeBuild를 사용할 때는 CMake 도구 모음 파일이 자동으로 사용됩니다. 자세한 내용은 Android 스튜디오의 C 및 C++ 코드를 프로젝트에 추가 가이드를 참고하세요.

명령줄

Gradle 외부에서 CMake로 빌드할 때는 도구 모음 파일 자체 및 인수를 CMake에 전달해야 합니다. 예:

$ cmake \
    -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
    -DANDROID_ABI=$ABI \
    -DANDROID_PLATFORM=android-$MINSDKVERSION \
    $OTHER_ARGS

도구 모음 인수

CMake 도구 모음 파일에 다음 인수를 전달할 수 있습니다. Gradle로 빌드하는 경우 ExternalNativeBuild 문서의 설명대로 android.defaultConfig.externalNativeBuild.cmake.arguments에 인수를 추가합니다. 명령줄에서 빌드하는 경우 -D를 사용해 CMake에 인수를 전달합니다. 예를 들어 armeabi-v7a가 Neon으로 빌드되지 않도록 할 수 있습니다. -DANDROID_ARM_NEON=FALSE를 전달합니다.

ANDROID_ABI

타겟 ABI입니다. 지원되는 ABI에 관한 자세한 내용은 Android ABI를 참고하세요.

Gradle

Gradle은 이 인수를 자동으로 제공합니다. 이 인수를 build.gradle 파일에서 명시적으로 설정하지 마세요. ABI Gradle의 타겟을 제어하려면 Android ABI의 설명대로 abiFilters를 사용하세요.

명령줄

CMake는 빌드별 하나의 타겟을 기준으로 빌드합니다. Android ABI 두 개 이상을 타겟팅하려면 ABI별로 한 번씩 빌드해야 합니다. ABI별로 다른 빌드 디렉터리를 사용하여 빌드 간의 충돌을 피하는 것이 좋습니다.

메모
armeabi-v7a
armeabi-v7a with NEON armeabi-v7a과 동일합니다.
arm64-v8a
x86
x86_64

ANDROID_ARM_MODE

armeabi-v7a용 arm 명령을 생성할지 아니면 thumb 명령을 생성할지 지정합니다. 다른 ABI의 경우 영향이 없습니다. 자세한 내용은 Android ABI 문서를 참고하세요.

메모
arm
thumb 기본 동작입니다.

ANDROID_NATIVE_API_LEVEL

ANDROID_PLATFORM의 별칭입니다.

ANDROID_PLATFORM

애플리케이션 또는 라이브러리에서 지원하는 최소 API 수준을 지정합니다. 이 값은 애플리케이션의 minSdkVersion에 해당합니다.

Gradle

Android Gradle 플러그인을 사용하는 경우 이 값은 애플리케이션의 minSdkVersion과 일치하는 값으로 자동 설정되며 수동으로 설정해서는 안 됩니다.

명령줄

CMake를 직접 호출하는 경우 사용 중인 NDK가 지원하는 API 수준 중 가장 낮은 수준이 기본값으로 설정됩니다. 예를 들어 NDK r20의 경우 이 값은 API 수준 16으로 기본 설정됩니다.

이 매개변수에는 여러 형식이 허용됩니다.

  • android-$API_LEVEL
  • $API_LEVEL
  • android-$API_LETTER

$API_LETTER 형식을 사용하면 출시와 관련된 번호를 확인하지 않고도 android-N을 지정할 수 있습니다. 일부 출시에서는 문자 증가 없이 API가 증가했습니다. 이러한 API는 -MR1 접미사를 추가하여 지정할 수 있습니다. 예를 들어 API 수준 25는 android-N-MR1입니다.

ANDROID_STL

이 애플리케이션에 사용할 STL을 지정합니다. 자세한 내용은 C++ 라이브러리 지원을 참고하세요. 기본적으로 c++_static이 사용됩니다.

메모
c++_shared libc++의 공유 라이브러리 변형입니다.
c++_static libc++의 정적 라이브러리 변형입니다.
none C++ 표준 라이브러리 지원이 없습니다.
system 시스템 STL입니다.

컴파일러 플래그 관리

빌드의 컴파일러 또는 링커에 특정 플래그를 전달해야 하는 경우 CMake 문서에서 set_target_compile_options 및 표시됩니다. '참고 항목' 해당 페이지 하단의 섹션에 몇 가지 유용한 단서가 나왔습니다.

일반적으로 가장 좋은 방법은 컴파일러 플래그를 가장 좁은 범위의 레이어로 선택할 수 있습니다 모든 타겟에 적용할 플래그 (예: -Werror)는 모듈별로 반복하기가 불편하지만 여전히 거의 실행되지 않아야 합니다. 전 세계적으로 적용 (CMAKE_CXX_FLAGS)하면 서드 파티 종속 항목을 사용할 수 있습니다. 이러한 경우 플래그는 디렉터리 범위 (add_compile_options)에 적용됨

컴파일러 플래그의 좁은 하위 집합의 경우 build.gradle에서 설정할 수도 있습니다. cppFlags 또는 유사한 속성을 사용하여 파일을 삭제할 수 있습니다. 이렇게 해서는 안 됩니다. 플래그 Gradle에서 CMake로 전달되는 코드에는 놀라운 우선순위 동작이 있습니다. 구현에서 암시적으로 전달된 플래그를 재정의하는 경우 Android 코드 빌드에 필요하지 않습니다 CMake 동작 처리를 항상 선호 직접 빌드하도록 할 수 있습니다. AGP buildType별로 컴파일러 플래그를 제어해야 하는 경우 CMake에서 AGP 빌드 유형 사용을 참고하세요.

CMake에서 AGP 빌드 유형 사용

CMake 동작을 맞춤 Gradle buildType에 맞춤설정하려면 다음을 사용합니다. 빌드 유형을 사용하여 추가 CMake 플래그 (컴파일러 플래그 아님)를 전달합니다. CMake 빌드 스크립트가 읽을 수 있습니다. 예를 들어 '무료' 및 'premium' 빌드 변형이 자신의 build.gradle.kts에 의해 제어되며 해당 데이터를 CMake에 추가합니다.

android {
    buildTypes {
        free {
            externalNativeBuild {
                cmake {
                    arguments.add("-DPRODUCT_VARIANT_PREMIUM=OFF")
                }
            }
        }
        premium {
            externalNativeBuild {
                cmake {
                    arguments.add("-DPRODUCT_VARIANT_PREMIUM=ON")
                }
            }
        }
    }
}

그런 다음 CMakeLists.txt에서 다음을 수행합니다.

if (DPRODUCT_VARIANT_PREMIUM)
  # Do stuff for the premium build.
else()
  # Do stuff for the free build.
endif()

변수의 이름은 원하는 대로 정할 수 있지만 ANDROID_, APP_ 또는 CMAKE_ 접두사는 사용할 수 있습니다

예는 Sanitizers NDK 샘플을 참고하세요.

CMake 빌드 명령어 이해

CMake 빌드 문제를 디버깅할 때 Android용 크로스 컴파일 시 Gradle에서 사용하는 특정 빌드 인수를 알고 있으면 유용합니다.

Android Gradle 플러그인은 각 ABI-빌드 유형 쌍의 CMake 빌드를 실행하는 데 사용하는 빌드 인수를 build_command.txt에 저장합니다. 이러한 파일은 다음 디렉터리에 있습니다.

<project-root>/<module-root>/.cxx/cmake/<build-type>/<ABI>/

다음 스니펫은 armeabi-v7a 아키텍처를 타겟팅하는 hello-jni 샘플의 디버깅 가능한 출시를 빌드하는 CMake 인수의 예를 보여줍니다.

                    Executable : ${HOME}/Android/Sdk/cmake/3.10.2.4988404/bin/cmake
arguments :
-H${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/src/main/cpp
-DCMAKE_FIND_ROOT_PATH=${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/.cxx/cmake/universalDebug/prefab/armeabi-v7a/prefab
-DCMAKE_BUILD_TYPE=Debug
-DCMAKE_TOOLCHAIN_FILE=${HOME}/Android/Sdk/ndk/22.1.7171670/build/cmake/android.toolchain.cmake
-DANDROID_ABI=armeabi-v7a
-DANDROID_NDK=${HOME}/Android/Sdk/ndk/22.1.7171670
-DANDROID_PLATFORM=android-23
-DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a
-DCMAKE_ANDROID_NDK=${HOME}/Android/Sdk/ndk/22.1.7171670
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/build/intermediates/cmake/universalDebug/obj/armeabi-v7a
-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/build/intermediates/cmake/universalDebug/obj/armeabi-v7a
-DCMAKE_MAKE_PROGRAM=${HOME}/Android/Sdk/cmake/3.10.2.4988404/bin/ninja
-DCMAKE_SYSTEM_NAME=Android
-DCMAKE_SYSTEM_VERSION=23
-B${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/.cxx/cmake/universalDebug/armeabi-v7a
-GNinja
jvmArgs :


                    Build command args: []
                    Version: 1

미리 빌드된 라이브러리 사용

가져와야 하는 미리 빌드된 라이브러리가 AAR로 배포된 경우 스튜디오의 종속 항목 문서를 따라 라이브러리를 가져오고 사용하세요. AGP를 사용하지 않는 경우 https://google.github.io/prefab/example-workflow.html을 따르면 되지만, AGP로 이전하는 것이 훨씬 쉬울 수 있습니다.

AAR로 배포되지 않는 라이브러리의 경우 CMake로 미리 빌드된 라이브러리를 사용하는 방법은 CMake 매뉴얼에서 IMPORTED 타겟에 관한 add_library 문서를 참고하세요.

서드 파티 코드 빌드

CMake 프로젝트의 일부로 서드 파티 코드를 빌드하는 방법에는 여러 가지가 있으며 가장 효과적인 옵션은 상황에 따라 다릅니다. 가장 좋은 방법은 CMake 프로젝트에서 서드 파티 코드를 빌드하지 않는 것입니다. 대신 라이브러리의 AAR을 빌드하여 애플리케이션에서 사용합니다. 이 AAR을 게시할 필요는 없습니다. Gradle 프로젝트 내부에 있으면 됩니다.

이런 방법을 사용할 수 없는 경우 다음 단계를 따르세요.

  • 서드 파티 소스를 저장소에 복사하고 add_subdirectory를 사용하여 빌드합니다. 이는 다른 라이브러리도 CMake로 빌드된 경우에만 작동합니다.
  • ExternalProject를 정의합니다.
  • 라이브러리를 프로젝트와 별개로 빌드하고 미리 빌드된 라이브러리 사용에 따라 미리 빌드된 라이브러리로 가져옵니다.

CMake에서 YASM 지원

NDK는 YASM으로 작성된 어셈블리 코드를 x86 및 x86-64 아키텍처에서 실행하도록 빌드할 수 있게 CMake 지원을 제공합니다. YASM은 x86 및 x86-64 아키텍처용 오픈소스 어셈블러로, NASM 어셈블러에 기반합니다.

CMake로 어셈블리 코드를 빌드하려면 프로젝트의 CMakeLists.txt에서 다음과 같이 변경합니다.

  1. 값을 ASM_NASM으로 설정하여 enable_language를 호출합니다.
  2. 빌드 중인 것이 공유 라이브러리 또는 실행 바이너리 중 무엇인지에 따라 add_library 또는 add_executable을 호출합니다. 인수에서 .asm 파일(YASM의 어셈블리 프로그램용) 및 .c 파일(연결된 C 라이브러리 또는 함수용)로 이루어진 소스 파일 목록을 전달합니다.

다음 스니펫은 YASM 프로그램을 공유 라이브러리로 빌드하도록 CMakeLists.txt를 구성하는 방법을 보여줍니다.

cmake_minimum_required(VERSION 3.6.0)

enable_language(ASM_NASM)

add_library(test-yasm SHARED jni/test-yasm.c jni/print_hello.asm)

YASM 프로그램을 실행 파일로 빌드하는 방법의 예는 NDK git 저장소의 yasm 테스트를 참고하세요.

문제 신고

NDK 또는 CMake 도구 모음 파일에 문제가 발생하면 GitHub의 android-ndk/ndk Issue tracker를 통해 보고하세요. Gradle 또는 Android Gradle 플러그인 문제의 경우 대신 Studio 버그를 신고하세요.