बेज़ेल के साथ Android नेटिव डेवलपमेंट किट का इस्तेमाल करना

संग्रह की मदद से व्यवस्थित रहें अपनी प्राथमिकताओं के आधार पर, कॉन्टेंट को सेव करें और कैटगरी में बांटें.
समस्या की शिकायत करें स्रोत देखें

अगर आप बेज़ल के बारे में नहीं जानते हैं, तो कृपया Bazel की मदद से Android बनाना ट्यूटोरियल देखें.

खास जानकारी

बैज़ेल कई बिल्ड कॉन्फ़िगरेशन में चल सकता है, जिनमें कई Android Android नेटिव डेवलपमेंट किट (एनडीके) टूलचेन का इस्तेमाल करते हैं. इसका मतलब है कि Android के लिए, cc_library और cc_binary के सामान्य नियमों को सीधे बेज़ल में कंपाइल किया जा सकता है. बैज़ल ने android_ndk_repository रिपॉज़िटरी नियम का इस्तेमाल करके ऐसा किया.

ज़रूरी शर्तें

कृपया पक्का करें कि आपने Android SDK और NDK इंस्टॉल किया है.

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 नियम के बारे में ज़्यादा जानकारी के लिए, बिल्ड एन्साइक्लोपीडिया की एंट्री देखें.

तुरंत शुरू करने का तरीका

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

बैज़ेल, सभी cc_libries को एक शेयर किए गए ऑब्जेक्ट (.so) में इकट्ठा करता है. इसे डिफ़ॉल्ट तौर पर, armeabi-v7a एबीआई के लिए टारगेट किया जाता है. इसमें बदलाव करने या एक साथ कई एबीआई बनाने के लिए, टारगेट एबीआई को कॉन्फ़िगर करने से जुड़ा सेक्शन देखें.

सेट अप करने का उदाहरण

यह उदाहरण Bazel with repository में उपलब्ध है.

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 में उपलब्ध एसटीएल:

एसटीएल टारगेट लेबल
एसटीएलपोर्ट @androidndk//:toolchain-stlport
लाइब्रेरी++ @androidndk//:toolchain-libcpp
gnustl @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 बनाएगा जिसमें हर एबीआई के लिए 'शेयर किया गया ऑब्जेक्ट' होगा.

NDK वर्शन में बदलाव और Android API के लेवल के आधार पर, ये एबीआई उपलब्ध हैं:

एनडीके रिज़ॉल्यूशन एबीआई
16 साल और उससे कम आर्मेबी, आर्मेबी-v7a, आर्म64-v8a, माइप्स, mips64, x86, x86_64
17 साल और इससे ज़्यादा आर्मेबी-v7a, आर्म64-v8a, x86, x86_64

इन एबीआई के बारे में ज़्यादा जानने के लिए, एनडीके दस्तावेज़ देखें.

मल्टी-एबीआई फ़ैट APKs का सुझाव, रिलीज़ के लिए बनाए जाने वाले बिल्ड के लिए नहीं दिया जाता है, क्योंकि इनसे 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 के साथ पास करने के बारे में ज़्यादा जानने के लिए, उपयोगकर्ता मैन्युअल पढ़ें.

copts और linkopts का इस्तेमाल करके, cc_library में कंपाइलर और लिंकर फ़्लैग भी एट्रिब्यूट के तौर पर बताए जा सकते हैं. उदाहरण के लिए:

cc_library(
    name = "jni_lib",
    srcs = ["cpp/native-lib.cpp"],
    copts = ["-std=c++11"],
    linkopts = ["-ldl"], # link against libdl
)

प्लैटफ़ॉर्म और टूलचेन के साथ इंटिग्रेशन

बैज़ेल का कॉन्फ़िगरेशन मॉडल, प्लैटफ़ॉर्म और टूलचेन की ओर बढ़ रहा है. अगर आपका बिल्डर, आर्किटेक्चर या ऑपरेटिंग सिस्टम चुनने के लिए --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")

इन टूलचेन को रजिस्टर करने पर, बेज़ल को आर्किटेक्चर और ऑपरेटिंग सिस्टम की समस्याओं को ठीक करते समय, एनडीके 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 का नियम, बैज़ेल को Android के साथ काम करने वाले कॉन्फ़िगरेशन में डिपेंडेंसी बनाने के लिए साफ़ तौर पर कह सकता है, ताकि बैज़ेल, एबीआई और एसटीएल कॉन्फ़िगरेशन के लिए, --fat_apk_cpu और --android_crosstool_top को छोड़कर, किसी खास तरह के फ़्लैग के साथ सिर्फ़ काम करता हो.

पर्दे के पीछे, अपने-आप कॉन्फ़िगरेशन की सुविधा में Android के कॉन्फ़िगरेशन से जुड़े ट्रांज़िशन का इस्तेमाल होता है.

एक android_binary, साथ काम करने वाला नियम, Android कॉन्फ़िगरेशन में डिपेंडेंसी के कॉन्फ़िगरेशन को अपने-आप बदल देता है. इसलिए, बिल्ड से जुड़े सिर्फ़ Android के खास सबट्री पर असर पड़ता है. बिल्ड ग्राफ़ के दूसरे हिस्से, टॉप-लेवल टारगेट कॉन्फ़िगरेशन का इस्तेमाल करके प्रोसेस किए जाते हैं. अगर यह काम करने के लिए बिल्ड ग्राफ़ में पाथ हैं, तो दोनों कॉन्फ़िगरेशन में एक टारगेट को भी प्रोसेस किया जा सकता है.

जब बेज़ल Android के साथ काम करने वाले कॉन्फ़िगरेशन में होता है, तो यह टॉप लेवल पर या हाई लेवल के ट्रांज़िशन पॉइंट की वजह से होता है. साथ ही, मिलने वाले अतिरिक्त ट्रांज़िशन पॉइंट से कॉन्फ़िगरेशन में और बदलाव नहीं होता.

Android की कॉन्फ़िगरेशन में ट्रांज़िशन के बारे में जानकारी देने के लिए, android_binary का deps एट्रिब्यूट, पहले से मौजूद होता है.

उदाहरण के लिए, अगर आप cc_library फ़्लैग के बिना 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 की कमांड लाइन पर टारगेट, पहले से ही इनमें से किसी भी ट्रांज़िशन के नियमों के नीचे है, जैसे कि किसी खास cc_library को टेस्ट करने वाले C++ डेवलपर, तो कस्टम --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 टारगेट को एनडीके टूलचेन का इस्तेमाल करके बनाया गया है. हालांकि, इससे बेज़ैल के अपने होस्ट टूल को एनडीके टूलचेन से बनाया जा सकता है. इस तरह यह 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 के ऑटोमैटिक कॉन्फ़िगरेशन ट्रांज़िशन पर भरोसा करें. यहां पर आपको ऐसे कई टारगेट बनाने की उम्मीद है जिन्हें आप कंट्रोल नहीं करते हैं.