Ninja를 사용하여 맞춤 C/C++ 빌드 시스템 통합(실험용)

CMake 또는 ndk-build를 사용하지 않고 Android Gradle 플러그인(AGP) C/C++ 빌드와 Android 스튜디오를 완전히 통합하려면 Ninja 빌드 파일 형식으로 빌드 정보를 작성하는 셸 스크립트를 만들어서 맞춤 C/C++ 빌드 시스템을 생성하면 됩니다.

맞춤 C/C++ 빌드 시스템의 실험적 지원 기능이 Android 스튜디오 및 AGP에 추가되었습니다. 이 기능은 Android 스튜디오 Dolphin | 2021.3.1 Canary 4부터 사용할 수 있습니다.

개요

C/C++ 프로젝트(특히 여러 플랫폼을 타겟팅하는 프로젝트)의 일반적인 패턴은 일부 기본 표현에서 플랫폼별로 프로젝트를 생성하는 것입니다. 이러한 패턴의 대표적인 예는 CMake입니다. CMake는 CMakeLists.txt 파일에 저장된 단일 기본 표현으로 Android, iOS, 기타 플랫폼용 프로젝트를 생성할 수 있습니다.

CMake는 AGP에서 직접 지원됩니다. 다음과 같이 직접 지원되지 않는 다른 프로젝트 생성기를 사용할 수도 있습니다.

이러한 유형의 프로젝트 생성기는 Ninja를 C/C++ 빌드의 백엔드 표현으로 지원하거나 Ninja를 백엔드 표현으로 생성하도록 조정할 수 있습니다.

통합 C/C++ 프로젝트 시스템 생성기가 포함된 AGP 프로젝트를 올바르게 구성하면 사용자가 다음을 할 수 있게 됩니다.

  • 명령줄 및 Android 스튜디오에서 빌드합니다.

  • Android 스튜디오에서 전체 언어 서비스 지원(예: 정의로 이동)을 사용하여 소스를 수정합니다.

  • Android 스튜디오 디버거를 사용하여 네이티브 및 혼합 프로세스를 디버그합니다.

맞춤 C/C++ 빌드 구성 스크립트를 사용하도록 빌드를 수정하는 방법

이 섹션에서는 AGP에서 맞춤 C/C++ 빌드 구성 스크립트를 사용하기 위한 단계를 안내합니다.

1단계: 구성 스크립트를 참조하도록 모듈 수준 build.gradle 파일 수정하기

AGP에서 Ninja 지원을 사용 설정하려면 모듈 수준 build.gradle 파일에서 experimentalProperties를 구성합니다.

