CMake

Android Studio 2.2 이상을 이용하면 NDK 및 CMake를 사용하여 C 및 C++ 코드를 네이티브 라이브러리에 컴파일할 수 있습니다. 그러면 Android Studio는 IDE의 내장 빌드 시스템인 Gradle을 사용하여 APK에 라이브러리를 패키징합니다.

Android Studio에서 CMake를 사용하는 것이 처음이라면, C 및 C++ 코드를 프로젝트에 추가로 이동하여 프로젝트에 네이티브 소스를 추가하고 CMake 빌드 스크립트를 생성하며 CMake 프로젝트를 Gradle 종속성으로 추가하는 데 대한 기본적인 사항을 배워보세요. 이 페이지는 CMake 빌드를 맞춤설정하는 데 사용할 수 있는 일부 추가 정보를 제공합니다.

Gradle에서 CMake 변수 사용

일단 Gradle 링크를 CMake 프로젝트에 링크하면, CMake가 네이티브 라이브러리를 빌드하는 방식을 변경하는 특정 NDK 전용 변수를 구성할 수 있습니다. 인수를 모듈 수준 build.gradle 파일에서 CMake에 전달하려면 다음 DSL을 사용합니다.

android {
  ...
  defaultConfig {
    ...
    // This block is different from the one you use to link Gradle
    // to your CMake build script.
    externalNativeBuild {
      cmake {
        ...
        // Use the following syntax when passing arguments to variables:
        // arguments "-DVAR_NAME=ARGUMENT".
        arguments "-DANDROID_ARM_NEON=TRUE",
        // If you're passing multiple arguments to a variable, pass them together:
        // arguments "-DVAR_NAME=ARG_1 ARG_2"
        // The following line passes 'rtti' and 'exceptions' to 'ANDROID_CPP_FEATURES'.
                  "-DANDROID_CPP_FEATURES=rtti exceptions"
      }
    }
  }
  buildTypes {...}

  // Use this block to link Gradle to your CMake build script.
  externalNativeBuild {
    cmake {...}
  }
}

다음 표는 NDK로 CMake를 사용할 때 구성할 수 있는 일부 변수를 설명합니다.

변수 이름 인수 설명
ANDROID_PLATFORM

플랫폼 이름과 그에 상응하는 Android 시스템 이미지의 전체목록은 Android NDK 네이티브 API를 참조하세요.

대상 Android 플랫폼의 이름을 지정합니다. 예: android-18은 Android 4.3(API 레벨 18)을 지정합니다.

이 플래그를 직접 변경하는 대신, defaultConfig 내의 minSdkVersion 속성이나 모듈 수준 build.gradle 파일productFlavors 블록을 설정해야 합니다. 이를 통해 라이브러리가 적절한 버전의 Android를 구동 중인 기기에 설치된 앱에서 사용되도록 할 수 있습니다. 그 후 CMake 툴체인은 다음 논리를 사용하여 빌드 중인 ABI에 최적화된 플랫폼 버전을 선택합니다.

  1. ABI에 minSdkVersion과 동일한 플랫폼 버전이 존재하는 경우, CMake는 해당 버전을 사용합니다.
  2. 그렇지 않은 경우, 즉 플랫폼 버전이 ABI의 minSdkVersion보다 낮은 경우에는 CMake가 그러한 플랫폼 버전 중 가장 높은 것을 사용합니다. 이것이 합당한 선택인 이유는 플랫폼 버전이 누락되었다는 것은 대개 이전에 이용 가능한 버전 이후로 네이티브 플랫폼 API가 변경되지 않았다는 뜻이기 때문입니다.
  3. 그렇지 않은 경우, CMake는 다음으로 이용 가능한 플랫폼 버전 중 minSdkVersion보다 높은 것을 사용합니다.
ANDROID_STL

옵션의 전체 목록은 도우미 런타임을 확인하세요.

기본적으로 CMake는 c++_static을 사용합니다.

CMake이 사용해야 하는 STL을 지정합니다.

ANDROID_PIE
  • ON(ANDROID_PLATFORM = android-16 이상일 때 기본값)
  • OFF(ANDROID_PLATFORM = android-15 이하일 때 기본값)

위치 독립 실행 파일(PIE)을 사용할지 지정합니다. Android의 동적 링커는 Android 4.1(API 레벨 16) 이상에서 PIE를 지원합니다.

ANDROID_CPP_FEATURES

이 변수는 비어있는 것이 기본값입니다. 다만 다음은 전달할 수 있는 인수의 몇 가지 예를 제시한 것입니다.

  • rtti(코드가 RTTI를 사용함을 표시)
  • exceptions(코드가 C++ 예외를 사용함을 표시)

RTTI(런타임 유형 정보) 및 C++ 예외와 같은 네이티브 라이브러리를 컴파일링할 때 CMake가 사용해야 하는 특정 C++ 기능을 지정합니다.

