Nếu bạn mới sử dụng Bazel, vui lòng bắt đầu bằng hướng dẫn Tạo ứng dụng Android bằng Bazel.
Tổng quan
Bazel có thể chạy trong nhiều cấu hình bản dựng khác nhau, bao gồm một số cấu hình sử dụng chuỗi công cụ Bộ công cụ phát triển mã gốc của Android (NDK). Điều này có nghĩa là các quy tắc cc_library và cc_binary thông thường có thể được biên dịch cho Android trực tiếp trong Bazel. Bazel thực hiện việc này bằng cách sử dụng quy tắc kho lưu trữ android_ndk_repository và tiện ích bzlmod liên quan.
Để biên dịch Android
chung, hãy sử dụng rules_android.
Hướng dẫn này minh hoạ cách tích hợp các phần phụ thuộc thư viện C++ vào
ứng dụng Android và sử dụng
rules_android_ndk để phát hiện và đăng ký chuỗi công cụ NDK.
Điều kiện tiên quyết
Vui lòng đảm bảo bạn đã cài đặt SDK Android và NDK.
Thiết lập NDK và SDK
Việc thiết lập kho lưu trữ bên ngoài sẽ khác nhau tuỳ thuộc vào việc bạn đang sử dụng WORKSPACE hay bzlmod (MODULE.bazel). Bzlmod là giải pháp ưu tiên cho Bazel 7+. Xin lưu ý rằng các đoạn mã thiết lập MODULE.bazel và WORKSPACE độc lập với nhau. Nếu đang sử dụng một giải pháp quản lý phần phụ thuộc, bạn không cần thêm mã soạn sẵn cho giải pháp còn lại.
Thiết lập MODULE.bazel của Bzlmod
Thêm đoạn mã sau vào 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")
Thiết lập WORKSPACE cũ
Thêm đoạn mã sau vào 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.
)
Ghi chú về khả năng tương thích cho WORKSPACE:
- Cả
rules_androidvàrules_android_ndkquy tắc đều yêu cầu thêm mã soạn sẵn không được mô tả trong đoạn mã WORKSPACE ở trên. Để biết đoạn mã thực thể hoá đầy đủ và mới nhất, hãy xem tệp WORKSPACE của ứng dụng ví dụ cơ bản củarules_android_ndk's.
Để biết thêm thông tin về quy tắc android_ndk_repository, hãy xem
chuỗi tài liệu của quy tắc này.
Bắt đầu nhanh
Để tạo C++ cho Android, bạn chỉ cần thêm các phần phụ thuộc cc_library vào quy tắc android_binary hoặc android_library.
Ví dụ: cho tệp BUILD sau đây cho một ứng dụng Android:
# 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",
)
Tệp BUILD này tạo ra biểu đồ mục tiêu sau:

