अगर आपने Bazel का इस्तेमाल पहले कभी नहीं किया है, तो कृपया Bazel की मदद से Android ऐप्लिकेशन बनाना ट्यूटोरियल से शुरुआत करें.
खास जानकारी
Bazel को कई अलग-अलग बिल्ड कॉन्फ़िगरेशन में चलाया जा सकता है. इनमें से कई, Android Native Development Kit (NDK) टूलचेन का इस्तेमाल करते हैं. इसका मतलब है कि Android के लिए, सामान्य cc_library
और cc_binary
नियमों को सीधे Bazel में कंपाइल किया जा सकता है. Bazel, android_ndk_repository
रिपॉज़िटरी के नियम का इस्तेमाल करके ऐसा करता है.
ज़रूरी शर्तें
कृपया पक्का करें कि आपने Android SDK और NDK इंस्टॉल किया हो.
एसडीके और एनडीके सेट अप करने के लिए, अपने WORKSPACE
में यह स्निपेट जोड़ें:
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
नियम के बारे में ज़्यादा जानने के लिए, Build Encyclopedia एंट्री देखें.
तुरंत शुरू करना
Android के लिए C++ बनाने के लिए, बस अपनी android_binary
या android_library
नियमों में cc_library
डिपेंडेंसी जोड़ें.
उदाहरण के लिए, Android ऐप्लिकेशन के लिए यहां दी गई BUILD
फ़ाइल देखें:
# 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",
)
इस BUILD
फ़ाइल से, यह टारगेट ग्राफ़ मिलता है:
पहली इमेज. cc_library डिपेंडेंसी के साथ Android प्रोजेक्ट का ग्राफ़ बनाता है.
ऐप्लिकेशन बनाने के लिए, यह कमांड चलाएं:
bazel build //app/src/main:app
bazel build
कमांड, Java फ़ाइलों, Android रिसॉर्स फ़ाइलों, और cc_library
नियमों को कंपाइल करती है. साथ ही, इन सभी को एक 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, सभी cc_libraries को एक ही शेयर किए गए ऑब्जेक्ट (.so
) फ़ाइल में कंपाइल करता है. यह फ़ाइल, डिफ़ॉल्ट रूप से armeabi-v7a
ABI के लिए टारगेट की जाती है. इसे बदलने या एक ही समय में कई एबीआइ के लिए बनाने के लिए, टारगेट एबीआइ को कॉन्फ़िगर करना सेक्शन देखें.
सेटअप का उदाहरण
यह उदाहरण, Bazel के उदाहरणों वाली रिपॉज़िटरी में उपलब्ध है.
BUILD.bazel
फ़ाइल में, android_binary
, android_library
, और cc_library
नियमों के साथ तीन टारगेट तय किए गए हैं.
android_binary
टॉप-लेवल टारगेट, APK बनाता है.
cc_library
टारगेट में, JNI फ़ंक्शन को लागू करने वाली एक C++ सोर्स फ़ाइल शामिल होती है:
#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
टारगेट, Java सोर्स, संसाधन फ़ाइलों, और cc_library
टारगेट पर निर्भरता के बारे में बताता है. इस उदाहरण में, MainActivity.java
शेयर की गई ऑब्जेक्ट फ़ाइल libapp.so
को लोड करता है. साथ ही, JNI फ़ंक्शन के लिए, तरीके के सिग्नेचर को तय करता है:
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("app");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
}
public native String stringFromJNI();
}
एसटीएल को कॉन्फ़िगर करना
C++ STL को कॉन्फ़िगर करने के लिए, --android_crosstool_top
फ़्लैग का इस्तेमाल करें.
bazel build //:app --android_crosstool_top=target label
@androidndk
में ये एसटीएल उपलब्ध हैं:
STL | टारगेट लेबल |
---|---|
STLport | @androidndk//:toolchain-stlport |
libc++ | @androidndk//:toolchain-libcpp |
gnustl | @androidndk//:toolchain-gnu-libstdcpp |
r16 और इससे पहले के वर्शन के लिए, डिफ़ॉल्ट STL gnustl
है. r17 और इसके बाद के वर्शन के लिए, यह libc++
है. आसानी के लिए, टारगेट @androidndk//:default_crosstool
को
संबंधित डिफ़ॉल्ट एसटीएल के तौर पर भी इस्तेमाल किया जा सकता है.
कृपया ध्यान दें कि r18 से, STLport और gnustl को हटा दिया जाएगा. इसलिए, NDK में सिर्फ़ libc++
STL उपलब्ध होगा.
इन एसटीएल के बारे में ज़्यादा जानने के लिए, NDK के दस्तावेज़ देखें.
टारगेट किए गए ABI को कॉन्फ़िगर करना
टारगेट एबीआई को कॉन्फ़िगर करने के लिए, --fat_apk_cpu
फ़्लैग का इस्तेमाल इस तरह करें:
bazel build //:app --fat_apk_cpu=comma-separated list of ABIs
डिफ़ॉल्ट रूप से, Bazel armeabi-v7a
के लिए नेटिव Android कोड बनाता है. x86 के लिए (जैसे, एम्युलेटर के लिए) बनाने के लिए, --fat_apk_cpu=x86
पास करें. एक से ज़्यादा आर्किटेक्चर के लिए फ़ैट APK बनाने के लिए, एक से ज़्यादा सीपीयू तय किए जा सकते हैं: --fat_apk_cpu=armeabi-v7a,x86
.
अगर एक से ज़्यादा ABI तय किए गए हैं, तो Bazel एक ऐसा APK बनाएगा जिसमें हर ABI के लिए एक शेयर किया गया ऑब्जेक्ट होगा.
एनडीके के वर्शन और Android एपीआई लेवल के हिसाब से, ये एबीआइ उपलब्ध हैं:
एनडीके का वर्शन | एबीआई |
---|---|
16 साल और इससे कम उम्र के लोगों के लिए | armeabi, armeabi-v7a, arm64-v8a, mips, mips64, x86, x86_64 |
17 साल और इससे ज़्यादा | armeabi-v7a, arm64-v8a, x86, x86_64 |
इन एबीआइ के बारे में ज़्यादा जानने के लिए, NDK के दस्तावेज़ देखें.
रिलीज़ बिल्ड के लिए, मल्टी-एबीआई फ़ैट APK इस्तेमाल करने का सुझाव नहीं दिया जाता, क्योंकि इससे APK का साइज़ बढ़ जाता है. हालांकि, डेवलपमेंट और QA बिल्ड के लिए इनका इस्तेमाल किया जा सकता है.
C++ स्टैंडर्ड चुनना
C++ स्टैंडर्ड के मुताबिक बनाने के लिए, इन फ़्लैग का इस्तेमाल करें:
C++ स्टैंडर्ड | झंडा |
---|---|
C++98 | डिफ़ॉल्ट, किसी फ़्लैग की ज़रूरत नहीं है |
C++11 | --cxxopt=-std=c++11 |
C++14 | --cxxopt=-std=c++14 |
उदाहरण के लिए:
bazel build //:app --cxxopt=-std=c++11
उपयोगकर्ता गाइड में, --cxxopt
, --copt
, और --linkopt
की मदद से कंपाइलर और लिंकर फ़्लैग पास करने के बारे में ज़्यादा पढ़ें.
कंपाइलर और लिंकर फ़्लैग को भी cc_library
में एट्रिब्यूट के तौर पर तय किया जा सकता है. इसके लिए, copts
और linkopts
का इस्तेमाल करें. उदाहरण के लिए:
cc_library(
name = "jni_lib",
srcs = ["cpp/native-lib.cpp"],
copts = ["-std=c++11"],
linkopts = ["-ldl"], # link against libdl
)
प्लैटफ़ॉर्म और टूलचेन के साथ इंटिग्रेशन
Bazel का कॉन्फ़िगरेशन मॉडल, प्लैटफ़ॉर्म और टूलचेन की ओर बढ़ रहा है. अगर आपके बिल्ड में, आर्किटेक्चर या ऑपरेटिंग सिस्टम को चुनने के लिए --platforms
फ़्लैग का इस्तेमाल किया जाता है, तो आपको NDK का इस्तेमाल करने के लिए, Bazel को --platforms
फ़्लैग पास करना होगा.--extra_toolchains
उदाहरण के लिए, Go के नियमों के तहत उपलब्ध कराई गई android_arm64_cgo
टूलचेन के साथ इंटिग्रेट करने के लिए, --platforms
फ़्लैग के साथ-साथ --extra_toolchains=@androidndk//:all
पास करें.
bazel build //my/cc:lib \
--platforms=@io_bazel_rules_go//go/toolchain:android_arm64_cgo \
--extra_toolchains=@androidndk//:all
इन्हें सीधे WORKSPACE
फ़ाइल में भी रजिस्टर किया जा सकता है:
android_ndk_repository(name = "androidndk")
register_toolchains("@androidndk//:all")
इन टूलचेन को रजिस्टर करने से, Bazel को आर्किटेक्चर और ऑपरेटिंग सिस्टम की पाबंदियों को हल करते समय, NDK BUILD
फ़ाइल (NDK 20 के लिए) में उन्हें खोजने के लिए कहा जाता है:
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",
)
यह कैसे काम करता है: Android कॉन्फ़िगरेशन ट्रांज़िशन की जानकारी
android_binary
नियम, Bazel से साफ़ तौर पर यह अनुरोध कर सकता है कि वह अपनी डिपेंडेंसी को Android के साथ काम करने वाले कॉन्फ़िगरेशन में बनाए, ताकि Bazel बिल्ड बिना किसी खास फ़्लैग के काम करे. हालांकि, एबीआई और एसटीएल कॉन्फ़िगरेशन के लिए --fat_apk_cpu
और --android_crosstool_top
फ़्लैग का इस्तेमाल किया जा सकता है.
यह अपने-आप कॉन्फ़िगर होने की सुविधा, Android कॉन्फ़िगरेशन ट्रांज़िशन का इस्तेमाल करती है.
android_binary
जैसे काम करने वाले नियम, अपनी डिपेंडेंसी के कॉन्फ़िगरेशन को Android कॉन्फ़िगरेशन में अपने-आप बदल देते हैं. इसलिए, बिल्ड के सिर्फ़ Android से जुड़े सबट्री पर असर पड़ता है. बिल्ड ग्राफ़ के अन्य हिस्सों को टॉप-लेवल के टारगेट कॉन्फ़िगरेशन का इस्तेमाल करके प्रोसेस किया जाता है. अगर बिल्ड ग्राफ़ में ऐसा करने के लिए पाथ मौजूद हैं, तो यह दोनों कॉन्फ़िगरेशन में एक ही टारगेट को प्रोसेस कर सकता है.
Bazel को Android के साथ काम करने वाले कॉन्फ़िगरेशन में सेट करने के बाद, कॉन्फ़िगरेशन में कोई बदलाव नहीं किया जा सकता. ऐसा तब होता है, जब कॉन्फ़िगरेशन को टॉप लेवल पर सेट किया गया हो या ट्रांज़िशन पॉइंट की वजह से सेट किया गया हो.
सिर्फ़ android_binary
का deps
एट्रिब्यूट, Android कॉन्फ़िगरेशन पर ट्रांज़िशन को ट्रिगर करने वाली इन-बिल्ट जगह है.
उदाहरण के लिए, अगर आपको बिना किसी फ़्लैग के cc_library
डिपेंडेंसी के साथ android_library
टारगेट बनाना है, तो आपको JNI हेडर मौजूद न होने से जुड़ी गड़बड़ी दिख सकती है:
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.
आमतौर पर, इन ऑटोमैटिक ट्रांज़िशन की वजह से, Bazel ज़्यादातर मामलों में सही तरीके से काम करता है. हालांकि, अगर Bazel कमांड-लाइन पर टारगेट पहले से ही इनमें से किसी ट्रांज़िशन नियम के तहत है, जैसे कि C++ डेवलपर किसी खास cc_library
की जांच कर रहे हैं, तो कस्टम --crosstool_top
का इस्तेमाल करना होगा.
android_binary
का इस्तेमाल किए बिना, Android के लिए cc_library
बनाना
android_binary
का इस्तेमाल किए बिना, Android के लिए स्टैंडअलोन cc_binary
या cc_library
बनाने के लिए, --crosstool_top
, --cpu
, और --host_crosstool_top
फ़्लैग का इस्तेमाल करें.
उदाहरण के लिए:
bazel build //my/cc/jni:target \
--crosstool_top=@androidndk//:default_crosstool \
--cpu=<abi> \
--host_crosstool_top=@bazel_tools//tools/cpp:toolchain
इस उदाहरण में, टॉप-लेवल cc_library
और cc_binary
टारगेट, NDK टूलचेन का इस्तेमाल करके बनाए गए हैं. हालांकि, इससे Bazel के होस्ट टूल, NDK टूलचेन (और इस तरह Android के लिए) के साथ बनाए जाते हैं. ऐसा इसलिए होता है, क्योंकि होस्ट टूलचेन को टारगेट टूलचेन से कॉपी किया जाता है. इस समस्या को हल करने के लिए, --host_crosstool_top
की वैल्यू को @bazel_tools//tools/cpp:toolchain
पर सेट करें, ताकि होस्ट के C++ टूलचेन को साफ़ तौर पर सेट किया जा सके.
इस तरीके से, पूरे बिल्ड ट्री पर असर पड़ता है.
इन फ़्लैग को bazelrc
config (हर ABI के लिए एक) में रखा जा सकता है. इसके लिए, 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
इसके बाद, x86
के लिए x86
बनाने के लिए, उदाहरण के तौर पर यह कमांड चलाएं:cc_library
bazel build //my/cc/jni:target --config=android_x86
आम तौर पर, इस तरीके का इस्तेमाल छोटे लेवल के टारगेट (जैसे, cc_library
) के लिए करें. इसके अलावा, इसका इस्तेमाल तब करें, जब आपको पता हो कि आपको क्या बनाना है. बड़े लेवल के टारगेट के लिए, android_binary
से ऑटोमैटिक कॉन्फ़िगरेशन ट्रांज़िशन पर भरोसा करें. ऐसा तब करें, जब आपको ऐसे कई टारगेट बनाने हों जिन्हें कंट्रोल नहीं किया जा सकता.