अगर आपने हाल ही में Bazel का इस्तेमाल शुरू किया है, तो कृपया Bazel के ट्यूटोरियल की मदद से, Android डिवाइस बनाना शुरू करें.
खास जानकारी
Bazel कई अलग-अलग बिल्ड कॉन्फ़िगरेशन में चल सकता है. इनमें कई ऐसे कॉन्फ़िगरेशन शामिल हैं जो Android नेटिव डेवलपमेंट किट (एनडीके) टूलचेन का इस्तेमाल करते हैं. इसका मतलब है कि Android के लिए सामान्य cc_library
और cc_binary
नियमों को सीधे Bazel में इकट्ठा किया जा सकता है. Bazel, android_ndk_repository
रिपॉज़िटरी
नियम का इस्तेमाल करके यह काम करता है.
ज़रूरी शर्तें
कृपया पक्का करें कि आपने Android SDK टूल और एनडीके इंस्टॉल किया हो.
SDK टूल और एनडीके सेट अप करने के लिए, अपने 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
नियम के बारे में ज़्यादा जानकारी के लिए, बिल्ड
इनसाइक्लोपीडिया एंट्री देखें.
तुरंत शुरू करने का तरीका
अगर आपको 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
एबीआई के लिए टारगेट किया गया है. इसे बदलने या एक साथ कई एबीआई के
बिल बनाने के लिए, टारगेट एबीआई को कॉन्फ़िगर
करने वाला सेक्शन देखें.
सेटअप का उदाहरण
यह उदाहरण Bazel के उदाहरण स्टोर करने की जगह में उपलब्ध है.
BUILD.bazel
फ़ाइल में, android_binary
,
android_library
, और cc_library
नियमों के साथ तीन टारगेट तय किए गए हैं.
android_binary
टॉप-लेवल टारगेट, APK बनाता है.
cc_library
टारगेट में एक C++ सोर्स फ़ाइल होती है, जिसमें JNI फ़ंक्शन लागू होता है:
#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++ एसटीएल को कॉन्फ़िगर करने के लिए, --android_crosstool_top
फ़्लैग का इस्तेमाल करें.
bazel build //:app --android_crosstool_top=target label
@androidndk
में उपलब्ध एसटीएल:
STL | टारगेट लेबल |
---|---|
STLport | @androidndk//:toolchain-stlport |
libc++ | @androidndk//:toolchain-libcpp |
गनस्टल | @androidndk//:toolchain-gnu-libstdcpp |
r16 और इससे पहले के वर्शन के लिए, डिफ़ॉल्ट एसटीएल gnustl
है. R17 और उससे ज़्यादा वर्शन के लिए, यह
libc++
है. आपकी सुविधा के लिए, टारगेट @androidndk//:default_crosstool
को
डिफ़ॉल्ट एसटीएल से तय किया गया है.
कृपया ध्यान दें कि r18 के बाद से, STLport और gnustl को
हटा दिया जाएगा.
इससे, एनडीके में सिर्फ़ libc++
को एसटीएल माना जाएगा.
इन एसटीएल के बारे में ज़्यादा जानकारी के लिए, एनडीके का दस्तावेज़ देखें.
टारगेट एबीआई को कॉन्फ़िगर करना
टारगेट एबीआई को कॉन्फ़िगर करने के लिए, --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
.
एक से ज़्यादा एबीआई तय करने पर, Bazel हर एबीआई के लिए एक ऐसा APK बनाएगा जिसमें शेयर किया गया ऑब्जेक्ट होगा.
एनडीके में हुए बदलाव और Android एपीआई लेवल के आधार पर, ये एबीआई उपलब्ध होते हैं:
NDK संशोधन | एबीआई |
---|---|
16 और उससे कम | आर्मेबी, Armeabi-v7a, Arm64-v8a, mips, mips64, x86, x86_64 |
17 साल और उससे ज़्यादा उम्र के बच्चों के लिए | Armeabi-v7a, Arm64-v8a, x86, x86_64 |
इन एबीआई के बारे में ज़्यादा जानकारी के लिए, एनडीके के दस्तावेज़ देखें.
एक से ज़्यादा एबीआई फ़ैट 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
फ़्लैग का इस्तेमाल किया जाता है, तो एनडीके का इस्तेमाल करने के लिए आपको Bazel को --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 को आर्किटेक्चर और ऑपरेटिंग सिस्टम से जुड़ी समस्याओं को हल करते समय एनडीके BUILD
फ़ाइल (एनडीके 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
टारगेट बनाने की कोशिश की जाती है, तो आपको जेएनआई हेडर मौजूद न होने से जुड़ी गड़बड़ी का सामना करना पड़ सकता है:
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
टारगेट, एनडीके टूलचेन का इस्तेमाल करके बनाए गए हैं. हालांकि, इसकी वजह से Bazel के होस्ट टूल, एनडीके टूलचेन (और Android के लिए) की मदद से बनाए गए हैं, क्योंकि होस्ट टूलचेन को टारगेट टूलचेन से कॉपी किया गया है. इससे बचने के लिए, होस्ट के C++ टूलचेन को साफ़ तौर पर सेट करने के लिए, --host_crosstool_top
की वैल्यू को @bazel_tools//tools/cpp:toolchain
पर सेट करें.
इस तरीके से पूरा बिल्ड ट्री प्रभावित होता है.
इन फ़्लैग को project/.bazelrc
में, 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
के लिए cc_library
बनाने के लिए, उदाहरण के लिए, इसे चलाएं:
bazel build //my/cc/jni:target --config=android_x86
आम तौर पर, यह तरीका लो-लेवल टारगेट (जैसे कि cc_library
) के लिए इस्तेमाल करें या जब आपको पता हो कि आप क्या बना रहे हैं, तो हाई-लेवल टारगेट के लिए android_binary
के अपने-आप होने वाले कॉन्फ़िगरेशन ट्रांज़िशन पर भरोसा करें, जहां आपको ऐसे बहुत सारे टारगेट बनाने की उम्मीद है जिन पर आपका कंट्रोल नहीं है.