ANDROID_ALLOW_UNDEFINED_SYMBOLS
  • TRUE
  • FALSE(기본값)

CMake가 네이티브 라이브러리 빌드 중 정의되지 않은 참조에 직면했을 때 정의되지 않은 기호 오류를 발생시킬지 지정합니다. 이러한 유형의 오류를 사용하지 않으려면 이 변수를 TRUE로 설정합니다.

ANDROID_ARM_MODE
  • arm
  • thumb(기본값)

arm 또는 thumb 모드의 ARM 대상 바이너리 생성 여부를 지정합니다. thumb 모드에서는 각 명령이 16비트 길이이며 thumb/ 디렉토리의 STL 라이브러리와 연결됩니다. arm을 전달하면 CMake가 32비트 arm 모드로 라이브러리의 객체 파일을 생성하도록 명령합니다.

ANDROID_ARM_NEON
  • TRUE
  • FALSE

CMake가 NEON 지원으로 네이티브 라이브러리를 빌드해야 할지 지정합니다. 기본적으로 API 레벨 23 이상에 대해 참이며, 그 외의 경우는 거짓입니다.

ANDROID_DISABLE_FORMAT_STRING_CHECKS
  • TRUE
  • FALSE(기본값)

소스 코드를 형식 문자열로 컴파일할지 지정합니다. 사용 설정하면 상수가 아닌 형식 문자열이 printf 형식 함수에 사용되었을 때 컴파일러가 오류를 발생시킵니다.

CMake 빌드 명령어의 이해

Cmake 빌드 문제를 디버깅할 때, Android에 대한 크로스 컴파일 시 Android Studio가 사용하는 특정 빌드 인수를 알면 도움이 됩니다.

Android Studio는 CMake 빌드 실행을 위해 사용하는 빌드 인수를 cmake_build_command.txt 파일에 저장합니다. 앱이 대상으로 하는 각 애플리케이션 바이너리 인터페이스(ABI) 및 이러한 ABI에 대한 각 빌드 유형(예: release 또는 debug)의 경우, Android Studio가 특정 구성에 대한 cmake_build_command.txt 파일의 사본을 생성합니다. Android Studio는 그 후 생성한 파일을 다음 디렉토리에 배치합니다.

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

팁: Android Studio에서 단축키 검색(shift+shift)을 사용하고 입력 필드에 cmake_build_command.txt를 입력하여 이 파일을 빠르게 볼 수 있습니다.

다음 스니펫은 armeabi-v7a 아키텍처를 대상으로 하는 디버깅 가능한 hello-jni 릴리스 샘플을 빌드하는 CMake 인수의 예를 나타냅니다.

Executable : /usr/local/google/home/{$USER}/Android/Sdk/cmake/3.6.3155560/bin/cmake
arguments :
-H/usr/local/google/home/{$USER}/Dev/github-projects/googlesamples/android-ndk/hello-jni/app/src/main/cpp
-B/usr/local/google/home/{$USER}/Dev/github-projects/googlesamples/android-ndk/hello-jni/app/.externalNativeBuild/cmake/arm7Debug/armeabi-v7a
-GAndroid Gradle - Ninja
-DANDROID_ABI=armeabi-v7a
-DANDROID_NDK=/usr/local/google/home/{$USER}/Android/Sdk/ndk-bundle
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=/usr/local/google/home/{$USER}/Dev/github-projects/googlesamples/android-ndk/hello-jni/app/build/intermediates/cmake/arm7/debug/obj/armeabi-v7a
-DCMAKE_BUILD_TYPE=Debug
-DCMAKE_MAKE_PROGRAM=/usr/local/google/home/{$USER}/Android/Sdk/cmake/3.6.3155560/bin/ninja
-DCMAKE_TOOLCHAIN_FILE=/usr/local/google/home/{$USER}/Android/Sdk/ndk-bundle/build/cmake/android.toolchain.cmake
-DANDROID_NATIVE_API_LEVEL=23
-DANDROID_TOOLCHAIN=clang
jvmArgs : 

빌드 인수

다음 표는 Android용 주요 CMake 빌드 인수를 강조표시한 것입니다. 이러한 빌드 인수는 개발자가 설정하는 것이 아닙니다. 그 대신, Gradle용 Android 플러그인이 프로젝트의 build.gradle 구성에 따라 이러한 인수를 설정합니다.

빌드 인수 설명
-G <build-system>

CMake가 생성하는 빌드 파일의 유형입니다.

네이티브 코드가 있는 Android Studio의 프로젝트에서 <build-system>Android Gradle - Ninja로 설정됩니다. 이 설정은 CMake이 앱에 대한 C/C++ 소스를 컴파일 및 링크하기 위해 ninja 빌드 시스템을 사용한다는 것을 나타냅니다. 또한, CMake은 android_gradle_build.json 파일을 생성합니다. 이 파일은 컴파일러 플래그 및 대상 이름 등의 CMake 빌드에 관한 Gradle 플러그인의 메타 데이터를 포함합니다.

