Menggunakan Android Native Development Kit dengan Bazel

Laporkan masalah Lihat sumber Per malam · 7,3 · 7,2 · 7,1 · 7,0 · 6,5

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:

Hasil contoh

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();

}

Mengonfigurasi ABI target

Untuk mengonfigurasi ABI target, gunakan flag --android_platforms sebagai berikut:

bazel build //:app --android_platforms=comma-separated list of platforms

Sama seperti flag --platforms, nilai yang diteruskan ke --android_platforms adalah label platform target, menggunakan nilai batasan standar untuk mendeskripsikan perangkat Anda.

Misalnya, untuk perangkat Android dengan prosesor ARM 64-bit, Anda harus mendefinisikan platform Anda seperti ini:

platform(
    name = "android_arm64",
    constraint_values = [
        "@platforms//os:android",
        "@platforms//cpu:arm64",
    ],
)

Setiap platform Android harus menggunakan @platforms//os:android Batasan OS. Untuk memigrasikan batasan CPU, periksa diagram ini:

Nilai CPU Platform
armeabi-v7a @platforms//cpu:armv7
arm64-v8a @platforms//cpu:arm64
x86 @platforms//cpu:x86_32
x86_64 @platforms//cpu:x86_64

Dan, tentu saja, untuk APK multi-arsitektur, Anda meneruskan beberapa label, untuk contoh: --android_platforms=//:arm64,//:x86_64 (dengan asumsi Anda menentukan hal-hal tersebut dalam file BUILD.bazel tingkat teratas Anda).

Bazel tidak dapat memilih platform Android {i> default<i}, jadi platform tersebut harus ditentukan dan ditentukan dengan --android_platforms.

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
C++17 --cxxopt=-std=c++17

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
)

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 flag --platforms.

Misalnya, asumsi Anda telah mendefinisikan platform Android di my/platforms/BUILD:

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

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 --platforms=//my/platforms:x86

common:android_armeabi-v7a --platforms=//my/platforms:armeabi-v7a

# In general
common:android_<abi> --platforms=//my/platforms:<abi>

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.