बिल्ड सिस्टम, rust_bindgen मॉड्यूल टाइप के ज़रिए bindgen बाइंडिंग जनरेट करने की सुविधा देता है. Bindgen, C लाइब्रेरी के लिए Rust FFI बाइंडिंग उपलब्ध कराता है. इसमें C++ के लिए कुछ सीमित सपोर्ट भी शामिल है. इसके लिए, cppstd प्रॉपर्टी सेट करनी होती है.

rust_bindgen का बुनियादी इस्तेमाल

यहां bindgen का इस्तेमाल करने वाले मॉड्यूल को तय करने का तरीका बताया गया है. साथ ही, उस मॉड्यूल को क्रेट के तौर पर इस्तेमाल करने का तरीका भी बताया गया है. अगर आपको बाहरी कोड के लिए, include!() मैक्रो के ज़रिए bindgen बाइंडिंग का इस्तेमाल करना है, तो सोर्स जनरेटर पेज देखें.

Rust से कॉल करने के लिए C लाइब्रेरी का उदाहरण

यहां C लाइब्रेरी का एक उदाहरण दिया गया है. इसमें Rust में इस्तेमाल करने के लिए, struct और फ़ंक्शन को तय किया गया है.

external/rust/libbuzz/libbuzz.h

typedef struct foo {
    int x;
} foo;

void fizz(int i, foo* cs);

external/rust/libbuzz/libbuzz.c

#include <stdio.h>
#include "libbuzz.h"

void fizz(int i, foo* my_foo){
    printf("hello from c! i = %i, my_foo->x = %i\n", i, my_foo->x);
}

rust_bindgen मॉड्यूल तय करना

रैपर हेडर, external/rust/libbuzz/libbuzz_wrapper.h को तय करें. इसमें सभी काम के हेडर शामिल होते हैं:

// Include headers that are required for generating bindings in a wrapper header.
#include "libbuzz.h"

Android.bp फ़ाइल को external/rust/libbuzz/Android.bp के तौर पर तय करें:

cc_library {
    name: "libbuzz",
    srcs: ["libbuzz.c"],
}

rust_bindgen {
     name: "libbuzz_bindgen",

     // Crate name that's used to generate the rust_library variants.
     crate_name: "buzz_bindgen",

     // Path to the wrapper source file.
     wrapper_src: "libbuzz_wrapper.h",

     // 'source_stem' controls the output filename.
     // This is the filename that's used in an include! macro.
     //
     // In this case, we just use "bindings", which produces
     // "bindings.rs".
     source_stem: "bindings",

     // Bindgen-specific flags and options to customize the bindings.
     // See the bindgen manual for more information.
     bindgen_flags: ["--verbose"],

     // Clang flags to be used when generating the bindings.
     cflags: ["-DSOME_FLAG"],

     // Shared, static, and header libraries which export the necessary
     // include directories must be specified.
     //
     // These libraries will also be included in the crate if static,
     // or propagated to dependents if shared.
     // static_libs: ["libbuzz"]
     // header_libs: ["libbuzz"]
     shared_libs: ["libbuzz"],
}

bindgen फ़्लैग इस्तेमाल करने के बारे में ज़्यादा जानने के लिए, bindgen मैनुअल सेक्शन में जनरेट की गई बाइंडिंग को पसंद के मुताबिक बनाना लेख पढ़ें.

अगर आपने इस सेक्शन का इस्तेमाल, rust_bindgen मैक्रो का इस्तेमाल करने के लिए rust_bindgen मॉड्यूल को ज़रूरी शर्त के तौर पर तय करने के लिए किया है, तो सोर्स जनरेटर पेज पर ज़रूरी शर्त पर वापस जाएं.include!() अगर ऐसा नहीं है, तो अगले सेक्शन पर जाएं.

बाइंडिंग को क्रेट के तौर पर इस्तेमाल करना

नीचे दिए गए कॉन्टेंट का इस्तेमाल करके external/rust/hello_bindgen/Android.bp बनाएं:

