अगर आपके लिए Bazel नया है, तो कृपया Bazel की मदद से Android ऐप्लिकेशन बनाना ट्यूटोरियल से शुरुआत करें.
खास जानकारी
Bazel को कई अलग-अलग बिल्ड कॉन्फ़िगरेशन में चलाया जा सकता है. इनमें से कई, Android Native Development Kit (NDK) टूलचेन का इस्तेमाल करते हैं. इसका मतलब है कि Android के लिए, सामान्य cc_library
और cc_binary
नियमों को सीधे Bazel में कंपाइल किया जा सकता है. Bazel, android_ndk_repository
रिपॉज़िटरी के नियम और उससे जुड़े bzlmod एक्सटेंशन का इस्तेमाल करके ऐसा करता है.
Android के सामान्य कंपाइलेशन के लिए, rules_android
का इस्तेमाल करें.
इस ट्यूटोरियल में बताया गया है कि C++ लाइब्रेरी डिपेंडेंसी को Android ऐप्लिकेशन में कैसे इंटिग्रेट किया जाता है. साथ ही, इसमें NDK टूलचेन का पता लगाने और उसे रजिस्टर करने के लिए, rules_android_ndk
का इस्तेमाल किया गया है.
ज़रूरी शर्तें
कृपया पक्का करें कि आपने Android SDK और NDK इंस्टॉल किया हो.
NDK और SDK टूल सेटअप करना
बाहरी रिपॉज़िटरी का सेटअप, इस बात पर निर्भर करता है कि WORKSPACE या bzlmod (MODULE.bazel) का इस्तेमाल किया जा रहा है या नहीं. Bazel 7 या इसके बाद के वर्शन के लिए, Bzlmod को इस्तेमाल करने का सुझाव दिया जाता है. ध्यान दें कि MODULE.bazel और WORKSPACE सेटअप स्टैंज़ा, एक-दूसरे से अलग होते हैं. अगर किसी एक डिपेंडेंसी मैनेजमेंट सॉल्यूशन का इस्तेमाल किया जा रहा है, तो आपको दूसरे के लिए बॉयलरप्लेट जोड़ने की ज़रूरत नहीं है.
Bzlmod MODULE.bazel सेटअप
अपने MODULE.bazel फ़ाइल में यह स्निपेट जोड़ें:
# NDK
bazel_dep(name = "rules_android_ndk", version = "0.1.3")
android_ndk_repository_extension = use_extension("@rules_android_ndk//:extension.bzl", "android_ndk_repository_extension")
use_repo(android_ndk_repository_extension, "androidndk")
register_toolchains("@androidndk//:all")
# SDK
bazel_dep(name = "rules_android", version = "0.6.6")
register_toolchains(
"@rules_android//toolchains/android:android_default_toolchain",
"@rules_android//toolchains/android_sdk:android_sdk_tools",
)
android_sdk_repository_extension = use_extension("@rules_android//rules/android_sdk_repository:rule.bzl", "android_sdk_repository_extension")
use_repo(android_sdk_repository_extension, "androidsdk")
register_toolchains("@androidsdk//:sdk-toolchain", "@androidsdk//:all")
WORKSPACE का लेगसी सेटअप
अपनी WORKSPACE
फ़ाइल में यह स्निपेट जोड़ें:
load("@rules_android//rules:rules.bzl", "android_sdk_repository")
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.
)
load("@rules_android_ndk//:rules.bzl", "android_ndk_repository")
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.
)
WORKSPACE के साथ काम करने से जुड़ी अहम जानकारी:
rules_android
औरrules_android_ndk
, दोनों नियमों के लिए अतिरिक्त बॉयलरप्लेट की ज़रूरत होती है. हालांकि, ऊपर दिए गए WORKSPACE स्निपेट में इसे नहीं दिखाया गया है. अप-टू-डेट और पूरी तरह से तैयार इंस्टैंटिएशन स्टैंज़ा के लिए,rules_android_ndk
के बुनियादी उदाहरण वाले ऐप्लिकेशन की WORKSPACE फ़ाइल देखें.
android_ndk_repository
नियम के बारे में ज़्यादा जानने के लिए, इसकी डॉकस्ट्रिंग देखें.
तुरंत शुरू करना
Android के लिए C++ बनाने के लिए, बस अपनी android_binary
या android_library
नियमों में cc_library
डिपेंडेंसी जोड़ें.
उदाहरण के लिए, Android ऐप्लिकेशन के लिए यहां दी गई BUILD
फ़ाइल देखें:
# In <project>/app/src/main/BUILD.bazel
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_android//rules:rules.bzl", "android_binary", "android_library")
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 --android_platforms=<your platform>
ध्यान दें कि अगर आपने --android_platforms
तय नहीं किया है, तो आपकी बिल्ड प्रोसेस पूरी नहीं होगी. साथ ही, आपको JNI हेडर मौजूद न होने से जुड़ी गड़बड़ियां दिखेंगी.
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
) फ़ाइल में कंपाइल करता है. यह --android_platforms
में बताए गए आर्किटेक्चर को टारगेट करता है.
ज़्यादा जानकारी के लिए, टारगेट एबीआई को कॉन्फ़िगर करना सेक्शन देखें.
सेटअप का उदाहरण
यह उदाहरण, 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();
}
टारगेट किए गए ABI को कॉन्फ़िगर करना
टारगेट एबीआई को कॉन्फ़िगर करने के लिए, --android_platforms
फ़्लैग का इस्तेमाल इस तरह करें:
bazel build //:app --android_platforms=comma-separated list of platforms
--platforms
फ़्लैग की तरह ही, --android_platforms
को पास की गई वैल्यू, platform
टारगेट के लेबल होती हैं. इनमें स्टैंडर्ड कंस्ट्रेंट वैल्यू का इस्तेमाल करके, आपके डिवाइस के बारे में बताया जाता है.
उदाहरण के लिए, 64-बिट ARM प्रोसेसर वाले Android डिवाइस के लिए, आपको अपना प्लैटफ़ॉर्म इस तरह से तय करना होगा:
platform(
name = "android_arm64",
constraint_values = [
"@platforms//os:android",
"@platforms//cpu:arm64",
],
)
हर Android platform
को @platforms//os:android
ओएस की ज़रूरी शर्त का इस्तेमाल करना चाहिए. सीपीयू की सीमा को माइग्रेट करने के लिए, यह चार्ट देखें:
सीपीयू की वैल्यू | प्लैटफ़ॉर्म |
---|---|
armeabi-v7a |
@platforms//cpu:armv7 |
arm64-v8a |
@platforms//cpu:arm64 |
x86 |
@platforms//cpu:x86_32 |
x86_64 |
@platforms//cpu:x86_64 |
इसके अलावा, मल्टी-आर्किटेक्चर वाले APK के लिए, एक से ज़्यादा लेबल पास किए जाते हैं. उदाहरण के लिए: --android_platforms=//:arm64,//:x86_64
(मान लें कि आपने उन्हें टॉप-लेवल की BUILD.bazel
फ़ाइल में तय किया है).
Bazel, डिफ़ॉल्ट Android प्लैटफ़ॉर्म नहीं चुन सकता. इसलिए, आपको एक प्लैटफ़ॉर्म तय करना होगा और उसे --android_platforms
के साथ तय करना होगा.
एनडीके के वर्शन और 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 |
C++17 | --cxxopt=-std=c++17 |
उदाहरण के लिए:
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
)
android_binary
का इस्तेमाल किए बिना, Android के लिए cc_library
बनाना
android_binary
का इस्तेमाल किए बिना, Android के लिए स्टैंडअलोन cc_binary
या cc_library
बनाने के लिए, --platforms
फ़्लैग का इस्तेमाल करें.
उदाहरण के लिए, मान लें कि आपने my/platforms/BUILD
में Android प्लैटफ़ॉर्म तय किए हैं:
bazel build //my/cc/jni:target \
--platforms=//my/platforms:x86_64
इस तरीके से, पूरे बिल्ड ट्री पर असर पड़ता है.
इन फ़्लैग को bazelrc
config (हर ABI के लिए एक) में रखा जा सकता है. इसके लिए, project/.bazelrc
का इस्तेमाल करें:
common:android_x86 --platforms=//my/platforms:x86
common:android_armeabi-v7a --platforms=//my/platforms:armeabi-v7a
# In general
common:android_<abi> --platforms=//my/platforms:<abi>
इसके बाद, x86
के लिए x86
बनाने के लिए, उदाहरण के तौर पर यह कमांड चलाएं:cc_library
bazel build //my/cc/jni:target --config=android_x86
आम तौर पर, इस तरीके का इस्तेमाल छोटे लेवल के टारगेट (जैसे, cc_library
) के लिए करें. इसके अलावा, इसका इस्तेमाल तब करें, जब आपको पता हो कि आपको क्या बनाना है. बड़े लेवल के टारगेट के लिए, android_binary
से ऑटोमैटिक कॉन्फ़िगरेशन ट्रांज़िशन पर भरोसा करें. ऐसा तब करें, जब आपको ऐसे कई टारगेट बनाने हों जिन्हें कंट्रोल नहीं किया जा सकता.