Bazel ile Android Yerel Geliştirme Kiti'ni kullanma

Sorun bildirin Kaynağı göster

Bazel'i kullanmaya yeni başladıysanız lütfen Bazel ile Android Oluşturma eğitimiyle başlayın.

Genel bakış

Bazel, Android Yerel Geliştirme Kiti (NDK) araç zincirini kullanan birkaç yapılandırma da dahil olmak üzere birçok farklı derleme yapılandırmasında çalışabilir. Bu, normal cc_library ve cc_binary kurallarının Android için doğrudan Bazel'da derlenebileceği anlamına gelir. Bazel, bu işlemi android_ndk_repository deposu kuralını kullanarak gerçekleştirir.

Ön koşullar

Lütfen Android SDK ve NDK'yı yüklediğinizden emin olun.

SDK ve NDK'yı ayarlamak için WORKSPACE cihazınıza aşağıdaki snippet'i ekleyin:

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 kuralı hakkında daha fazla bilgi için Derleme Ansiklopedisi girişi bölümüne bakın.

Hızlı başlangıç

Android için C++ geliştirmek üzere android_binary veya android_library kurallarınıza cc_library bağımlılıkları eklemeniz yeterlidir.

Örneğin, bir Android uygulaması için aşağıdaki BUILD dosyasını ele alalım:

# 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",
)

Bu BUILD dosyası, aşağıdaki hedef grafikle sonuçlanır:

Örnek sonuçlar

Şekil 1. cc_library bağımlılarını içeren Android projesinin grafiğini oluşturma

Uygulamayı derlemek için şu komutu çalıştırın:

bazel build //app/src/main:app

bazel build komutu Java dosyalarını, Android kaynak dosyalarını ve cc_library kurallarını derler ve her şeyi bir APK şeklinde paketler:

$ 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, tüm cc_libraries adlı dosyaları tek bir paylaşılan nesne (.so) dosyasında derler ve varsayılan olarak armeabi-v7a ABI'yi hedefler. Bunu değiştirmek veya aynı anda birden fazla ABI için derleme yapmak istiyorsanız hedef ABI'yi yapılandırma bölümüne bakın.

Örnek kurulum

Bu örnek, Bazel örnekleri deposunda bulunmaktadır.

BUILD.bazel dosyasında android_binary, android_library ve cc_library kurallarıyla üç hedef tanımlanmıştır.

APK'yı üst düzey android_binary hedefi oluşturur.

cc_library hedefi, JNI işlevi uygulamasına sahip tek bir C++ kaynak dosyası içerir:

#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 hedefi; Java kaynaklarını, kaynak dosyalarını ve bir cc_library hedefine bağımlılığı belirtir. Bu örnekte MainActivity.java, libapp.so paylaşılan nesne dosyasını yükler ve JNI işlevi için yöntem imzasını tanımlar:

public class MainActivity extends AppCompatActivity {

    static {
        System.loadLibrary("app");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
       // ...
    }

    public native String stringFromJNI();

}

STL'yi yapılandırma

C++ STL'yi yapılandırmak için --android_crosstool_top işaretini kullanın.

bazel build //:app --android_crosstool_top=target label

@androidndk içinde kullanılabilen STL'ler:

STL Hedef etiket
STLport @androidndk//:toolchain-stlport
libc++ @androidndk//:toolchain-libcpp
Gnustl @androidndk//:toolchain-gnu-libstdcpp

r16 ve önceki sürümler için varsayılan STL gnustl'dir. r17 ve sonraki sürümlerde libc++. Kolaylık sağlaması açısından, @androidndk//:default_crosstool hedefi ilgili varsayılan STL'lerle eşleştirilmektedir.

18 sürümünden itibaren STLport ve gnustl'ın kaldırılacağını, dolayısıyla libc++ ürününün NDK'daki tek STL olacağını lütfen unutmayın.

Bu STL'ler hakkında daha fazla bilgi için NDK belgelerini inceleyin.

Hedef ABI'yı yapılandırma

Hedef ABI'yı yapılandırmak için --fat_apk_cpu işaretini aşağıdaki şekilde kullanın:

bazel build //:app --fat_apk_cpu=comma-separated list of ABIs

Varsayılan olarak Bazel, armeabi-v7a için yerel Android kodu oluşturur. x86 için derlemek (örneğin emülatörlerde) için --fat_apk_cpu=x86 değerini iletin. Birden fazla mimari için yağ APK'sı oluşturmak isterseniz birden fazla CPU belirtebilirsiniz: --fat_apk_cpu=armeabi-v7a,x86.

Birden fazla ABI belirtilirse Bazel, her ABI için paylaşılan nesne içeren bir APK oluşturur.

NDK düzeltmesine ve Android API düzeyine bağlı olarak aşağıdaki ABI'ler kullanılabilir:

NDK düzeltmesi ABI'lar
16 ve altı armeabi, armeabi-v7a, arm64-v8a, mips, mips64, x86, x86_64
17 ve üzeri armeabi-v7a, arm64-v8a, x86, x86_64

Bu ABI'ler hakkında daha fazla bilgi için NDK dokümanlarına bakın.

Çoklu ABI Yağ APK'ları, APK'nın boyutunu artırdığından sürüm derlemeleri için önerilmez ancak geliştirme ve QA derlemeleri için yararlı olabilir.

C++ standardı seçme

C++ standardına göre derleme yapmak için aşağıdaki flag'leri kullanın:

C++ Standart İşaret
C++98 Varsayılan, işaret gerekmez
C++11 --cxxopt=-std=c++11
C++14 --cxxopt=-std=c++14