rust_binary {
   name: "hello_bindgen",
   srcs: ["main.rs"],

   // Add the rust_bindgen module as if it were a rust_library dependency.
   rustlibs: ["libbuzz_bindgen"],
}

नीचे दिए गए कॉन्टेंट का इस्तेमाल करके external/rust/hello_bindgen/src/main.rs बनाएं:

//! Example crate for testing bindgen bindings

fn main() {
    let mut x = buzz_bindgen::foo { x: 2 };
    unsafe { buzz_bindgen::fizz(1, &mut x as *mut buzz_bindgen::foo) }
}

आखिर में, बाइनरी बनाने के लिए m hello_bindgen को कॉल करें.

Bindgen बाइंडिंग की जांच करना

Bindgen बाइंडिंग में आम तौर पर, जनरेट किए गए कई लेआउट टेस्ट शामिल होते हैं. इनसे मेमोरी लेआउट के मिसमैच को रोका जा सकता है. AOSP का सुझाव है कि इन टेस्ट के लिए, आपके पास एक टेस्ट मॉड्यूल होना चाहिए. साथ ही, ये टेस्ट आपके प्रोजेक्ट के सामान्य टेस्ट सुइट के हिस्से के तौर पर चलने चाहिए.

इनके लिए टेस्ट बाइनरी आसानी से बनाई जा सकती है. इसके लिए, external/rust/hello_bindgen/Android.bp में rust_test मॉड्यूल को इस तरह से तय करें:

rust_test {
    name: "bindings_test",
    srcs: [
        ":libbuzz_bindgen",
    ],
    crate_name: "buzz_bindings_test",
    test_suites: ["general-tests"],
    auto_gen_config: true,

    // Be sure to disable lints as the generated source
    // is not guaranteed to be lint-free.
    clippy_lints: "none",
    lints: "none",
}

विज़िबिलिटी और लिंक करने की सुविधा

जनरेट किए गए बाइंडिंग का साइज़ आम तौर पर बहुत कम होता है, क्योंकि इनमें टाइप डेफ़िनिशन, फ़ंक्शन सिग्नेचर, और उनसे जुड़े कॉन्स्टेंट शामिल होते हैं. इसलिए, इन लाइब्रेरी को डाइनैमिक तरीके से लिंक करना आम तौर पर फ़ायदेमंद नहीं होता. हमने इन मॉड्यूल के लिए डाइनैमिक लिंकिंग की सुविधा बंद कर दी है, ताकि rustlibs के ज़रिए इनका इस्तेमाल करने पर, स्टैटिक वैरिएंट अपने-आप चुना जा सके.

डिफ़ॉल्ट रूप से, rust_bindgen मॉड्यूल में visibility प्रॉपर्टी की वैल्यू [":__subpackages__"] होती है. इससे, सिर्फ़ एक ही Android.bp फ़ाइल में मौजूद मॉड्यूल या डायरेक्ट्री हैरारकी में उसके नीचे मौजूद मॉड्यूल को इसे देखने की अनुमति मिलती है. इससे दो मकसद पूरे होते हैं:

  • इससे ट्री में कहीं और रॉ सी बाइंडिंग के इस्तेमाल को बढ़ावा नहीं मिलता.
  • यह स्टैटिक और डाइनैमिक लिंकेज के कॉम्बिनेशन का इस्तेमाल करके, डायमंड लिंकिंग से जुड़ी समस्याओं से बचाता है.

आम तौर पर, आपको जनरेट किए गए मॉड्यूल के चारों ओर एक सुरक्षित रैपर लाइब्रेरी उपलब्ध करानी चाहिए. इसे आपने बाइंडिंग के तौर पर उसी डायरेक्ट्री ट्री में जोड़ा है. इसका मकसद, दूसरे डेवलपर को इसका इस्तेमाल करने की अनुमति देना है. अगर यह आपके इस्तेमाल के उदाहरण के लिए काम नहीं करता है, तो विज़िबिलिटी में अतिरिक्त पैकेज जोड़े जा सकते हैं. विज़िबिलिटी के अतिरिक्त स्कोप जोड़ते समय, कृपया ध्यान रखें कि आपने ऐसे दो स्कोप न जोड़े हों जो आने वाले समय में एक ही प्रोसेस से लिंक हो सकते हैं. ऐसा करने पर, लिंक करने की प्रोसेस पूरी नहीं हो सकती.

