将 Android 原生开发套件与 Bazel 搭配使用

报告问题 查看源代码 每夜版 · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

如果您是 Bazel 新手,请先参阅使用 Bazel 构建 Android 教程。

概览

Bazel 可以在许多不同的 build 配置中运行,包括使用 Android 原生开发套件 (NDK) 工具链的配置。这意味着,正常的 cc_librarycc_binary 规则可以直接在 Bazel 中针对 Android 进行编译。Bazel 通过使用 android_ndk_repository 代码库规则及其相关的 bzlmod 扩展程序来实现此目的。

对于常规 Android 编译,请使用 rules_android。本教程演示了如何将 C++ 库依赖项集成到 Android 应用中,并使用 rules_android_ndk 进行 NDK 工具链发现和注册。

前提条件

请确保您已安装 Android SDK 和 NDK。

NDK 和 SDK 设置

外部代码库设置因您使用的是 WORKSPACE 还是 bzlmod (MODULE.bazel) 而异。对于 Bazel 7 及更高版本,Bzlmod 是首选解决方案。请注意,MODULE.bazel 和 WORKSPACE 设置 stanza 彼此独立。 如果您使用一种依赖项管理解决方案,则无需添加另一种解决方案的样板代码。

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_androidrules_android_ndk 规则都需要额外的样板代码,而上面的 WORKSPACE 代码段中并未显示这些代码。如需查看最新且完整的实例化 stanza,请参阅 rules_android_ndk 的基本示例应用的 WORKSPACE 文件。

如需详细了解 android_ndk_repository 规则,请参阅其文档字符串

快速入门

如需为 Android 构建 C++,只需将 cc_library 依赖项添加到 android_binaryandroid_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 项目的 build 图。

如需构建应用,只需运行以下命令:

bazel build //app/src/main:app --android_platforms=<your platform>

请注意,如果您未指定 --android_platforms,则 build 将失败并显示有关缺少 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_library 编译成一个共享对象 (.so) 文件,并以 --android_platforms 指定的架构为目标平台。 如需了解详情,请参阅配置目标 ABI 部分。

示例设置

此示例可在 Bazel 示例代码库中找到。

BUILD.bazel 文件中,使用 android_binaryandroid_librarycc_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 胖 APK 用于发布 build,因为它们会增加 APK 的大小,但可用于开发和 QA build。

选择 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 传递编译器和链接器标志,请参阅用户手册

编译器和链接器标志也可以在 cc_library 中使用 coptslinkopts 指定为属性。例如:

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 的独立 cc_binarycc_library(适用于 Android),请使用 --platforms 标志。

例如,假设您已在 my/platforms/BUILD 中定义了 Android 平台:

bazel build //my/cc/jni:target \
      --platforms=//my/platforms:x86_64

采用这种方法,整个 build 树都会受到影响。

这些标志可以放入 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 的自动配置转换。