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, 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 thông thường
cc_library và cc_binary 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.
Đ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 SDK và NDK, hãy thêm đoạn mã sau vào WORKSPACE:
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.
)
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.
)
Để biết thêm thông tin về quy tắc android_ndk_repository, hãy xem mục Nhập từ điển
bản dựng.
Nếu bạn đang sử dụng phiên bản NDK Android gần đây (r22 trở lên), hãy sử dụng quá trình triển khai Starlark của android_ndk_repository.
Hãy làm theo hướng dẫn trong
tệp README.
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
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 biểu đồ 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:appLệnh bazel build biên dịch các tệp Java, tệp tài nguyên Android và
cc_library quy tắc, đồ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) duy nhất,
nhắm đến ABI armeabi-v7a theo mặc định. Để thay đổi điều này hoặc tạo cho
nhiều ABI cùng một lúc, hãy xem phần về cách định cấu hình ABI
mục tiêu.
Thiết lập mẫu
Ví dụ này có trong kho lưu trữ ví dụ về Bazel.
Trong tệp BUILD.bazel, 3 mục tiêu được xác định bằng các 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 quá trình 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 STL
Để định cấu hình STL C++, hãy sử dụng cờ --android_crosstool_top.
bazel build //:app --android_crosstool_top=target labelCác STL có sẵn trong @androidndk là:
| STL | Nhãn mục tiêu |
|---|---|
| STLport | @androidndk//:toolchain-stlport |
| libc++ | @androidndk//:toolchain-libcpp |
| gnustl | @androidndk//:toolchain-gnu-libstdcpp |
Đối với r16 trở xuống, STL mặc định là gnustl. Đối với r17 trở lên, đó là
libc++. Để thuận tiện, mục tiêu @androidndk//:default_crosstool được
đặt bí danh cho các STL mặc định tương ứng.
Xin lưu ý rằng từ r18 trở đi, STLport và gnustl sẽ bị
xoá,
khiến libc++ trở thành STL duy nhất trong NDK.
Xem tài liệu NDK documentation để biết thêm thông tin về các STL này.
Đị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ờ --fat_apk_cpu như sau:
bazel build //:app --fat_apk_cpu=comma-separated list of ABIsTheo mặc định, Bazel tạo mã Android gốc cho armeabi-v7a. Để tạo cho x86
(chẳng hạn như cho trình mô phỏng), hãy truyền --fat_apk_cpu=x86. Để tạo một APK fat cho nhiều
kiến trúc, bạn có thể chỉ định nhiều CPU: --fat_apk_cpu=armeabi-v7a,x86.
Nếu chỉ định nhiều ABI, Bazel sẽ tạo một APK chứa một đối tượng dùng chung cho mỗi ABI.
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 |
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 nhiều ABI cho các bản dựng phát hành vì chúng 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 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 |
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.
Cờ trình biên dịch và trình liên kết cũng có thể được chỉ định 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ích hợp với các nền tảng và chuỗi công cụ
Mô hình cấu hình của Bazel đang chuyển sang
các nền tảng và
chuỗi công cụ. Nếu bản dựng của bạn sử dụng cờ --platforms để chọn kiến trúc hoặc hệ điều hành cần tạo, bạn sẽ cần truyền cờ --extra_toolchains đến Bazel để sử dụng NDK.
Ví dụ: để tích hợp với chuỗi công cụ android_arm64_cgo do
các quy tắc Go cung cấp, hãy truyền --extra_toolchains=@androidndk//:all ngoài cờ
--platforms.
bazel build //my/cc:lib \
--platforms=@io_bazel_rules_go//go/toolchain:android_arm64_cgo \
--extra_toolchains=@androidndk//:allBạn cũng có thể đăng ký trực tiếp trong tệp WORKSPACE:
android_ndk_repository(name = "androidndk")
register_toolchains("@androidndk//:all")
Việc đăng ký các chuỗi công cụ này sẽ cho Bazel biết để tìm các chuỗi công cụ này trong tệp NDK BUILD (cho NDK 20) khi giải quyết các ràng buộc về kiến trúc và hệ điều hành:
toolchain(
name = "x86-clang8.0.7-libcpp_toolchain",
toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
target_compatible_with = [
"@platforms//os:android",
"@platforms//cpu:x86_32",
],
toolchain = "@androidndk//:x86-clang8.0.7-libcpp",
)
toolchain(
name = "x86_64-clang8.0.7-libcpp_toolchain",
toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
target_compatible_with = [
"@platforms//os:android",
"@platforms//cpu:x86_64",
],
toolchain = "@androidndk//:x86_64-clang8.0.7-libcpp",
)
toolchain(
name = "arm-linux-androideabi-clang8.0.7-v7a-libcpp_toolchain",
toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
target_compatible_with = [
"@platforms//os:android",
"@platforms//cpu:arm",
],
toolchain = "@androidndk//:arm-linux-androideabi-clang8.0.7-v7a-libcpp",
)
toolchain(
name = "aarch64-linux-android-clang8.0.7-libcpp_toolchain",
toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
target_compatible_with = [
"@platforms//os:android",
"@platforms//cpu:aarch64",
],
toolchain = "@androidndk//:aarch64-linux-android-clang8.0.7-libcpp",
)
Cách hoạt động: giới thiệu về quá trình chuyển đổi cấu hình Android
Quy tắc android_binary có thể yêu cầu Bazel tạo các phần phụ thuộc của nó trong
một cấu hình tương thích với Android để bản dựng Bazel chỉ hoạt động mà không cần
bất kỳ cờ đặc biệt nào, ngoại trừ --fat_apk_cpu và --android_crosstool_top cho
cấu hình ABI và STL.
Ở phía sau, cấu hình tự động này sử dụng quá trình chuyển đổi cấu hình Android cấu hình chuyển đổi.
Một quy tắc tương thích, chẳng hạn như android_binary, sẽ tự động thay đổi
cấu hình của các phần phụ thuộc thành cấu hình Android, vì vậy, chỉ
các cây con dành riêng cho Android của bản dựng mới bị ảnh hưởng. Các phần khác của biểu đồ bản dựng
được xử lý bằng cấu hình mục tiêu cấp cao nhất. Thậm chí, hệ thống có thể xử lý một mục tiêu duy nhất trong cả hai cấu hình, nếu có các đường dẫn thông qua biểu đồ bản dựng để hỗ trợ điều đó.
Sau khi Bazel ở trong cấu hình tương thích với Android, được chỉ định ở cấp cao nhất hoặc do điểm chuyển đổi cấp cao hơn, các điểm chuyển đổi bổ sung gặp phải sẽ không sửa đổi thêm cấu hình.
Vị trí tích hợp duy nhất kích hoạt quá trình chuyển đổi sang cấu hình Android
là android_binary's deps thuộc tính.
Ví dụ: nếu bạn cố gắng tạo mục tiêu android_library với phần phụ thuộc cc_library
mà không có bất kỳ cờ nào, bạn có thể gặp lỗi về tiêu đề JNI
bị thiếu:
ERROR: project/app/src/main/BUILD.bazel:16:1: C++ compilation of rule '//app/src/main:jni_lib' failed (Exit 1)
app/src/main/cpp/native-lib.cpp:1:10: fatal error: 'jni.h' file not found
#include <jni.h>
^~~~~~~
1 error generated.
Target //app/src/main:lib failed to build
Use --verbose_failures to see the command lines of failed build steps.
Lý tưởng nhất là các quá trình chuyển đổi tự động này sẽ giúp Bazel thực hiện đúng việc trong phần lớn các trường hợp. Tuy nhiên, nếu mục tiêu trên dòng lệnh Bazel đã nằm dưới bất kỳ quy tắc chuyển đổi nào trong số này, chẳng hạn như nhà phát triển C++ kiểm thử một
cc_library, thì phải sử dụng --crosstool_top tuỳ chỉnh.
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ác cờ --crosstool_top, --cpu và --host_crosstool_top.
Ví dụ:
bazel build //my/cc/jni:target \
--crosstool_top=@androidndk//:default_crosstool \
--cpu=<abi> \
--host_crosstool_top=@bazel_tools//tools/cpp:toolchainTrong ví dụ này, các mục tiêu cc_library và cc_binary cấp cao nhất được tạo
bằng chuỗi công cụ NDK. Tuy nhiên, điều này khiến các công cụ máy chủ riêng của Bazel được tạo
bằng chuỗi công cụ NDK (và do đó là cho Android), vì chuỗi công cụ máy chủ được
sao chép từ chuỗi công cụ mục tiêu. Để giải quyết vấn đề này, hãy chỉ định giá trị của
--host_crosstool_top là @bazel_tools//tools/cpp:toolchain để
đặt rõ ràng chuỗi công cụ C++ của máy chủ.
Vớ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 --crosstool_top=@androidndk//:default_crosstool
common:android_x86 --cpu=x86
common:android_x86 --host_crosstool_top=@bazel_tools//tools/cpp:toolchain
common:android_armeabi-v7a --crosstool_top=@androidndk//:default_crosstool
common:android_armeabi-v7a --cpu=armeabi-v7a
common:android_armeabi-v7a --host_crosstool_top=@bazel_tools//tools/cpp:toolchain
# In general
common:android_<abi> --crosstool_top=@androidndk//:default_crosstool
common:android_<abi> --cpu=<abi>
common:android_<abi> --host_crosstool_top=@bazel_tools//tools/cpp:toolchain
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 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.