rust_bindgen की खास प्रॉपर्टी

यहां दी गई प्रॉपर्टी के अलावा, ज़रूरी सामान्य प्रॉपर्टी भी लागू होती हैं. ये प्रॉपर्टी सभी मॉड्यूल पर लागू होती हैं. ये Rustbindgen मॉड्यूल के लिए खास तौर पर ज़रूरी हैं या rust_bindgen मॉड्यूल टाइप के लिए खास तौर पर यूनीक व्यवहार दिखाते हैं.

स्टेम, नाम, crate_name

rust_bindgen, लाइब्रेरी के वैरिएंट बनाता है. इसलिए, stem, name, और crate_name प्रॉपर्टी के लिए, rust_library मॉड्यूल की ज़रूरी शर्तें भी इन पर लागू होती हैं. रेफ़रंस के लिए, Rust लाइब्रेरी की मुख्य प्रॉपर्टी देखें.

wrapper_src

यह रैपर हेडर फ़ाइल का रिलेटिव पाथ है. इसमें इन बाइंडिंग के लिए ज़रूरी हेडर शामिल होते हैं. फ़ाइल एक्सटेंशन से यह तय होता है कि हेडर को कैसे समझा जाए. साथ ही, इससे यह भी तय होता है कि डिफ़ॉल्ट रूप से कौनसा -std फ़्लैग इस्तेमाल किया जाए. इसे C हेडर माना जाता है जब तक एक्सटेंशन .hh या .hpp न हो. अगर आपके C++ हेडर का एक्सटेंशन कुछ और होना चाहिए, तो cpp_std प्रॉपर्टी सेट करें. इससे डिफ़ॉल्ट व्यवहार बदल जाएगा. डिफ़ॉल्ट व्यवहार के मुताबिक, फ़ाइल को C फ़ाइल माना जाता है.

source_stem

यह जनरेट की गई सोर्स फ़ाइल का फ़ाइल नाम है. इस फ़ील्ड को डिफ़ाइन करना ज़रूरी है. भले ही, बाइंडिंग का इस्तेमाल क्रेट के तौर पर किया जा रहा हो. ऐसा इसलिए, क्योंकि stem प्रॉपर्टी सिर्फ़ जनरेट किए गए लाइब्रेरी वैरिएंट के आउटपुट फ़ाइल नाम को कंट्रोल करती है. अगर कोई मॉड्यूल, rustlibs के ज़रिए क्रेट के तौर पर इस्तेमाल करने के बजाय, सोर्स के तौर पर कई सोर्स जनरेटर (जैसे कि bindgen और protobuf) पर निर्भर करता है, तो आपको यह पक्का करना होगा कि उस मॉड्यूल पर निर्भर करने वाले सभी सोर्स जनरेटर के लिए, source_stem की वैल्यू यूनीक हों. डिपेंडेंट मॉड्यूल, SourceProvider में तय की गई सभी SourceProvider डिपेंडेंसी से सोर्स कॉपी करते हैं. ये सोर्स, एक सामान्य OUT_DIR डायरेक्ट्री में कॉपी किए जाते हैं. इसलिए, source_stem में टकराव होने पर, जनरेट की गई सोर्स फ़ाइलें OUT_DIR डायरेक्ट्री में बदल दी जाएंगी.srcs

c_std

यह एक स्ट्रिंग है. इसमें यह बताया जाता है कि C-स्टैंडर्ड के किस वर्शन का इस्तेमाल करना है. मान्य वैल्यू यहां दी गई हैं:

  • कोई खास वर्शन, जैसे कि "gnu11".
  • "experimental", build/soong/cc/config/global.go में बिल्ड सिस्टम की तय की गई वैल्यू है. यह उपलब्ध होने पर, C++1z जैसे ड्राफ़्ट वर्शन का इस्तेमाल कर सकती है.
  • इसे सेट न करें या "" पर सेट करें. इससे पता चलता है कि बिल्ड सिस्टम के डिफ़ॉल्ट वर्शन का इस्तेमाल किया जाना चाहिए.

