Bazel ile Android Yerel Geliştirme Kiti'ni kullanma

Sorun bildirin Kaynağı göster Gece · 7,4 , 7.3 · 7,2 · 7,1 · 7,0 · 6,5

Bazel'i kullanmaya yeni başladıysanız lütfen ile Android Geliştirme Bazel eğitimi.

Genel Bakış

Bazel, birçok farklı derleme yapılandırmasında çalışabilir. Bunlardan bazıları, test edildi. Bu, normal verilerin cc_library ve cc_binary kuralları, Android için doğrudan Bazel. Bazel, bu işlemi android_ndk_repository deposunu kullanarak gerçekleştirir tıklayın.

Ön koşullar

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

SDK'yı ve NDK'yı ayarlamak için WORKSPACE dosyanı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 bkz. Oluşturma Ansiklopedi girişi.

Android NDK'nın son sürümünü (r22 ve sonraki sürümler) kullanıyorsanız android_ndk_repository için Starlark uygulamasını kullanın. README dosyasında yer alan talimatları uygulayın.

Hızlı başlangıç

Android için C++ derlemek istiyorsanız 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ı verilmiştir:

# 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 kural oluşturuyor ve her şeyi bir APK olarak 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 dosyalarını varsayılan olarak armeabi-v7a ABI için hedeflenen tek bir paylaşılan nesne (.so) dosyasında derleyebilir. Bunu değiştirmek veya için birden fazla ABI'yi kullanıyorsanız hedefi yapılandırma ABI değerleridir.

Örnek kurulum

Bu örnek, Bazel örneklerinde depo.

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şlevine sahip tek bir C++ kaynak dosyası içeriyor uygulama:

#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 cc_library hedefine olan bağımlılığı belirtir. Bu örnekte, MainActivity.java yüklenir libapp.so adlı paylaşılan nesne dosyasını kullanır ve JNI için yöntem imzasını tanımlar işlev:

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'te kullanılabilen STL'ler şunlardır:

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 üzeri sürümlerde libc++ Kolaylık sağlaması açısından hedef @androidndk//:default_crosstool diğer adı ilgili varsayılan STL'lere atanır.

r18'den itibaren STLport ve gnustl'nin kaldırılacağını, bu nedenle NDK'da yalnızca libc++ STL'sinin kullanılabileceğini lütfen unutmayın.

NDK'ya göz atın belgeleri 'na bakın.

Hedef ABI'yi yapılandırma

Hedef ABI'yi yapılandırmak için --fat_apk_cpu işaretini aşağıdaki gibi 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 istiyorsanız (ör. emülatörler için) --fat_apk_cpu=x86 değerini iletin. Birden fazla APK için yağ APK'sı birden fazla CPU belirtebilirsiniz: --fat_apk_cpu=armeabi-v7a,x86.

Birden fazla ABI belirtilirse Bazel, paylaşılan bir ABI içeren APK nesnesini ifade eder.

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

NDK sürümü ABI'lar
16 yaş 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 belgelerine bakın.

Çoklu ABI Yağ APK'ları, yayın derlemeleri için önerilmez çünkü APK'nın boyutunu değiştirebilir ancak geliştirme ve KG 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

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

Derleyici ve bağlayıcı işaretleri, cc_library özelliğinde özellik olarak da belirtilebilir. copts ve linkopts kullanılıyor. Ö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 doğru ilerliyor. Eğer derleme, mimari veya işletim sistemini seçmek için --platforms işaretini kullanır --extra_toolchains işaretini Bazel'e iletmeniz ve NDK'yı kullanmak için yeterli.

Örneğin, Go kuralları tarafından sağlanan android_arm64_cgo araç setiyle entegrasyon yapmak için --platforms işaretçisine ek olarak --extra_toolchains=@androidndk//:all işaretçisini 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ç zincirlerini kaydettiğinizde, Bazel bunları NDK'da BUILD dosyasını (NDK 20 için) kopyalayın:

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'den bağımlılıklarını Android uyumlu bir yapılandırmada derlemesini açıkça 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 sadece çalışır.

Perde arkasında bu otomatik yapılandırma, Android config değiştirilebilir.

android_binary gibi uyumlu bir kural, Android yapılandırmasına bağımlıların yapılandırılması. Bu nedenle, Derlemenin Android'e özgü alt ağaçları bu durumdan etkilenmektedir. Yapının diğer bölümleri grafiği, üst düzey hedef yapılandırması kullanılarak işlenir. Hatta bir sürü Bir hedefe giden yollar varsa her iki yapılandırmada da tek bir hedef işleme bir yapı grafiği oluşturacaksınız.

Bazel, veya daha üst düzeyde bir geçiş noktası nedeniyle yapılandırmada başka değişiklik yapmaz.

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

Örneğin, cc_library bağımlığı olan bir android_library hedefini herhangi bir işaret olmadan oluşturmaya çalışırsanız eksik JNI başlığıyla ilgili bir 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ğlar. Ancak Bazel komut satırındaki hedef, bu geçiş kurallarının herhangi birinin altındaysa (ör. C++ geliştiricileri belirli bir cc_library'ü test ediyorsa) özel bir --crosstool_top kullanılmalıdır.

android_binary kullanmadan Android için cc_library oluşturma

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

Ö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 derlenir. Ancak bu, Bazel'ın kendi barındırma araçlarının oluşturulmasına neden olur. olması nedeniyle, ana makine araç zincirinin hedef araç zincirinden kopyalanır. Bu sorunu çözmek için @bazel_tools//tools/cpp:toolchain olarak --host_crosstool_top ana makinenin C++ araç zincirini açık bir şekilde ayarlamanız gerekir.

Bu yaklaşımda derleme ağacının tamamı etkilenir.

Bu flag'ler bir bazelrc yapılandırmasına (her ABI için bir adet) yerleştirilebilir. 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

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 düşük düzey hedefler (cc_library gibi) için veya tam olarak ne oluşturduğunuzu bildiğiniz durumlarda kullanın. Kontrol etmediğiniz çok sayıda hedef oluşturmayı beklediğiniz yüksek düzey hedefler için android_binary'ten otomatik yapılandırma geçişlerine güvenin.