Örneğin:

bazel build //:app --cxxopt=-std=c++11

Kullanıcı Kılavuzu'nda derleyici ve bağlayıcı işaretlerini --cxxopt, --copt ve --linkopt ile iletme hakkında daha fazla bilgi edinin.

Derleyici ve bağlayıcı işaretleri, copts ve linkopts kullanılarak cc_library içinde özellik olarak da belirtilebilir. Örneğin:

cc_library(
    name = "jni_lib",
    srcs = ["cpp/native-lib.cpp"],
    copts = ["-std=c++11"],
    linkopts = ["-ldl"], # link against libdl
)

Platformlar ve araç zincirleriyle entegrasyon

Bazel'in yapılandırma modeli platformlar ve araç zincirlerine taşınıyor. Derlemenizde, derleme yapılacak mimariyi veya işletim sistemini seçmek için --platforms işaretini kullanıyorsa NDK'yı kullanmak için --extra_toolchains işaretini Bazel'a iletmeniz gerekir.

Örneğin, Go kuralları tarafından sağlanan android_arm64_cgo araç zinciriyle entegrasyon yapmak için --platforms işaretine ek olarak --extra_toolchains=@androidndk//:all değerini de iletin.

bazel build //my/cc:lib \
  --platforms=@io_bazel_rules_go//go/toolchain:android_arm64_cgo \
  --extra_toolchains=@androidndk//:all

Bunları doğrudan WORKSPACE dosyasına da kaydedebilirsiniz:

android_ndk_repository(name = "androidndk")
register_toolchains("@androidndk//:all")

Bu araç zincirlerinin kaydedilmesi, Bazel'e mimari ve işletim sistemi kısıtlamalarını çözümlerken onları NDK BUILD dosyasında (NDK 20 için) aramasını söyler:

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",
)

İşleyiş şekli: Android yapılandırma geçişleri tanıtımı

android_binary kuralı, Bazel'dan bağımlılıklarını Android uyumlu bir yapılandırmada oluşturmasını açık bir şekilde isteyebilir. Böylece, Bazel derlemesi ABI ve STL yapılandırması için --fat_apk_cpu ve --android_crosstool_top hariç herhangi bir özel işaret olmadan çalışmak için kullanılır.

Bu otomatik yapılandırma, perde arkasında Android yapılandırma geçişlerini kullanır.

android_binary gibi uyumlu bir kural, bağımlılarının yapılandırmasını otomatik olarak bir Android yapılandırmasıyla değiştirir. Bu nedenle, derlemenin yalnızca Android'e özgü alt ağaçları etkilenir. Derleme grafiğinin diğer bölümleri, üst düzey hedef yapılandırması kullanılarak işlenir. Derleme grafiğinde bir hedefi destekleyen yollar varsa her iki yapılandırmada da tek bir hedefi işleyebilir.

Bazel, en üst düzeyde veya daha üst düzeydeki bir geçiş noktası nedeniyle Android ile uyumlu bir yapılandırmaya sahip olduğunda, karşılaşılan ek geçiş noktaları yapılandırmada başka değişiklik yapmaz.

Android yapılandırmasına geçişi tetikleyen tek yerleşik konum android_binary ürününün deps özelliğidir.

Örneğin, herhangi bir işaret olmadan cc_library bağımlılığına sahip bir android_library hedefi oluşturmaya çalışırsanız eksik bir JNI üstbilgisiyle ilgili hatayla karşılaşabilirsiniz:

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.

İdeal olarak, bu otomatik geçişler Bazel'in çoğu durumda doğru şeyi yapmasını sağlamalıdır. Bununla birlikte, belirli bir cc_library testini test eden C++ geliştiricileri gibi Bazel komut satırındaki hedef zaten bu geçiş kurallarından herhangi birinin altındaysa özel bir --crosstool_top kullanılmalıdır.

android_binary kullanmadan Android için cc_library oluşturma

android_binary kullanmadan Android için bağımsız bir cc_binary veya cc_library oluşturmak isterseniz --crosstool_top, --cpu ve --host_crosstool_top işaretlerini kullanın.

Örneğin:

bazel build //my/cc/jni:target \
      --crosstool_top=@androidndk//:default_crosstool \
      --cpu=<abi> \
      --host_crosstool_top=@bazel_tools//tools/cpp:toolchain

Bu örnekte, üst düzey cc_library ve cc_binary hedefleri NDK araç zinciri kullanılarak oluşturulmuştur. Ancak bu, ana makine araç zincirinin hedef araç zincirinden kopyalanmasından dolayı Bazel'in kendi ana makine araçlarının NDK araç zinciriyle (ve dolayısıyla Android için) derlenmesine neden olur. Bu sorunu çözmek için --host_crosstool_top değerini @bazel_tools//tools/cpp:toolchain olarak belirtin ve ana makinenin C++ araç zincirini açıkça ayarlayın.

Bu yaklaşımda tüm yapı ağacı etkilenir.

Bu işaretler, project/.bazelrc içinde bir bazelrc yapılandırmasına (her ABI için bir adet) yerleştirilebilir:

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

Ardından, örneğin x86 için bir cc_library oluşturmak üzere şu komutu çalıştırın:

bazel build //my/cc/jni:target --config=android_x86

Genel olarak, bu yöntemi cc_library gibi alt düzey hedefler için veya ne oluşturduğunuzu tam olarak bildiğiniz durumlarda kullanın. Kontrol etmediğiniz çok sayıda hedef oluşturmayı beklediğiniz üst düzey hedefler için android_binary tarafından yapılan otomatik yapılandırma geçişlerini kullanın.