如果您是 Bazel 新手,請先參閱「使用 Bazel 建構 Android 應用程式」教學課程。
總覽
Bazel 可在許多不同的建構設定中執行,包括使用 Android Native Development Kit (NDK) 工具鍊的設定。也就是說,您可以在 Bazel 中直接為 Android 編譯一般 cc_library
和 cc_binary
規則。Bazel 會使用 android_ndk_repository
存放區規則和相關的 bzlmod 擴充功能來完成這項作業。
如要進行一般 Android 編譯,請使用 rules_android
。本教學課程說明如何將 C++ 程式庫依附元件整合至 Android 應用程式,並使用 rules_android_ndk
探索及註冊 NDK 工具鍊。
必要條件
請確認您已安裝 Android SDK 和 NDK。
設定 NDK 和 SDK
外部存放區設定方式會因您使用 WORKSPACE 或 bzlmod (MODULE.bazel) 而異。Bzlmod 是 Bazel 7 以上版本的首選解決方案。 請注意,MODULE.bazel 和 WORKSPACE 設定段落互不相干。 如果您使用其中一種依附元件管理解決方案,就不必新增另一種的樣板。
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_android
和rules_android_ndk
規則都需要額外的樣板,但上述 WORKSPACE 片段未顯示。如需最新且完整的例項化節,請參閱rules_android_ndk
基本範例應用程式的 WORKSPACE 檔案。
如要進一步瞭解 android_ndk_repository
規則,請參閱其說明字串。
快速入門
如要為 Android 建構 C++,只要將 cc_library
依附元件新增至 android_binary
或 android_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 專案的圖表。
如要建構應用程式,只要執行下列指令即可:
bazel build //app/src/main:app --android_platforms=<your platform>
請注意,如果您未指定 --android_platforms
,建構作業會失敗,並顯示缺少 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_libraries 編譯成單一共用物件 (.so
) 檔案,並以 --android_platforms
指定的架構為目標。詳情請參閱「設定目標 ABI」一節。
設定範例
這個範例位於 Bazel 範例存放區。
在 BUILD.bazel
檔案中,系統會使用 android_binary
、android_library
和 cc_library
規則定義三個目標。
android_binary
頂層目標會建構 APK。
cc_library
目標包含單一 C++ 來源檔案,其中含有 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();
}
設定目標 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,因為這會增加 APK 的大小,但對於開發和 QA 版本來說可能很有用。
選取 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
中指定為屬性,方法是使用 copts
和 linkopts
。例如:
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 建構獨立的 cc_binary
或 cc_library
,而不使用 android_binary
,請使用 --platforms
標記。
舉例來說,假設您已在 my/platforms/BUILD
中定義 Android 平台:
bazel build //my/cc/jni:target \
--platforms=//my/platforms:x86_64
採用這種做法會影響整個建構樹狀結構。
這些標記可以放入 bazelrc
config (每個 ABI 各一個),位於 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>
然後,如要為 x86
建構 cc_library
,請執行:
bazel build //my/cc/jni:target --config=android_x86
一般來說,這個方法適用於低階目標 (例如 cc_library
),或您確切知道要建構的內容時;對於您預期要建構大量不受控目標的高階目標,請依賴 android_binary
的自動設定轉換。