Bazel ile Android Yerel Geliştirme Kiti'ni kullanma

Sorun bildir Kaynağı görüntüle Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

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

Genel Bakış

Bazel, Android Native Development Kit (NDK) araç zincirini kullananlar da dahil olmak üzere birçok farklı derleme yapılandırmasında çalışabilir. Bu, normal cc_library ve cc_binary kurallarının doğrudan Bazel'de Android için 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 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 Build Encyclopedia entry başlıklı makaleyi inceleyin.

Android NDK'nın yeni bir sürümünü (r22 ve sonrası) kullanıyorsanız android_ndk_repository için Starlark uygulamasını kullanın. README dosyasındaki talimatları uygulayın.

Hızlı başlangıç

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

Örneğin, bir Android uygulaması için aşağıdaki BUILD dosyası verildiğinde:

# 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 grafiği oluşturur:

Örnek sonuçlar

1.şekil cc_library bağımlılıklarıyla Android projesinin grafiğini oluşturun.

Uygulamayı oluşturmak için şu komutu çalıştırmanız yeterlidir:

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'da 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'i 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 örneği Bazel örnekleri deposunda bulabilirsiniz.

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

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

cc_library hedefi, JNI işlevi uygulaması içeren 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 cc_library hedefindeki bağımlılığı belirtir. Bu örnekte, MainActivity.java, paylaşılan nesne dosyasını libapp.so 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'da kullanılabilen STL'ler şunlardır:

STL Hedef etiketi
STLport @androidndk//:toolchain-stlport
libc++ @androidndk//:toolchain-libcpp
gnustl @androidndk//:toolchain-gnu-libstdcpp

r16 ve önceki sürümlerde varsayılan STL gnustl'dır. r17 ve sonraki sürümlerde libc++'dır. Kolaylık sağlamak için hedef @androidndk//:default_crosstool, ilgili varsayılan STL'ler olarak adlandırılır.

r18'den itibaren STLport ve gnustl'nin kaldırılacağını ve libc++'nin NDK'daki tek STL olacağını lütfen unutmayın.

Bu STL'ler hakkında daha fazla bilgi için NDK dokümanları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

Bazel, varsayılan olarak armeabi-v7a için yerel Android kodu oluşturur. x86 için (ör. emülatörler için) derleme yapmak istiyorsanız --fat_apk_cpu=x86 iletin. Birden fazla mimari için kalın bir APK oluşturmak istiyorsanız birden fazla CPU belirtebilirsiniz: --fat_apk_cpu=armeabi-v7a,x86.

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

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

NDK düzeltmesi ABI'lar
16 ve daha düşük armeabi, armeabi-v7a, arm64-v8a, mips, mips64, x86, x86_64
17 yaş 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 Fat APK'lar, APK'nın boyutunu artırdığı için yayın derlemeleri için önerilmez ancak geliştirme ve kalite güvencesi derlemeleri için yararlı olabilir.

C++ standardı seçme

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

C++ Standard İş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

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

Derleyici ve bağlayıcı işaretleri, cc_library içinde copts ve linkopts kullanılarak ö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 platformlara ve araç zincirlerine doğru ilerliyor. Derlemeniz, derlenecek mimariyi veya işletim sistemini seçmek için --platforms işaretini kullanıyorsa NDK'yı kullanmak için --platforms işaretini Bazel'e iletmeniz gerekir.--extra_toolchains

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

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

Ayrıca bunları doğrudan WORKSPACE dosyasına da kaydedebilirsiniz:

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

Bu araç zincirlerini kaydettiğinizde Bazel, mimari ve işletim sistemi kısıtlamalarını çözerken bunları NDK BUILD dosyasında (NDK 20 için) arar:

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şlerine giriş

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

Bu otomatik yapılandırma, arka planda Android yapılandırma geçişlerini kullanır.

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

Bazel, Android ile uyumlu bir yapılandırmaya girdikten sonra (üst düzeyde belirtildiği veya daha üst düzey bir geçiş noktası nedeniyle) karşılaşılan ek geçiş noktaları yapılandırmayı daha fazla değiştirmez.

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

Örneğin, herhangi bir işaret olmadan cc_library bağımlılığı olan bir android_library hedefi 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 çoğu durumda Bazel'in doğru şeyi yapmasını sağlamalıdır. Ancak Bazel komut satırındaki hedef zaten bu geçiş kurallarından 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

android_binary kullanmadan Android için bağımsız bir cc_binary veya cc_library oluşturmak istiyorsanız --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şturulur. Ancak bu durum, ana makine araç zinciri hedef araç zincirinden kopyalandığı için Bazel'in kendi ana makine araçlarının NDK araç zinciriyle (dolayısıyla Android için) oluşturulmasına neden olur. Bu sorunu çözmek için --host_crosstool_top değerini @bazel_tools//tools/cpp:toolchain olarak belirtin. Böylece, ana makinenin C++ araç zinciri açıkça ayarlanır.

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

Bu işaretler, bazelrc yapılandırmasına (her ABI için bir tane) 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 cc_library için x86 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üzeyli hedefler (ör. cc_library) için veya tam olarak ne oluşturduğunuzu bildiğinizde kullanın. Kontrol etmediğiniz birçok hedef oluşturmayı beklediğiniz yüksek düzeyli hedefler için android_binary'den otomatik yapılandırma geçişlerini kullanın.