如果您是 Bazel 新手,请首先参阅使用 Bazel 构建 Android 应用 教程。
概览
Bazel 可以在许多不同的 build 配置中运行,包括使用
Android 原生开发套件 (NDK) 工具链的配置。这意味着,普通的
cc_library 和 cc_binary 规则可以直接在
Bazel 中针对 Android 进行编译。Bazel 通过使用 android_ndk_repository 代码库
规则来实现此目的。
前提条件
请确保您已安装 Android SDK 和 NDK。
如需设置 SDK 和 NDK,请将以下代码段添加到 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.
)
如需详细了解 android_ndk_repository 规则,请参阅 Build
百科全书条目。
如果您使用的是最新版本的 Android NDK(r22 及更高版本),请使用
android_ndk_repository 的 Starlark 实现。
按照其
自述文件中的说明操作。
快速入门
如需为 Android 构建 C++,只需将 cc_library 依赖项添加到您的
android_binary 或 android_library 规则即可。
例如,假设 Android 应用的 BUILD 文件如下所示:
# 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",
)
此 BUILD 文件会生成以下目标图:

图 1。具有 cc_library 依赖项的 Android 项目的 build 图。
如需构建应用,只需运行以下命令:
bazel build //app/src/main:appbazel 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.MFBazel 会将所有 cc_libraries 编译到单个共享对象 (.so) 文件中,
默认情况下以 armeabi-v7a ABI 为目标。如需更改此设置或同时针对
多个 ABI 进行构建,请参阅有关配置目标
ABI的部分。
示例设置
此示例位于 Bazel 示例 代码库中。
在 BUILD.bazel 文件中,使用 android_binary、
android_library 和 cc_library 规则定义了三个目标。
android_binary 顶级目标会构建 APK。
cc_library 目标包含一个具有 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());
}
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();
}
配置 STL
如需配置 C++ STL,请使用标志 --android_crosstool_top。
bazel build //:app --android_crosstool_top=target label@androidndk 中的可用 STL 为:
| STL | 目标标签 |
|---|---|
| STLport | @androidndk//:toolchain-stlport |
| libc++ | @androidndk//:toolchain-libcpp |
| gnustl | @androidndk//:toolchain-gnu-libstdcpp |
对于 r16 及更低版本,默认 STL 为 gnustl。对于 r17 及更高版本,默认 STL 为
libc++。为方便起见,目标 @androidndk//:default_crosstool 已
别名为相应的默认 STL。
请注意,从 r18 开始,STLport 和 gnustl 将被
移除,
因此 libc++ 将成为 NDK 中唯一的 STL。
如需详细了解这些 STL,请参阅 NDK 文档。
配置目标 ABI
如需配置目标 ABI,请使用 --fat_apk_cpu 标志,如下所示:
bazel build //:app --fat_apk_cpu=comma-separated list of ABIs默认情况下,Bazel 会为 armeabi-v7a 构建原生 Android 代码。如需为 x86
(例如模拟器)构建,请传递 --fat_apk_cpu=x86。如需为多个
架构创建胖 APK,您可以指定多个 CPU:--fat_apk_cpu=armeabi-v7a,x86。
如果指定了多个 ABI,Bazel 将构建一个 APK,其中包含每个 ABI 的共享 对象。
根据 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 文档 。
对于发布 build,不建议使用多 ABI 胖 APK,因为它们会增加 APK 的大小,但对于开发和质量保证 build 来说,它们可能很有用。
选择 C++ 标准
使用以下标志根据 C++ 标准进行构建:
| C++ 标准 | 标志 |
|---|---|
| C++98 | 默认,无需标志 |
| C++11 | --cxxopt=-std=c++11 |
| C++14 | --cxxopt=-std=c++14 |
例如:
bazel build //:app --cxxopt=-std=c++11如需详细了解如何使用 --cxxopt、--copt 和
--linkopt 传递编译器和链接器标志,请参阅用户手册。
也可以使用 copts 和 linkopts 将编译器和链接器标志指定为 cc_library
中的属性。例如:
cc_library(
name = "jni_lib",
srcs = ["cpp/native-lib.cpp"],
copts = ["-std=c++11"],
linkopts = ["-ldl"], # link against libdl
)
与平台和工具链集成
Bazel 的配置模型正朝着
平台和
工具链的方向发展。如果您的
build 使用 --platforms 标志来选择要构建的架构或操作系统
,则需要将 --extra_toolchains 标志传递给 Bazel,以便使用 NDK。
例如,如需与 android_arm64_cgo 工具链集成(由
Go 规则提供),除了
--platforms 标志之外,还要传递 --extra_toolchains=@androidndk//:all。
bazel build //my/cc:lib \
--platforms=@io_bazel_rules_go//go/toolchain:android_arm64_cgo \
--extra_toolchains=@androidndk//:all您还可以直接在 WORKSPACE 文件中注册它们:
android_ndk_repository(name = "androidndk")
register_toolchains("@androidndk//:all")
注册这些工具链会告知 Bazel 在解析架构和操作系统限制时在 NDK BUILD
文件(对于 NDK 20)中查找它们:
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",
)
工作原理:Android 配置转换简介
android_binary 规则可以明确要求 Bazel 在与 Android 兼容的配置中构建其依赖项,以便 Bazel build 正常运行,而无需任何特殊标志,但 ABI 和 STL 配置的 --fat_apk_cpu 和 --android_crosstool_top 除外。
在后台,此自动配置使用 Android 配置 转换。
兼容的规则(如 android_binary)会自动将其依赖项的
配置更改为 Android 配置,因此只有
build 的特定于 Android 的子树会受到影响。build
图的其他部分使用顶级目标配置进行处理。如果 build 图中有支持该配置的路径,它甚至可以在这两种配置中处理单个目标。
一旦 Bazel 处于与 Android 兼容的配置中(无论是在 顶级指定还是由于更高级别的转换点),遇到的其他转换 点都不会进一步修改配置。
触发转换为 Android
配置的唯一内置位置是 android_binary's deps 属性。
例如,如果您尝试构建具有 cc_library
依赖项但没有任何标志的 android_library 目标,则可能会遇到有关缺少 JNI
标头的错误:
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.
理想情况下,这些自动转换应使 Bazel 在
大多数情况下都能正确执行操作。但是,如果 Bazel 命令行上的目标已低于任何这些转换规则(例如 C++ 开发者测试特定的
cc_library),则必须使用自定义 --crosstool_top。
在不使用 android_binary 的情况下为 Android 构建 cc_library
如需在不使用
android_binary的情况下为 Android 构建独立的 cc_binary 或 cc_library,请使用 --crosstool_top、--cpu 和 --host_crosstool_top
标志。
例如:
bazel build //my/cc/jni:target \
--crosstool_top=@androidndk//:default_crosstool \
--cpu=<abi> \
--host_crosstool_top=@bazel_tools//tools/cpp:toolchain在此示例中,顶级 cc_library 和 cc_binary 目标是使用 NDK 工具链构建的。但是,这会导致 Bazel 自己的主机工具使用 NDK 工具链(因此针对 Android)进行构建
,因为主机工具链是从目标工具链
复制的。如需解决此问题,请将
--host_crosstool_top的值指定为@bazel_tools//tools/cpp:toolchain,以
明确设置主机的 C++ 工具链。
使用此方法,整个 build 树都会受到影响。
这些标志可以放入 bazelrc 配置(每个 ABI 一个)中,位于
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
然后,如需为 x86 构建 cc_library,请运行以下命令:
bazel build //my/cc/jni:target --config=android_x86一般来说,对于低级别目标(如 cc_library)或当
您确切知道要构建的内容时,请使用此方法;对于您希望构建大量不受您控制的高级别目标,请依赖于
android_binary 的自动配置转换。