Jika Anda baru menggunakan Bazel, mulailah dengan tutorial Mem-build Android dengan Bazel.
Ringkasan
Bazel dapat berjalan dalam berbagai konfigurasi build, termasuk beberapa yang menggunakan
toolchain Android Native Development Kit (NDK). Artinya, aturan
cc_library
dan cc_binary
normal dapat dikompilasi untuk Android langsung dalam
Bazel. Bazel melakukannya dengan menggunakan aturan repositori
android_ndk_repository
.
Prasyarat
Pastikan Anda telah menginstal Android SDK dan NDK.
Untuk menyiapkan SDK dan NDK, tambahkan cuplikan berikut ke 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.
)
Untuk informasi selengkapnya tentang aturan android_ndk_repository
, lihat entri
Ensiklopedia Build.
Jika Anda menggunakan Android NDK versi terbaru (r22 dan yang lebih baru), gunakan
implementasi Starlark android_ndk_repository
.
Ikuti petunjuk di
README-nya.
Mulai cepat
Untuk mem-build C++ untuk Android, cukup tambahkan dependensi cc_library
ke
aturan android_binary
atau android_library
Anda.
Misalnya, dengan file BUILD
berikut untuk aplikasi 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",
)
File BUILD
ini menghasilkan grafik target berikut:
Gambar 1. Membuat grafik project Android dengan dependensi cc_library.
Untuk mem-build aplikasi, cukup jalankan:
bazel build //app/src/main:app
Perintah bazel build
mengompilasi file Java, file resource Android, dan
aturan cc_library
, serta memaketkan semuanya ke dalam 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 mengompilasi semua cc_library menjadi satu file objek bersama (.so
),
yang ditargetkan untuk ABI armeabi-v7a
secara default. Untuk mengubahnya atau mem-build untuk
beberapa ABI secara bersamaan, lihat bagian tentang mengonfigurasi
target ABI.
Contoh penyiapan
Contoh ini tersedia di repositori contoh Bazel.
Dalam file BUILD.bazel
, tiga target ditentukan dengan aturan android_binary
,
android_library
, dan cc_library
.
Target tingkat atas android_binary
mem-build APK.
Target cc_library
berisi satu file sumber C++ dengan implementasi fungsi
JDK:
#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());
}
Target android_library
menentukan sumber Java, file resource, dan
dependensi pada target cc_library
. Untuk contoh ini, MainActivity.java
memuat
file objek bersama libapp.so
, dan menentukan tanda tangan metode untuk fungsi
JNI:
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("app");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
}
public native String stringFromJNI();
}
Mengonfigurasi STL
Untuk mengonfigurasi STL C++, gunakan flag --android_crosstool_top
.
bazel build //:app --android_crosstool_top=target label
STL yang tersedia di @androidndk
adalah:
STL | Label target |
---|---|
STLport | @androidndk//:toolchain-stlport |
libc++ | @androidndk//:toolchain-libcpp |
gnustl | @androidndk//:toolchain-gnu-libstdcpp |
Untuk r16 dan yang lebih lama, STL default-nya adalah gnustl
. Untuk r17 dan yang lebih baru, nilainya adalah
libc++
. Untuk memudahkan, @androidndk//:default_crosstool
target
akan diberi alias ke STL default masing-masing.
Perlu diperhatikan bahwa mulai r18 dan seterusnya, STLport dan gnustl akan
dihapus,
sehingga libc++
menjadi satu-satunya STL di NDK.
Lihat dokumentasi NDK untuk mengetahui informasi selengkapnya tentang STL ini.
Mengonfigurasi ABI target
Untuk mengonfigurasi ABI target, gunakan flag --fat_apk_cpu
sebagai berikut:
bazel build //:app --fat_apk_cpu=comma-separated list of ABIs
Secara default, Bazel mem-build kode Android native untuk armeabi-v7a
. Untuk mem-build untuk x86
(seperti untuk emulator), teruskan --fat_apk_cpu=x86
. Untuk membuat APK tebal untuk beberapa
arsitektur, Anda dapat menentukan beberapa CPU: --fat_apk_cpu=armeabi-v7a,x86
.
Jika lebih dari satu ABI ditentukan, Bazel akan mem-build APK yang berisi objek umum untuk setiap ABI.
Bergantung pada revisi NDK dan API level Android, ABI berikut tersedia:
Revisi NDK | ABI |
---|---|
16 tahun ke bawah | armeabi, armeabi-v7a, arm64-v8a, mips, mips64, x86, x86_64 |
17 tahun ke atas | armeabi-v7a, arm64-v8a, x86, x86_64 |
Lihat dokumen NDK untuk informasi selengkapnya tentang ABI ini.
Fat APK Multi-ABI tidak direkomendasikan untuk build rilis karena meningkatkan ukuran APK, tetapi dapat berguna untuk build pengembangan dan QA.
Memilih standar C++
Gunakan flag berikut untuk mem-build sesuai dengan standar C++:
Standar C++ | Bendera |
---|---|
C++98 | Default, tidak perlu tanda |
C++11 | --cxxopt=-std=c++11 |
C++14 | --cxxopt=-std=c++14 |
Contoh:
bazel build //:app --cxxopt=-std=c++11
Baca selengkapnya tentang meneruskan flag compiler dan linker dengan --cxxopt
, --copt
, dan
--linkopt
di Panduan Pengguna.
Flag compiler dan linker juga dapat ditentukan sebagai atribut di cc_library
menggunakan copts
dan linkopts
. Contoh:
cc_library(
name = "jni_lib",
srcs = ["cpp/native-lib.cpp"],
copts = ["-std=c++11"],
linkopts = ["-ldl"], # link against libdl
)
Integrasi dengan platform dan toolchain
Model konfigurasi Bazel beralih ke
platform dan
toolchain. Jika build
Anda menggunakan flag --platforms
untuk memilih arsitektur atau sistem operasi
yang akan di-build, Anda harus meneruskan flag --extra_toolchains
ke Bazel
untuk menggunakan NDK.
Misalnya, untuk berintegrasi dengan toolchain android_arm64_cgo
yang disediakan oleh
aturan Go, teruskan --extra_toolchains=@androidndk//:all
selain
tanda --platforms
.
bazel build //my/cc:lib \
--platforms=@io_bazel_rules_go//go/toolchain:android_arm64_cgo \
--extra_toolchains=@androidndk//:all
Anda juga dapat mendaftarkannya langsung di file WORKSPACE
:
android_ndk_repository(name = "androidndk")
register_toolchains("@androidndk//:all")
Mendaftarkan toolchain ini akan memberi tahu Bazel untuk mencarinya dalam file BUILD
NDK (untuk NDK 20) saat me-resolve batasan arsitektur dan sistem operasi:
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",
)
Cara kerjanya: memperkenalkan transisi konfigurasi Android
Aturan android_binary
dapat secara eksplisit meminta Bazel untuk mem-build dependensinya dalam
konfigurasi yang kompatibel dengan Android sehingga build Bazel langsung berfungsi tanpa
flag khusus, kecuali untuk --fat_apk_cpu
dan --android_crosstool_top
untuk
konfigurasi ABI dan STL.
Di balik layar, konfigurasi otomatis ini menggunakan transisi konfigurasi Android.
Aturan yang kompatibel, seperti android_binary
, secara otomatis mengubah
konfigurasi dependensinya menjadi konfigurasi Android, sehingga hanya
subtree khusus Android dari build yang terpengaruh. Bagian lain dari grafik
build diproses menggunakan konfigurasi target tingkat teratas. Bahkan, build ini dapat
memproses satu target di kedua konfigurasi, jika ada jalur melalui
grafik build untuk mendukungnya.
Setelah Bazel berada dalam konfigurasi yang kompatibel dengan Android, baik yang ditentukan di level teratas atau karena titik transisi level yang lebih tinggi, titik transisi tambahan yang ditemukan tidak akan mengubah konfigurasi lebih lanjut.
Satu-satunya lokasi bawaan yang memicu transisi ke konfigurasi
Android adalah atribut deps
android_binary
.
Misalnya, jika Anda mencoba mem-build target android_library
dengan dependensi
cc_library
tanpa flag apa pun, Anda mungkin mengalami error tentang header
JNI yang tidak ada:
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.
Idealnya, transisi otomatis ini akan membuat Bazel melakukan hal yang benar dalam
sebagian besar kasus. Namun, jika target di command line Bazel sudah
di bawah salah satu aturan transisi ini, seperti developer C++ yang menguji
cc_library
tertentu, --crosstool_top
kustom harus digunakan.
Mem-build cc_library
untuk Android tanpa menggunakan android_binary
Untuk mem-build cc_binary
atau cc_library
mandiri untuk Android tanpa menggunakan
android_binary
, gunakan flag
--crosstool_top
, --cpu
, dan --host_crosstool_top
.
Contoh:
bazel build //my/cc/jni:target \
--crosstool_top=@androidndk//:default_crosstool \
--cpu=<abi> \
--host_crosstool_top=@bazel_tools//tools/cpp:toolchain
Dalam contoh ini, target cc_library
dan cc_binary
level teratas dibuat
menggunakan toolchain NDK. Namun, hal ini menyebabkan alat host Bazel sendiri di-build
dengan toolchain NDK (dan untuk Android), karena toolchain host
dikopy dari toolchain target. Untuk mengatasi hal ini, tentukan nilai
--host_crosstool_top
menjadi @bazel_tools//tools/cpp:toolchain
untuk
menetapkan toolchain C++ host secara eksplisit.
Dengan pendekatan ini, seluruh hierarki build akan terpengaruh.
Flag ini dapat dimasukkan ke dalam konfigurasi bazelrc
(satu untuk setiap ABI), di
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
Kemudian, untuk mem-build cc_library
untuk x86
misalnya, jalankan:
bazel build //my/cc/jni:target --config=android_x86
Secara umum, gunakan metode ini untuk target tingkat rendah (seperti cc_library
) atau saat
Anda mengetahui dengan tepat apa yang Anda build; andalkan transisi konfigurasi
otomatis dari android_binary
untuk target tingkat tinggi tempat Anda mengharapkan
untuk mem-build banyak target yang tidak Anda kontrol.