Bazel을 처음 사용하는 경우 Bazel로 Android 빌드 튜토리얼로 시작하세요.
개요
Bazel은 Android Native Development Kit (NDK) 도구 모음을 사용하는 여러 구성을 비롯한 다양한 빌드 구성에서 실행할 수 있습니다. 즉, 일반 cc_library
및 cc_binary
규칙을 Bazel 내에서 직접 Android용으로 컴파일할 수 있습니다. Bazel은 android_ndk_repository
저장소 규칙과 관련 bzlmod 확장 프로그램을 사용하여 이를 달성합니다.
일반 Android 컴파일의 경우 rules_android
를 사용합니다.
이 튜토리얼에서는 C++ 라이브러리 종속 항목을 Android 앱에 통합하는 방법을 보여주고 NDK 도구 모음 검색 및 등록에 rules_android_ndk
를 사용합니다.
기본 요건
Android SDK 및 NDK가 설치되어 있는지 확인하세요.
NDK 및 SDK 설정
외부 저장소 설정은 WORKSPACE를 사용하는지 아니면 bzlmod (MODULE.bazel)를 사용하는지에 따라 다릅니다. Bzlmod는 Bazel 7 이상에 권장되는 솔루션입니다. MODULE.bazel 및 WORKSPACE 설정 스탠자는 서로 독립적입니다. 하나의 종속 항목 관리 솔루션을 사용하는 경우 다른 솔루션의 상용구를 추가할 필요가 없습니다.
Bzlmod MODULE.bazel 설정
MODULE.bazel에 다음 스니펫을 추가합니다.
# NDK
bazel_dep(name = "rules_android_ndk", version = "0.1.3")
android_ndk_repository_extension = use_extension("@rules_android_ndk//:extension.bzl", "android_ndk_repository_extension")
use_repo(android_ndk_repository_extension, "androidndk")
register_toolchains("@androidndk//:all")
# SDK
bazel_dep(name = "rules_android", version = "0.6.6")
register_toolchains(
"@rules_android//toolchains/android:android_default_toolchain",
"@rules_android//toolchains/android_sdk:android_sdk_tools",
)
android_sdk_repository_extension = use_extension("@rules_android//rules/android_sdk_repository:rule.bzl", "android_sdk_repository_extension")
use_repo(android_sdk_repository_extension, "androidsdk")
register_toolchains("@androidsdk//:sdk-toolchain", "@androidsdk//:all")
기존 WORKSPACE 설정
WORKSPACE
에 다음 스니펫을 추가합니다.
load("@rules_android//rules:rules.bzl", "android_sdk_repository")
android_sdk_repository(
name = "androidsdk", # Required. Name *must* be "androidsdk".
path = "/path/to/sdk", # Optional. Can be omitted if `ANDROID_HOME` environment variable is set.
)
load("@rules_android_ndk//:rules.bzl", "android_ndk_repository")
android_ndk_repository(
name = "androidndk", # Required. Name *must* be "androidndk".
path = "/path/to/ndk", # Optional. Can be omitted if `ANDROID_NDK_HOME` environment variable is set.
)
WORKSPACE 호환성 정보:
rules_android
및rules_android_ndk
규칙에는 위의 작업공간 스니펫에 표시되지 않은 추가 상용구가 필요합니다. 최신 상태의 완전한 인스턴스화 스탠자는rules_android_ndk
의 기본 예시 앱의 WORKSPACE 파일을 참고하세요.
android_ndk_repository
규칙에 관한 자세한 내용은 docstring을 참고하세요.
빠른 시작
Android용 C++을 빌드하려면 cc_library
종속 항목을 android_binary
또는 android_library
규칙에 추가하면 됩니다.
예를 들어 Android 앱의 다음 BUILD
파일이 있다고 가정해 보겠습니다.
# In <project>/app/src/main/BUILD.bazel
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_android//rules:rules.bzl", "android_binary", "android_library")
cc_library(
name = "jni_lib",
srcs = ["cpp/native-lib.cpp"],
)
android_library(
name = "lib",
srcs = ["java/com/example/android/bazel/MainActivity.java"],
resource_files = glob(["res/**/*"]),
custom_package = "com.example.android.bazel",
manifest = "LibraryManifest.xml",
deps = [":jni_lib"],
)
android_binary(
name = "app",
deps = [":lib"],
manifest = "AndroidManifest.xml",
)
이 BUILD
파일은 다음 타겟 그래프를 생성합니다.
그림 1. cc_library 종속 항목이 있는 Android 프로젝트의 그래프를 빌드합니다.
앱을 빌드하려면 다음을 실행하면 됩니다.
bazel build //app/src/main:app --android_platforms=<your platform>
--android_platforms
를 지정하지 않으면 JNI 헤더 누락에 관한 오류로 인해 빌드가 실패합니다.
bazel build
명령어는 Java 파일, Android 리소스 파일, cc_library
규칙을 컴파일하고 모든 것을 APK로 패키징합니다.
$ zipinfo -1 bazel-bin/app/src/main/app.apk
nativedeps
lib/armeabi-v7a/libapp.so
classes.dex
AndroidManifest.xml
...
res/...
...
META-INF/CERT.SF
META-INF/CERT.RSA
META-INF/MANIFEST.MF
Bazel은 모든 cc_libraries를 --android_platforms
로 지정된 아키텍처를 타겟팅하는 단일 공유 객체 (.so
) 파일로 컴파일합니다.
자세한 내용은 타겟 ABI 구성 섹션을 참고하세요.
설정 예시
이 예시는 Bazel 예시 저장소에서 확인할 수 있습니다.
BUILD.bazel
파일에서 android_binary
, android_library
, cc_library
규칙을 사용하여 세 개의 타겟이 정의됩니다.
android_binary
최상위 타겟은 APK를 빌드합니다.
cc_library
타겟에는 JNI 함수 구현이 포함된 단일 C++ 소스 파일이 포함됩니다.
#include <jni.h>
#include <string>
extern "C"
JNIEXPORT jstring
JNICALL
Java_com_example_android_bazel_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
android_library
타겟은 Java 소스, 리소스 파일, cc_library
타겟의 종속 항목을 지정합니다. 이 예에서 MainActivity.java
는 공유 객체 파일 libapp.so
을 로드하고 JNI 함수의 메서드 서명을 정의합니다.
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("app");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
}
public native String stringFromJNI();
}
타겟 ABI 구성
타겟 ABI를 구성하려면 다음과 같이 --android_platforms
플래그를 사용하세요.
bazel build //:app --android_platforms=comma-separated list of platforms
--platforms
플래그와 마찬가지로 --android_platforms
에 전달되는 값은 표준 제약 조건 값을 사용하여 기기를 설명하는 platform
타겟의 라벨입니다.
예를 들어 64비트 ARM 프로세서가 있는 Android 기기의 경우 다음과 같이 플랫폼을 정의합니다.
platform(
name = "android_arm64",
constraint_values = [
"@platforms//os:android",
"@platforms//cpu:arm64",
],
)
모든 Android platform
은 @platforms//os:android
OS 제약 조건을 사용해야 합니다. CPU 제약 조건을 이전하려면 다음 차트를 확인하세요.
CPU 값 | 플랫폼 |
---|---|
armeabi-v7a |
@platforms//cpu:armv7 |
arm64-v8a |
@platforms//cpu:arm64 |
x86 |
@platforms//cpu:x86_32 |
x86_64 |
@platforms//cpu:x86_64 |
물론 다중 아키텍처 APK의 경우 --android_platforms=//:arm64,//:x86_64
와 같은 여러 라벨을 전달합니다 (최상위 BUILD.bazel
파일에 정의했다고 가정).
Bazel은 기본 Android 플랫폼을 선택할 수 없으므로 --android_platforms
로 정의하고 지정해야 합니다.
NDK 버전 및 Android API 수준에 따라 다음 ABI를 사용할 수 있습니다.
NDK 버전 | ABI |
---|---|
16 이하 | armeabi, armeabi-v7a, arm64-v8a, mips, mips64, x86, x86_64 |
17 이상 | armeabi-v7a, arm64-v8a, x86, x86_64 |
이러한 ABI에 관한 자세한 내용은 NDK 문서를 참고하세요.
다중 ABI fat APK는 APK 크기를 늘리므로 출시 빌드에는 권장되지 않지만 개발 및 QA 빌드에는 유용할 수 있습니다.
C++ 표준 선택
다음 플래그를 사용하여 C++ 표준에 따라 빌드합니다.
C++ 표준 | 플래그 |
---|---|
C++98 | 기본값, 플래그가 필요하지 않음 |
C++11 | --cxxopt=-std=c++11 |
C++14 | --cxxopt=-std=c++14 |
C++17 | --cxxopt=-std=c++17 |
예를 들면 다음과 같습니다.
bazel build //:app --cxxopt=-std=c++11
사용자 설명서에서 --cxxopt
, --copt
, --linkopt
를 사용하여 컴파일러 및 링커 플래그를 전달하는 방법을 자세히 알아보세요.
컴파일러 및 링커 플래그는 copts
및 linkopts
를 사용하여 cc_library
의 속성으로 지정할 수도 있습니다. 예를 들면 다음과 같습니다.
cc_library(
name = "jni_lib",
srcs = ["cpp/native-lib.cpp"],
copts = ["-std=c++11"],
linkopts = ["-ldl"], # link against libdl
)
android_binary
을 사용하지 않고 Android용 cc_library
빌드
android_binary
를 사용하지 않고 Android용 독립형 cc_binary
또는 cc_library
를 빌드하려면 --platforms
플래그를 사용합니다.
예를 들어 my/platforms/BUILD
에 Android 플랫폼을 정의했다고 가정합니다.
bazel build //my/cc/jni:target \
--platforms=//my/platforms:x86_64
이 접근 방식을 사용하면 전체 빌드 트리가 영향을 받습니다.
이러한 플래그는 project/.bazelrc
에서 bazelrc
구성 (각 ABI에 하나씩)에 넣을 수 있습니다.
common:android_x86 --platforms=//my/platforms:x86
common:android_armeabi-v7a --platforms=//my/platforms:armeabi-v7a
# In general
common:android_<abi> --platforms=//my/platforms:<abi>
그런 다음 x86
의 cc_library
를 빌드하려면 다음을 실행합니다.
bazel build //my/cc/jni:target --config=android_x86
일반적으로 하위 수준 타겟 (예: cc_library
)의 경우 또는 빌드하는 항목을 정확히 아는 경우 이 메서드를 사용합니다. 제어하지 않는 타겟을 많이 빌드할 것으로 예상되는 상위 수준 타겟의 경우 android_binary
의 자동 구성 전환을 사용합니다.