Sử dụng Bộ phát triển gốc Android với Bazel

Báo cáo vấn đề Xem nguồn Hằng đêm · 7,3 · 7,2 · 7.1 · 7 · 6,5

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_librarycc_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:

Kết quả mẫu

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_librarycc_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 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 platforms

Giống như cờ --platforms, các giá trị được chuyển đến --android_platforms là các nhãn của platform đích, sử dụng các giá trị ràng buộc chuẩn để mô tả thiết bị của bạn.

Ví dụ: đối với thiết bị Android có bộ xử lý ARM 64 bit, bạn sẽ định nghĩa nền tảng của bạn như sau:

platform(
    name = "android_arm64",
    constraint_values = [
        "@platforms//os:android",
        "@platforms//cpu:arm64",
    ],
)

Mọi platform của Android đều phải sử dụng @platforms//os:android Quy tắc ràng buộc đối với hệ điều hành. Để di chuyển quy tắc ràng buộc về CPU, hãy xem biểu đồ sau:

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 một APK đa kiến trúc, bạn sẽ chuyển nhiều nhãn, để ví dụ: --android_platforms=//:arm64,//:x86_64 (giả sử bạn đã xác định những mã đó trong tệp BUILD.bazel cấp cao nhất của bạn).

Bazel không thể chọn nền tảng Android mặc định, nên bạn phải xác định một nền tảng và được chỉ định 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 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
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--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 coptslinkopts. 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 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 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_64

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 --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 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.