android {
  defaultConfig {
    externalNativeBuild {
      experimentalProperties["ninja.abiFilters"] = [ "x86", "arm64-v8a" ]
      experimentalProperties["ninja.path"] = "source-file-list.txt"
      experimentalProperties["ninja.configure"] = "configure-ninja"
      experimentalProperties["ninja.arguments"] = [
            "\${ndk.moduleMakeFile}",
            "--variant=\${ndk.variantName}",
            "--abi=Android-\${ndk.abi}",
            "--configuration-dir=\${ndk.configurationDir}",
            "--ndk-version=\${ndk.moduleNdkVersion}",
            "--min-sdk-version=\${ndk.minSdkVersion}"
       ]
     }
   }

속성은 AGP에서 다음과 같이 해석됩니다.

  • ninja.abiFilters는 빌드할 ABI 목록입니다. 유효한 값은 x86, x86-64, armeabi-v7a, arm64-v8a입니다.

  • ninja.path는 C/C++ 프로젝트 파일의 경로입니다. 파일의 형식은 원하는 대로 지정할 수 있습니다. 이 파일을 변경하면 Android 스튜디오에서 Gradle 동기화 메시지가 트리거됩니다.

  • ninja.configure는 C/C++ 프로젝트를 구성해야 할 때 Gradle에서 실행할 스크립트 파일의 경로입니다. 프로젝트는 첫 번째 빌드(Android 스튜디오에서 Gradle 동기화 중)에서 구성되거나 구성 스크립트 입력 중 하나가 변경될 때 구성됩니다.

  • ninja.arguments는 ninja.configure로 정의된 스크립트에 전달되는 인수 목록입니다. 이 목록의 요소는 AGP의 현재 구성 컨텍스트에 따라 값이 달라지는 매크로 집합을 참조할 수 있습니다.

    • ${ndk.moduleMakeFile}ninja.configure 파일의 전체 경로입니다. 이 예에서는 C:\path\to\configure-ninja.bat입니다.

    • ${ndk.variantName}은 빌드 중인 현재 AGP 변형의 이름입니다. 예를 들어 debug 또는 release입니다.

    • ${ndk.abi}는 빌드 중인 현재 AGP ABI의 이름입니다. 예를 들어 x86 또는 arm64-v8a입니다.

    • ${ndk.buildRoot}는 스크립트가 출력물을 기록하는 폴더 이름으로 AGP에서 생성합니다. 자세한 내용은 2단계: 구성 스크립트 만들기에서 설명합니다.

    • ${ndk.ndkVersion}은 사용할 NDK 버전입니다. 일반적으로 build.gradle 파일의 android.ndkVersion에 전달된 값이거나 기본값입니다(전달된 값이 없는 경우).

    • ${ndk.minPlatform}은 AGP에서 요청하는 최소 타겟 Android 플랫폼입니다.

  • ninja.targets는 빌드해야 하는 특정 Ninja 타겟의 목록입니다.

2단계: 구성 스크립트 만들기

Ninja로 빌드할 때 프로젝트의 모든 네이티브 출력을 컴파일하고 연결하는 build.ninja 파일을 생성하는 것이 구성 스크립트(이전 예에서 configure-ninja.bat)의 최소한의 역할입니다. 일반적으로 .o(객체), .a(보관 파일), .so(공유 객체) 파일이 여기에 해당됩니다.

구성 스크립트는 필요에 따라 두 개의 다른 위치에 build.ninja 파일을 쓸 수 있습니다.

  • AGP에서 위치를 선택해도 괜찮다면 구성 스크립트는 ${ndk.buildRoot} 매크로에 설정된 위치에 build.ninja를 작성합니다.

  • 구성 스크립트에서 build.ninja 파일의 위치를 선택해야 하는 경우 ${ndk.buildRoot} 매크로에 설정된 위치에 build.ninja.txt라는 파일도 작성합니다. 이 파일에는 구성 스크립트가 작성한 build.ninja 파일의 전체 경로가 포함됩니다.

build.ninja 파일의 구조

일반적으로 Android C/C++ 빌드를 정확하게 나타내는 구조는 대부분 작동합니다. AGP 및 Android 스튜디오에 필요한 핵심 요소는 다음과 같습니다.

  • C/C++ 소스 파일과 이를 컴파일하기 위해 Clang에 필요한 플래그 목록

  • 출력 라이브러리 목록. 일반적으로 .so(공유 객체) 파일이지만 .a(보관 파일) 또는 실행 파일(확장자 없음)일 수도 있습니다.

build.ninja 파일을 생성하는 방법의 예가 필요하면 build.ninja 생성기를 사용할 때 CMake의 출력을 확인하세요.

다음은 최소 build.ninja 템플릿의 예입니다.

rule COMPILE
   command = /path/to/ndk/clang -c $in -o $out {other flags}
rule LINK
   command = /path/to/ndk/clang $in -o $out {other flags}

build source.o : COMPILE source.cpp
build lib.so : LINK source.o

권장사항

다음은 요구사항(소스 파일 및 출력 라이브러리 목록) 외에 권장되는 몇 가지 사항입니다.

phony 규칙으로 이름이 지정된 출력 선언

가능하면 build.ninja 구조체에서 phony 규칙을 사용하여 사람이 읽을 수 있는 빌드 출력 이름을 제공하는 것이 좋습니다. 예를 들어 c:/path/to/lib.so라는 출력이 있으면 다음과 같이 사람이 읽을 수 있는 이름을 지정할 수 있습니다.

build curl: phony /path/to/lib.so

이렇게 하면 build.gradle 파일에서 이 이름을 빌드 타겟으로 지정할 수 있습니다. 예:

android {
  defaultConfig {
    externalNativeBuild {
      ...
      experimentalProperties["ninja.targets"] = [ "curl" ]

'all' 타겟 지정

all 타겟을 지정하면 build.gradle 파일에 명시적으로 지정된 타겟이 없는 경우 이것이 AGP에서 빌드한 기본 라이브러리 집합이 됩니다.

rule COMPILE
   command = /path/to/ndk/clang $in -o $out {other flags}
rule LINK
   command = /path/to/ndk/clang $in -o $out {other flags}

build foo.o : COMPILE foo.cpp
build bar.o : COMPILE bar.cpp
build libfoo.so : LINK foo.o
build libbar.so : LINK bar.o
build all: phony libfoo.so libbar.so

대체 빌드 메서드 지정(선택사항)

더 고급 사용 사례는 Ninja 기반이 아닌 기존 빌드 시스템을 래핑하는 것입니다. 이 경우에도 Android 스튜디오에서 자동 완성 및 정의로 이동과 같은 적절한 언어 서비스 기능을 제공할 수 있도록 모든 소스와 플래그 및 출력 라이브러리를 표현해야 합니다. 그러나 AGP에서 실제 빌드 중에 기본 빌드 시스템으로 지연해야 하는 경우도 있습니다.

이 경우 Ninja 빌드 출력에 특정 확장자 .passthrough를 사용하면 됩니다.

좀 더 구체적인 예로, MSBuild를 래핑한다고 가정해 보겠습니다. 구성 스크립트는 평상시와 같이 build.ninja를 생성하지만, AGP에서 MSBuild를 호출하는 방법을 정의하는 패스 스루 타겟도 추가합니다.

rule COMPILE
   command = /path/to/ndk/clang $in -o $out {other flags}
rule LINK
   command = /path/to/ndk/clang $in -o $out {other flags}

rule MBSUILD_CURL
  command = /path/to/msbuild {flags to build curl with MSBuild}

build source.o : COMPILE source.cpp
build lib.so : LINK source.o
build curl : phony lib.so
build curl.passthrough : MBSUILD_CURL

의견 보내기

이 기능은 실험적 기능이므로 의견을 보내주시면 큰 도움이 됩니다. 다음 채널을 통해 의견을 보낼 수 있습니다.

  • 일반적인 의견을 보내려면 이 버그에 댓글을 추가하세요.

  • 버그를 신고하려면 Android 스튜디오를 열고 Help > Submit Feedback을 클릭합니다. 해당 버그를 찾을 수 있도록 '맞춤 C/C++ 빌드 시스템'을 언급해 주세요.

  • Android 스튜디오를 설치하지 않은 경우에는 이 템플릿을 사용하여 버그를 신고하세요.