अगर इसे सेट किया जाता है, तो फ़ाइल एक्सटेंशन को अनदेखा कर दिया जाता है और हेडर को C हेडर मान लिया जाता है. इसे cpp_std के साथ एक ही समय पर सेट नहीं किया जा सकता.

cpp_std

cpp_std एक स्ट्रिंग है. इससे पता चलता है कि C स्टैंडर्ड का कौनसा वर्शन इस्तेमाल करना है. मान्य मान:

  • कोई खास वर्शन, जैसे कि "gnu++11"
  • "experimental", build/soong/cc/config/global.go में बिल्ड सिस्टम की तय की गई वैल्यू है. यह उपलब्ध होने पर, C++1z जैसे ड्राफ़्ट वर्शन का इस्तेमाल कर सकती है.
  • इसे सेट न करें या "" पर सेट करें. इससे पता चलता है कि बिल्ड सिस्टम के डिफ़ॉल्ट वर्शन का इस्तेमाल किया जाना चाहिए.

अगर इसे सेट किया जाता है, तो फ़ाइल एक्सटेंशन को अनदेखा कर दिया जाता है. साथ ही, हेडर को C++ हेडर मान लिया जाता है. इसे c_std के साथ एक ही समय पर सेट नहीं किया जा सकता.

cflags

cflags, हेडर को सही तरीके से समझने के लिए ज़रूरी Clang फ़्लैग की स्ट्रिंग सूची उपलब्ध कराता है.

custom_bindgen

एडवांस इस्तेमाल के मामलों के लिए, bindgen को लाइब्रेरी के तौर पर इस्तेमाल किया जा सकता है. यह एक ऐसा एपीआई उपलब्ध कराता है जिसे कस्टम रस्ट बाइनरी के हिस्से के तौर पर बदला जा सकता है. custom_bindgen फ़ील्ड, rust_binary_host मॉड्यूल का मॉड्यूल नेम लेता है. यह मॉड्यूल, सामान्य bindgen बाइनरी के बजाय bindgen API का इस्तेमाल करता है.

इस कस्टम बाइनरी को bindgen की तरह ही आर्ग्युमेंट की ज़रूरत होती है. जैसे,

$ my_bindgen [flags] wrapper_header.h -o [output_path] -- [clang flags]

इनमें से ज़्यादातर काम, bindgen लाइब्रेरी खुद ही करती है. इसके इस्तेमाल का उदाहरण देखने के लिए, external/rust/crates/libsqlite3-sys/android/build.rs पर जाएं.

इसके अलावा, लाइब्रेरी की सभी प्रॉपर्टी, लाइब्रेरी के कंपाइलेशन को कंट्रोल करने के लिए उपलब्ध होती हैं. हालांकि, इन्हें कभी-कभार ही तय करने या बदलने की ज़रूरत होती है.

handle_static_inline और static_inline_library

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

इनका इस्तेमाल करने के लिए, handle_static_inline: true को सेट करें. साथ ही, static_inline_library को cc_library_static पर सेट करें. यह rust_bindgen मॉड्यूल को सोर्स इनपुट के तौर पर तय करता है.

इस्तेमाल का उदाहरण:

    rust_bindgen {
        name: "libbindgen",
        wrapper_src: "src/any.h",
        crate_name: "bindgen",
        stem: "libbindgen",
        source_stem: "bindings",

        // Produce bindings for static inline fucntions
        handle_static_inline: true,
        static_inline_library: "libbindgen_staticfns"
    }

    cc_library_static {
        name: "libbindgen_staticfns",

        // This is the rust_bindgen module defined above
        srcs: [":libbindgen"],

        // The include path to the header file in the generated c file is
        // relative to build top.
        include_dirs: ["."],
    }