Jika Anda baru mengenal Bazel, mulailah dengan Membangun Android dengan Bazel.
Ringkasan
Bazel dapat berjalan di berbagai konfigurasi build yang berbeda, termasuk beberapa yang menggunakan
toolchain Android Native Development Kit (NDK). Ini berarti bahwa normal
Aturan cc_library
dan cc_binary
dapat dikompilasi untuk Android secara langsung di dalam
Bazel. Bazel melakukannya dengan menggunakan repositori android_ndk_repository
aturan.
Prasyarat
Pastikan Anda telah menginstal Android SDK dan NDK.
Untuk menyiapkan SDK dan NDK, tambahkan cuplikan berikut ke WORKSPACE
Anda:
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 dokumen Build
Entri Ensiklopedia.
Jika Anda menggunakan Android NDK versi terbaru (r22 dan versi yang lebih baru), gunakan
Implementasi Starlark dari android_ndk_repository
.
Ikuti petunjuk di
READMEnya.
Mulai cepat
Untuk membangun C++ untuk Android, cukup tambahkan dependensi cc_library
ke
android_binary
atau android_library
.
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 membuat aplikasi, cukup jalankan:
bazel build //app/src/main:app
Perintah bazel build
mengompilasi file Java, file resource Android, dan
cc_library
aturan, dan memaketkan semuanya menjadi 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_libraries menjadi satu file objek bersama (.so
),
yang ditargetkan untuk ABI armeabi-v7a
secara default. Untuk mengubahnya
atau membangun untuk
beberapa ABI secara bersamaan, lihat bagian tentang mengonfigurasi target
ABI lebih lanjut.
Contoh penyiapan
Contoh ini tersedia dalam contoh Bazel repositori Anda.
Dalam file BUILD.bazel
, tiga target ditentukan dengan android_binary
,
android_library
, dan cc_library
aturan.
Target tingkat atas android_binary
membuat APK.
Target cc_library
berisi satu file sumber C++ dengan fungsi JNI
penerapan:
#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 JNI
{i>function<i}:
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("app");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
}
public native String stringFromJNI();
}
Mengkonfigurasi 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 |
Gnustal | @androidndk//:toolchain-gnu-libstdcpp |
Untuk r16 dan yang lebih lama, STL default adalah gnustl
. Untuk r17 ke atas,
libc++
. Untuk memudahkan, target @androidndk//:default_crosstool
dialiaskan ke masing-masing STL {i>default<i}.
Perlu diketahui bahwa mulai r18 dan seterusnya, STLport dan gnustl akan
dihapus,
menjadikan libc++
satu-satunya STL di NDK.
Lihat NDK dokumentasi untuk informasi lebih lanjut 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 membuat kode native Android untuk armeabi-v7a
. Untuk membangun aplikasi x86
(misalnya untuk emulator), teruskan --fat_apk_cpu=x86
. Untuk membuat APK gemuk untuk beberapa
Anda dapat menentukan beberapa CPU sekaligus: --fat_apk_cpu=armeabi-v7a,x86
.
Jika lebih dari satu ABI ditentukan, Bazel akan membangun APK yang berisi untuk setiap ABI.
Bergantung pada revisi NDK dan level API Android, ABI berikut adalah tersedia:
Revisi NDK | ABI |
---|---|
16 dan yang lebih lama | armeabi, armeabi-v7a, arm64-v8a, mips, mips64, x86, x86_64 |
17 ke atas | armeabi-v7a, arm64-v8a, x86, x86_64 |
Lihat dokumen NDK untuk mengetahui informasi selengkapnya tentang ABI ini.
APK Lemak Multi-ABI tidak direkomendasikan untuk build rilis karena meningkat ukuran APK, tetapi dapat berguna untuk pengembangan dan build QA.
Memilih standar C++
Gunakan flag berikut untuk membangun 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 tanda compiler dan penaut dengan --cxxopt
, --copt
, dan
--linkopt
di Panduan Pengguna.
Tanda compiler dan penaut 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 bergerak menuju
platform dan
Rantai Konten. Jika
build menggunakan flag --platforms
yang dipilih untuk arsitektur atau sistem operasi
untuk membangun, Anda harus meneruskan flag --extra_toolchains
ke Bazel di
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")
Dengan mendaftarkan toolchain ini, Bazel akan diberi tahu untuk mencarinya di BUILD
NDK
(untuk NDK 20) saat me-resolve arsitektur dan batasan 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 membangun dependensinya di
konfigurasi yang kompatibel dengan Android sehingga build Bazel dapat berfungsi tanpa
tanda khusus apa pun, kecuali --fat_apk_cpu
dan --android_crosstool_top
untuk
konfigurasi ABI dan STL.
Di balik layar, konfigurasi otomatis ini menggunakan konfigurasi Android ini.
Aturan yang kompatibel, seperti android_binary
, secara otomatis mengubah
konfigurasi dependensinya ke konfigurasi Android, jadi hanya
Subhierarki khusus Android dari build terpengaruh. Bagian build lainnya
grafik diproses menggunakan konfigurasi target tingkat teratas. Bahkan mungkin
memproses satu target di kedua konfigurasi, jika ada jalur melalui
untuk mendukungnya.
Setelah Bazel berada dalam konfigurasi yang kompatibel dengan Android, tingkat teratas atau karena titik transisi tingkat yang lebih tinggi, transisi tambahan titik yang ditemui tidak mengubah konfigurasi lebih lanjut.
Satu-satunya lokasi bawaan yang memicu transisi ke Android
konfigurasi adalah atribut deps
dari android_binary
.
Misalnya, jika Anda mencoba membuat target android_library
dengan cc_library
dependensi tanpa tanda apa pun, Anda mungkin akan mengalami error tentang JNI yang tidak ada
{i>header<i}:
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 harus membuat Bazel melakukan hal yang benar
dalam kebanyakan kasus. Namun, jika target pada
baris perintah Bazel sudah
di bawah salah satu aturan transisi ini, seperti developer C++ yang menguji
cc_library
, maka --crosstool_top
kustom harus digunakan.
Membuat cc_library
untuk Android tanpa menggunakan android_binary
Untuk membuat cc_binary
atau cc_library
mandiri bagi Android tanpa menggunakan elemen
android_binary
, gunakan --crosstool_top
, --cpu
, dan --host_crosstool_top
penanda.
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
tingkat teratas dibuat
menggunakan toolchain NDK. Namun, hal ini menyebabkan
alat {i>host<i} Bazel dibuat
dengan toolchain NDK (dan juga untuk Android), karena toolchain host
yang disalin dari toolchain target. Untuk mengatasi hal ini, tentukan nilai
--host_crosstool_top
menjadi @bazel_tools//tools/cpp:toolchain
bagi
secara eksplisit mengatur toolchain C++ host.
Dengan pendekatan ini, seluruh struktur pohon 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 membangun 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 tahu persis apa yang
sedang Anda bangun; mengandalkan konfigurasi otomatis
transisi dari android_binary
untuk target tingkat tinggi yang Anda harapkan
untuk membangun banyak target
yang tidak Anda kontrol.