Nếu bạn mới sử dụng Bazel, vui lòng bắt đầu với hướng dẫn Xây dựng Android bằng Hướng dẫn về 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ụ Android Native Development Kit (NDK). Điều này có nghĩa là bình thường
Bạn có thể biên dịch các quy tắc cc_library
và cc_binary
cho Android ngay trong
Bazel. Bazel hoàn thành việc này bằng cách sử dụng kho lưu trữ android_ndk_repository
.
Điều kiện tiên quyết
Vui lòng đảm bảo rằng bạn đã cài đặt NDK và SDK Android.
Để 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 bài viết Tạo
Mục nhập từ bách khoa toàn thư.
Nếu bạn đang dùng phiên bản Android NDK gần đây (r22 trở lên), hãy sử dụng
Cách triển khai android_ndk_repository
của Starlark.
Làm theo hướng dẫn trong
README của ứng dụng.
Bắt đầu nhanh
Để xây dựng 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ụ: với 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 đây:
Hình 1. Tạo biểu đồ dự án Android có 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
Lệ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 và đóng gói mọi thứ vào một 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 biên dịch tất cả các cc_ libraries thành một tệp đối tượng dùng chung (.so
),
được nhắm mục tiêu cho ABI armeabi-v7a
theo mặc định. Để thay đổi chế độ này hoặc tạo bản dựng cho
nhiều ABI cùng lúc, xem phần định cấu hình mục tiêu
ABI.
Thiết lập mẫu
Ví dụ này có sẵn trong ví dụ về vùng Brazil kho lưu trữ.
Trong tệp BUILD.bazel
, 3 mục tiêu được xác định bằng android_binary
,
Các quy tắc 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++ có hàm JNI
triển khai:
#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());
}
Đích android_library
chỉ định các nguồn Java, tệp tài nguyên và
phần phụ thuộc trên mục tiêu cc_library
. Trong ví dụ này, MainActivity.java
tải
tệp đối tượng dùng chung libapp.so
và định nghĩa chữ ký phương thức cho JNI
hàm:
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 label
Sau đây là các STL có sẵn trong @androidndk
:
STL | Nhãn mục tiêu |
---|---|
STLport | @androidndk//:toolchain-stlport |
libc++ | @androidndk//:toolchain-libcpp |
sư tử đầu chim | @androidndk//:toolchain-gnu-libstdcpp |
Đối với phiên bản r16 trở xuống, STL mặc định là gnustl
. Đối với r17 trở lên, nó là
libc++
. Để thuận tiện, @androidndk//:default_crosstool
mục tiêu là
được đặt bí danh thành STL mặc định tương ứng.
Xin lưu ý rằng từ phiên bản r18 trở đi, STLport và gnustl sẽ
đã bị xoá,
đặt libc++
thành STL duy nhất trong NDK.
Xem NDK tài liệu để 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 ABIs
Theo mặc định, Bazel tạo mã Android gốc cho armeabi-v7a
. Cách xây dựng cho x86
(chẳng hạn như đối với trình mô phỏng), hãy truyền --fat_apk_cpu=x86
. Để tạo một APK lớn cho nhiều
cấu trúc, bạn có thể chỉ định nhiều CPU: --fat_apk_cpu=armeabi-v7a,x86
.
Nếu bạn chỉ định nhiều ABI, Bazel sẽ tạo một APK chứa một ABI cho từng ABI.
Tuỳ thuộc vào bản sửa đổi NDK và cấp độ API Android, các ABI sau đây hiện có:
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 về NDK để biết thêm thông tin về các ABI này.
Không nên dùng nhiều APK nhiều ABI cho các bản phát hành vì các APK này tăng lên 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à đảm bảo chất lượng.
Chọn một tiêu chuẩn C++
Sử dụng các cờ sau để xây dựng theo tiêu chuẩn C++:
Tiêu chuẩn C++ | Cờ |
---|---|
C++98 | Mặc định, không cần gắ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 làm thuộc tính trong cc_library
bằ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 được hướng tới
nền tảng và
chuỗi công cụ. Nếu
bản dựng này sử dụng cờ --platforms
để chọn cấu trúc hoặc hệ điều hành
để xây dựng, bạn sẽ cần truyền cờ --extra_toolchains
đến Bazel trong
yêu cầu 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, hãy chuyể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//:all
Bạn cũng có thể đăng ký trực tiếp trong tệp WORKSPACE
:
android_ndk_repository(name = "androidndk")
register_toolchains("@androidndk//:all")
Khi đăng ký các chuỗi công cụ này, Bazel sẽ yêu cầu Bazel tìm các chuỗi công cụ đó trong NDK BUILD
(đối với NDK 20) khi phân giải các hạn chế về cấu 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 các hiệu ứng 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 một cách rõ ràng trong
cấu hình tương thích với Android để bản dựng Bazel 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.
Cấu hình tự động này mặc nhiên sử dụng cấu hình Android hiệu ứng chuyển cảnh.
Một quy tắc tương thích (chẳng hạn như android_binary
) sẽ tự động thay đổi
của các phần phụ thuộc đó thành một 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 sẽ bị ảnh hưởng. Các phần khác của bản dựng
biểu đồ được xử lý bằng cách sử dụng cấu hình mục tiêu cấp cao nhất. Thậm chí 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
tạo biểu đồ để hỗ trợ điều đó.
Sau khi Bazel đã ở trong một cấu hình tương thích với Android, hoặc được chỉ định tại cấp cao nhất hoặc do điểm chuyển tiếp ở cấp cao hơn, chuyển đổi bổ sung điểm gặp phải 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 Android
là thuộc tính deps
của android_binary
.
Ví dụ: nếu bạn cố gắng tạo mục tiêu android_library
bằng cc_library
không có bất kỳ cờ nào, nên bạn có thể gặp lỗi về việc thiếu JNI
tiê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.
Tốt nhất là những chuyển đổi tự động này sẽ giúp Bazel làm đúng việc trong
trong hầu hết các trường hợp. Tuy nhiên, nếu mục tiêu trên dòng lệnh Bazel đã
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++ đang thử nghiệm một
cc_library
, thì bạn phải dùng một --crosstool_top
tuỳ chỉnh.
Tạo cc_library
cho Android mà không cần sử dụng android_binary
Để tạo một cc_binary
hoặc cc_library
độc lập cho Android mà không cần sử dụng
android_binary
, sử dụng --crosstool_top
, --cpu
và --host_crosstool_top
cờ.
Ví dụ:
bazel build //my/cc/jni:target \
--crosstool_top=@androidndk//:default_crosstool \
--cpu=<abi> \
--host_crosstool_top=@bazel_tools//tools/cpp:toolchain
Trong ví dụ này, các mục tiêu cc_library
và cc_binary
cấp cao nhất sẽ đượ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ụ lưu trữ của Bazel được tạo
bằng chuỗi công cụ NDK (và do đó dành cho Android), vì chuỗi công cụ lưu trữ là
đã 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
từ @bazel_tools//tools/cpp:toolchain
đến
đặt rõ ràng chuỗi công cụ C++ của máy chủ lưu trữ.
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 những 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
chẳng hạn, hãy chạy:
bazel build //my/cc/jni:target --config=android_x86
Nó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 xây dựng; dựa vào cấu hình tự động
chuyển đổi từ android_binary
cho các mục tiêu cấp cao mà bạn đang mong đợi
để xây dựng nhiều mục tiêu
mà bạn không kiểm soát được.