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

समस्या की शिकायत करें सोर्स देखें

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

खास जानकारी

Bazel कई अलग-अलग बिल्ड कॉन्फ़िगरेशन में काम कर सकता है. इनमें 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 Android NDK (r22 और उसके बाद के वर्शन) के हाल के वर्शन का इस्तेमाल किया जा रहा है, तो android_ndk_repository के Starlark तरीके का इस्तेमाल करें. इसके Read_ME में दिए गए निर्देशों का पालन करें.

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

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

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

यह उदाहरण बैज़ल के उदाहरणों की जगह में उपलब्ध है.

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++ STL को कॉन्फ़िगर करने के लिए, फ़्लैग --android_crosstool_top का इस्तेमाल करें.

bazel build //:app --android_crosstool_top=target label

@androidndk में उपलब्ध एसटीएल नीचे दिए गए हैं:

एसटीएल टारगेट लेबल
एसटीएलपोर्ट @androidndk//:toolchain-stlport
lib++ @androidndk//:toolchain-libcpp
gnustl @androidndk//:toolchain-gnu-libstdcpp

r16 और उससे पहले के वर्शन के लिए, डिफ़ॉल्ट एसटीएल gnustl है. r17 और इसके बाद के वर्शन के लिए, यह libc++ है. आपकी सुविधा के लिए, टारगेट @androidndk//:default_crosstool डिफ़ॉल्ट डिफ़ॉल्ट एसटीएल पर निर्भर करता है.

कृपया ध्यान दें कि r18 के बाद से, STLport और gnustl को से हटा दिया जाएगा, जो libc++ NDK में इकलौता STL बन जाएगा.

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

टारगेट एबीआई को कॉन्फ़िगर करना

टारगेट एबीआई को कॉन्फ़िगर करने के लिए, --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.

अगर एक से ज़्यादा एबीआई तय किए गए हैं, तो बज़ेल एक ऐसा APK बनाएगा जिसमें हर एबीआई के लिए एक शेयर किया गया ऑब्जेक्ट होगा.

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

NDK वर्शन एबीआई
16 और उससे कम Armeabi, Armeabi-v7a, Arm64-v8a, mips, mips64, x86, x86_64
17 और उससे ज़्यादा Armeabi-v7a, Arm64-v8a, x86, x86_64

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

रिलीज़ बिल्ड के लिए मल्टी-एबीआई फ़ैट APK का सुझाव नहीं दिया जाता है, क्योंकि ये APK के साइज़ को बढ़ाते हैं. हालांकि, ये डेवलपमेंट और QA बिल्ड के लिए उपयोगी हो सकते हैं.

C++ मानक चुनना

C++ मानक के हिसाब से बनाने के लिए, इन फ़्लैग का इस्तेमाल करें:

C++ मानक झंडा
सी+99 डिफ़ॉल्ट, कोई फ़्लैग ज़रूरी नहीं है
सी++11 --cxxopt=-std=c++11
सी++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 का इस्तेमाल करने के लिए, आपको बैजल में --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 फ़ाइल (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 नियम, Android डिवाइसों के साथ काम करने वाले कॉन्फ़िगरेशन में डिपेंडेंट डिपेंडेंसी बनाने के लिए कह सकता है, ताकि Bazel, किसी खास फ़्लैग के बिना सिर्फ़ काम कर सके. हालांकि, एबीआई और एसटीएल कॉन्फ़िगरेशन के लिए --fat_apk_cpu और --android_crosstool_top को छोड़कर, ऐसा नहीं किया जा सकता.

यह सिस्टम अपने-आप Android कॉन्फ़िगरेशन कॉन्फ़िगरेशन का इस्तेमाल करता है.

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

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

Android की कॉन्फ़िगरेशन में ट्रांज़िशन ट्रिगर करने वाली इकलौती जगह ही android_binary की deps विशेषता है.

उदाहरण के लिए, अगर आप android_library बिना किसी फ़्लैग के cc_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.

आम तौर पर, अपने-आप होने वाले इन ट्रांज़िशन की वजह से, बेजल आने वाले ज़्यादातर मामलों में, इवेंट को सही तरीके से किया जा सकता है. हालांकि, अगर Bazecc_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 के खुद के होस्ट टूल, NDK टूलचेन (और Android के लिए) के साथ बनाए जा सकते हैं. ऐसा इसलिए, क्योंकि होस्ट टूलचेन, टारगेट टूलचेन से कॉपी किया जाता है. इस पर काम करने के लिए, --host_crosstool_top का मान @bazel_tools//tools/cpp:toolchain रखें और साफ़ तौर पर होस्ट का C++ टूलचेन सेट करें.

इस तरीके से, पूरा बिल्ड ट्री प्रभावित होता है.

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