이 설정은 CMake이 앱에 대한 C/C++ 소스를 컴파일 및 링크하기 위해 Gradle과 함께 ninja 빌드 시스템을 사용한다는 것을 나타냅니다. Ninja 빌드 시스템은 스튜디오가 지원하는 유일한 생성기입니다.

-DANDROID_ABI <abi>

대상 ABI입니다.

NDK는 ABI 관리에서 설명된 바와 같이 ABI 집합을 지원합니다. 이 옵션은 ndk-build 도구가 사용하는 APP_ABI 변수와 유사합니다.

기본적으로 Gradle은 네이티브 라이브러리를 NDK가 지원하는 별도의 ABI용 .so 파일로 빌드하고 전체를 APK로 패키징합니다. Gradle이 특정 ABI 구성에 대해서만 빌드하도록 하려면 C 및 C++ 코드를 프로젝트에 추가의 지침을 따라야 합니다.

대상 ABI가 지정되지 않으면 ABI는 armeabi-v7a를 기본으로 사용합니다.

유효한 대상 이름:

  • armeabi: 소프트웨어 부동 소수점 연산을 포함한 ARMv5TE 기반 CPU입니다.
  • armeabi-v7a: 하드웨어 FPU 명령을 포함한 ARMv7 기반 기기(VFPv3_D16)입니다.
  • armeabi-v7a with NEON: armeabi-v7a와 동일하지만 NEON 부동 소수점 명령을 사용 설정합니다. -DANDROID_ABI=armeabi-v7a-DANDROID_ARM_NEON=ON 설정과 동일합니다.
  • arm64-v8a: ARMv8 AArch64 명령 집합입니다.
  • x86: IA-32 명령 집합입니다.
  • x86_64 - x86-64 아키텍처에 대한 명령 집합입니다.
-DANDROID_NDK <path> 호스트의 NDK 설치 루트 디렉토리로 가는 절대 경로입니다.
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY <path> 빌드되었을 때 CMake가 LIBRARY 대상 파일을 배치하는 호스트에서의 위치입니다.
-DCMAKE_BUILD_TYPE <type> ndk-build 도구의 빌드 유형과 유사합니다. 유효한 값은 ReleaseDebug입니다. 디버깅을 간소화하기 위해, CMake는 릴리스 또는 디버그 버전을 빌드의 일부분으로 삭제하지 않습니다. 다만 Gradle은 바이너리를 APK에 패키징할 때 바이너리를 삭제합니다.
-DCMAKE_MAKE_PROGRAM <program-name> 네이티브 빌드 시스템 실행용 도구 Gradle 플러그인은 Android SDK와 번들링된 CMake ninja 생성기에 이 값을 설정합니다.
-DCMAKE_TOOLCHAIN_FILE <path> CMake가 Android용 크로스 컴파일에 사용하는 android.toolchain.cmake 파일 경로입니다. 일반적으로 이 파일은 $NDK가 호스트의 NDK 설치 디렉토리일 때 $NDK/build/cmake/에 위치합니다. 툴체인 파일에 관한 더 많은 정보는 Android용 크로스 컴파일을 참조하세요.
-DANDROID_NATIVE_API_LEVEL <level> CMake가 컴파일하는 Android API 레벨입니다.
-DANDROID_TOOLCHAIN <type> CMake가 사용하는 컴파일러 툴체인입니다. 기본값은 clang입니다.

CMake의 YASM 지원

NDK는 x86 및 x86-64 아키텍처에서 실행하기 위해 YASM으로 작성된 어셈블리 코드 빌드에 대한 CMake 지원을 제공합니다. YASM은 NASM을 기반으로 한 x86 및 x86-64 아키텍처용 오픈소스 어셈블러입니다.

어셈블리 코드에서 C 라이브러리나 함수에 접근하기 위해 어셈블리 언어 프로그램이나 루틴을 C 코드에 연결할 때 유용합니다. 또한 컴파일된 C 코드에 짧은 어셈블리 루틴을 포함하면 어셈블리 코드가 감당할 수 있는, 더 나은 기계 성능을 활용할 수 있습니다.

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

  1. enable_languageASM_NASM으로 설정된 값과 함께 호출합니다.
  2. 빌드 중인 것이 공유 라이브러리 또는 실행 바이너리 중 무엇인지에 따라 add_library 또는 add_executable을 호출합니다. 이 인수에서 YASM의 어셈블리 프로그램에 대한 .asm 파일과 관련 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 코드를 참조하세요.

문제 보고

CMake 오픈소스 버전으로 인한 것이 아닌 다른 문제에 직면한 경우, GitHub의 android-ndk/ndk 이슈 트래커를 통해 보고하세요.