Bazel'i kullanmaya yeni başladıysanız lütfen Bazel ile Android geliştirme eğitimiyle 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 Android için doğrudan Bazel'de derlenebileceği anlamına gelir. Bazel, bunu android_ndk_repository
depo kuralını kullanarak yapar.
Ö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 Build Ansiklopedisi girişine bakın.
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 grafiği oluşturur:
Şekil 1. cc_library bağımlılıkları olan Android projesinin grafiğini oluşturun.
Uygulamayı derlemek için şunları ç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ı derleyip her şeyi bir APK'ya 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'si için hedeflenen tek bir paylaşılan nesne (.so
) dosyasında derleyebilir. 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 yer almaktadır.
BUILD.bazel
dosyasında android_binary
, android_library
ve cc_library
kurallarıyla üç hedef tanımlanmıştır.
android_binary
üst düzey hedefi APK'yı oluşturur.
cc_library
hedefi, JNI işlev uygulamasını 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
hedefine olan bağımlılığı belirtir. Bu örnekte MainActivity.java
, paylaşılan nesne dosyasını libapp.so
yükler ve JNI işlevinin 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
'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ümlerde varsayılan STL gnustl
'tür. r17 ve sonraki sürümler için libc++
değerini kullanın. Kolaylık sağlamak için hedef @androidndk//:default_crosstool
, ilgili varsayılan STL'lere takma ad olarak 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.
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 derlemek istiyorsanız (ör. emülatörler için) --fat_apk_cpu=x86
değerini iletin. Birden fazla mimari için büyük 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 sürümü | ABI'lar |
---|---|
16 yaş ve altı | armeabi, armeabi-v7a, arm64-v8a, mips, mips64, x86, x86_64 |
17 yaş ve üstü | armeabi-v7a, arm64-v8a, x86, x86_64 |
Bu ABI'ler hakkında daha fazla bilgi için NDK belgelerine bakın.
Çoklu ABI'li büyük APK'lar, APK'nın boyutunu artırdığından sürüm derlemeleri için önerilmez ancak geliştirme ve kalite kontrol derlemeleri için yararlı olabilir.
C++ standardı seçme
C++ standardına göre derlemek 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
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, 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 doğru ilerliyor. Derlemeniz, derleme için mimariyi veya işletim sistemini seçmek üzere --platforms
işaretini kullanıyorsa NDK'yı kullanmak için --extra_toolchains
işaretini Bazel'e iletmeniz gerekir.
Ö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, mimari ve işletim sistemi kısıtlamalarını çözerken bunları NDK BUILD
dosyasında (NDK 20 için) arayacaktır:
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şlerini tanıtıyoruz
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.
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ılıkları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, üst düzey hedef yapılandırması kullanılarak işlenir. Oluşturma grafiğinde bunu destekleyen yollar varsa her iki yapılandırmada da tek bir hedefi işleyebilir.
Bazel, üst düzeyde veya daha üst düzey bir geçiş noktası nedeniyle Android uyumlu bir yapılandırmada olduğunda, karşılaşılan ek geçiş noktaları yapılandırmada başka bir 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
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, ana makine araç zinciri hedef araç zincirinden kopyalandığı için Bazel'in kendi ana makine araçlarının NDK araç zinciriyle (ve dolayısıyla Android için) oluşturulmasına neden olur. Bu sorunu gidermek için barındırıcının C++ araç zincirini açıkça ayarlamak üzere --host_crosstool_top
değerini @bazel_tools//tools/cpp:toolchain
olarak belirtin.
Bu yaklaşımda derleme ağacının tamamı etkilenir.
Bu işaretler, bazelrc
yapılandırmasına (her ABI için bir tane) project/.bazelrc
aşağıdaki şekilde eklenebilir:
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.