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

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

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

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

Rust में इस्तेमाल करने के लिए, स्ट्रक्चर और फ़ंक्शन की परिभाषा देने वाली C लाइब्रेरी का उदाहरण यहां दिया गया है.

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 मैन्युअल सेक्शन देखें.

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

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

इस कॉन्टेंट का इस्तेमाल करके 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 मॉड्यूल में [":__subpackages__"] की visibility प्रॉपर्टी होती है. इसे सिर्फ़ उसी Android.bp फ़ाइल में मौजूद मॉड्यूल या डायरेक्ट्री हैरारकी में उसके नीचे मौजूद मॉड्यूल देख सकते हैं. इसके दो मकसद हैं:

  • इससे ट्री में कहीं और रॉ C बाइंडिंग का इस्तेमाल करने से बचने में मदद मिलती है.
  • यह स्टैटिक और डाइनैमिक लिंकेज के मिश्रण से, डायमंड लिंकिंग की समस्याओं से बचाता है.

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

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

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

stem, name, 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 वैल्यू यूनीक हों. डिपेंडेंट मॉड्यूल, srcs में बताई गई सभी SourceProvider डिपेंडेंसी के सोर्स को कॉपी करके, एक सामान्य OUT_DIR डायरेक्ट्री में डाल देते हैं. इसलिए, source_stem में होने वाले कोलिज़न की वजह से, जनरेट की गई सोर्स फ़ाइलें OUT_DIR डायरेक्ट्री में ओवरराइट हो जाएंगी.

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, हेडर का सही तरीके से विश्लेषण करने के लिए ज़रूरी Clang फ़्लैग की स्ट्रिंग सूची उपलब्ध कराता है.

custom_bindgen

बेहतर इस्तेमाल के उदाहरणों के लिए, bindgen का इस्तेमाल लाइब्रेरी के तौर पर किया जा सकता है. इससे एक ऐसा एपीआई मिलता है जिसे कस्टम Rust बाइनरी के हिस्से के तौर पर मैनिप्युलेट किया जा सकता है. 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

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

इनका इस्तेमाल करने के लिए, 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: ["."],
    }