Hình 1. Tạo đồ thị của dự án Android với các phần phụ thuộc cc_library.
Để tạo ứng dụng, bạn chỉ cần chạy:
bazel build //app/src/main:app --android_platforms=<your platform>Xin lưu ý rằng nếu bạn không chỉ định --android_platforms, bản dựng sẽ không thành công do lỗi về các tiêu đề JNI bị thiếu.
Lệnh bazel build biên dịch các tệp Java, tệp tài nguyên Android và quy tắc cc_library, đồng thời đóng gói mọi thứ vào một tệp 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.MFBazel biên dịch tất cả cc_libraries thành một tệp đối tượng dùng chung (.so), nhắm đến các kiến trúc được chỉ định bởi --android_platforms.
Hãy xem phần về cách định cấu hình ABI mục tiêu để
biết thêm thông tin.
Thiết lập mẫu
Ví dụ này có trong kho lưu trữ ví dụ của Bazel.
Trong tệp BUILD.bazel, 3 mục tiêu được xác định bằng quy tắc android_binary, android_library và cc_library.
Mục tiêu cấp cao nhất android_binary tạo APK.
Mục tiêu cc_library chứa một tệp nguồn C++ duy nhất với việc triển khai hàm JNI:
#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());
}
Mục tiêu android_library chỉ định các nguồn Java, tệp tài nguyên và phần phụ thuộc vào mục tiêu cc_library. Đối với ví dụ này, MainActivity.java tải tệp đối tượng dùng chung libapp.so và xác định chữ ký phương thức cho hàm JNI:
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("app");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
}
public native String stringFromJNI();
}
Định cấu hình ABI mục tiêu
Để định cấu hình ABI mục tiêu, hãy sử dụng cờ --android_platforms như sau:
bazel build //:app --android_platforms=comma-separated list of platformsGiống như cờ --platforms, các giá trị được truyền đến --android_platforms là nhãn của các mục tiêu platform, sử dụng các giá trị ràng buộc tiêu chuẩn để mô tả thiết bị của bạn.
Ví dụ: đối với một thiết bị Android có bộ xử lý ARM 64 bit, bạn sẽ xác định nền tảng của mình như sau:
platform(
name = "android_arm64",
constraint_values = [
"@platforms//os:android",
"@platforms//cpu:arm64",
],
)
Mọi Android platform đều phải sử dụng ràng buộc hệ điều hành @platforms//os:android. Để di chuyển ràng buộc CPU, hãy kiểm tra biểu đồ này:
| Giá trị CPU | Nền tảng |
|---|---|
armeabi-v7a |
@platforms//cpu:armv7 |
arm64-v8a |
@platforms//cpu:arm64 |
x86 |
@platforms//cpu:x86_32 |
x86_64 |
@platforms//cpu:x86_64 |
Và tất nhiên, đối với APK đa kiến trúc, bạn sẽ truyền nhiều nhãn, ví dụ: --android_platforms=//:arm64,//:x86_64 (giả sử bạn đã xác định các nhãn đó trong tệp BUILD.bazel cấp cao nhất).
Bazel không thể chọn nền tảng Android mặc định, vì vậy, bạn phải xác định và chỉ định một nền tảng bằng --android_platforms.
Tuỳ thuộc vào bản sửa đổi NDK và cấp độ API Android, các ABI sau đây sẽ có sẵn:
| Bản sửa đổi NDK | ABI |
|---|---|
| 16 trở xuống | armeabi, armeabi-v7a, arm64-v8a, mips, mips64, x86, x86_64 |
| 17 trở lên | armeabi-v7a, arm64-v8a, x86, x86_64 |
Hãy xem tài liệu NDK để biết thêm thông tin về các ABI này.
Bạn không nên sử dụng APK Fat đa ABI cho các bản dựng phát hành vì chúng làm tăng kích thước của APK, nhưng có thể hữu ích cho các bản dựng phát triển và QA.
Chọn một tiêu chuẩn C++
Sử dụng các cờ sau để tạo theo tiêu chuẩn C++:
| Tiêu chuẩn C++ | Cờ |
|---|---|
| C++98 | Mặc định, không cần cờ |
| C++11 | --cxxopt=-std=c++11 |
| C++14 | --cxxopt=-std=c++14 |
| C++17 | --cxxopt=-std=c++17 |
Ví dụ:
bazel build //:app --cxxopt=-std=c++11Đọc thêm về cách truyền cờ trình biên dịch và trình liên kết bằng --cxxopt, --copt và
--linkopt trong Hướng dẫn sử dụng.
Bạn cũng có thể chỉ định cờ trình biên dịch và trình liên kết dưới dạng thuộc tính trong cc_library bằng cách sử dụng copts và linkopts. Ví dụ:
cc_library(
name = "jni_lib",
srcs = ["cpp/native-lib.cpp"],
copts = ["-std=c++11"],
linkopts = ["-ldl"], # link against libdl
)
Tạo cc_library cho Android mà không cần sử dụng android_binary
Để tạo cc_binary hoặc cc_library độc lập cho Android mà không cần sử dụng android_binary, hãy sử dụng cờ --platforms.
Ví dụ: giả sử bạn đã xác định các nền tảng Android trong my/platforms/BUILD:
bazel build //my/cc/jni:target \
--platforms=//my/platforms:x86_64Với phương pháp này, toàn bộ cây bản dựng sẽ bị ảnh hưởng.
Bạn có thể đặt các cờ này vào cấu hình bazelrc (một cờ cho mỗi ABI), trong
project/.bazelrc:
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>
Sau đó, để tạo cc_library cho x86, ví dụ: hãy chạy:
bazel build //my/cc/jni:target --config=android_x86Nói chung, hãy sử dụng phương thức này cho các mục tiêu cấp thấp (như cc_library) hoặc khi bạn biết chính xác những gì mình đang tạo; dựa vào các quá trình chuyển đổi cấu hình tự động từ android_binary cho các mục tiêu cấp cao, nơi bạn dự kiến sẽ tạo nhiều mục tiêu mà bạn không kiểm soát.