وحدات ربط Bindgen

يتيح نظام التصميم إنشاء روابط bindgen من خلال نوع الوحدة rust_bindgen. توفّر أداة Bindgen روابط Rust FFI لمكتبات C (مع بعض الدعم المحدود للغة C++، ما يتطلّب ضبط السمة cppstd).

الاستخدام الأساسي لوحدة rust_bindgen

في ما يلي مثال على كيفية تحديد وحدة تستخدم bindgen وكيفية استخدام هذه الوحدة كحزمة. إذا كنت بحاجة إلى استخدام روابط bindgen من خلال ماكرو include!()، مثلاً للرمز الخارجي، يُرجى الاطّلاع على مولّدات المصدر.

مثال على مكتبة C للاتصال من Rust

في ما يلي مثال على مكتبة C تحدّد بنية ودالة لاستخدامهما في Rust.

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 كشرط أساسي لـ استخدام الماكرو 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 بتحديد وحدة اختبار لهذه الاختبارات، وأن يتم تشغيل الاختبارات كجزء من مجموعة الاختبارات العادية لمشروعك.

يمكنك إنشاء ملف ثنائي للاختبار من خلال تحديد وحدة rust_test في external/rust/hello_bindgen/Android.bp:

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 نفسه أو الوحدات التي تقع أسفله في التسلسل الهرمي للدليل بالاطّلاع عليها. يخدم ذلك غرضَين:

  • يمنع استخدام روابط C الأولية في مكان آخر في الشجرة.
  • يتجنّب مشاكل الربط الماسي مع مزيج من الربط الثابت والديناميكي.

يجب توفير مكتبة برنامج تضمين آمنة حول الوحدة التي تم إنشاؤها والتي أضفتها في شجرة الدليل نفسها التي تتضمّن الروابط، والتي يُفترض أن يستخدمها المطوّرون الآخرون. إذا لم ينجح ذلك في حالة الاستخدام، يمكنك إضافة حِزم إضافية إلى visibility. عند إضافة نطاقات ظهور إضافية، لا تُضِف نطاقَين قد يتم ربطهما في العملية نفسها في المستقبل، لأنّ ذلك قد يؤدي إلى تعذُّر الربط.

سمات rust_bindgen البارزة

السمات المحدّدة في هذا القسم هي بالإضافة إلى الـ سمات الشائعة المهمة التي تنطبق على جميع الوحدات. تكون هذه السمات إما مهمة لوحدات Rust bindgen أو تعرض سلوكًا فريدًا خاصًا بنوع الوحدة rust_bindgen.

stem وname وcrate_name

rust_bindgen تنتج متغيرات مكتبة، لذا تشارك المتطلبات نفسها مع وحدات rust_library للسمات stem وname وcrate_name الخاصة. للحصول على مرجع، يُرجى الاطّلاع على سمات مكتبة Rust البارزة.

wrapper_src

هذا هو المسار النسبي إلى ملف عنوان برنامج تضمين يتضمّن ملفات العناوين المطلوبة لهذه الروابط. يحدّد امتداد الملف كيفية تفسير ملف الرأس ويحدّد علامة -std التي يجب استخدامها تلقائيًا. يُفترض أنّ هذا الملف هو ملف رأس C ما لم يكن الامتداد .hh أو .hpp. إذا كان يجب أن يكون لملف رأس C++ امتداد آخر، اضبط السمة cpp_std لتجاوز السلوك التلقائي الذي يفترض أنّ الملف هو ملف C.

source_stem

هذا هو اسم ملف المصدر الذي تم إنشاؤه. يجب تحديد هذا الحقل حتى إذا كنت تستخدم الروابط كحزمة، لأنّ السمة stem تتحكّم فقط في اسم ملف الإخراج لمتغيرات المكتبة التي تم إنشاؤها. إذا كانت إحدى الوحدات تعتمد على عدة مولّدات مصدر (مثل bindgen وprotobuf) كمصدر بدلاً من الحِزم من خلال rustlibs، تأكَّد من أنّ جميع مولّدات المصدر التي تمثّل تبعيات لتلك الوحدة تتضمّن قيمًا فريدة لـ source_stem. تنسخ الوحدات التابعة المصادر من جميع تبعيات SourceProvider المحدّدة في srcs إلى دليل 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

توفّر cflags قائمة سلاسل بعلامات Clang المطلوبة لتفسير ملفات الرأس بشكلٍ صحيح.

custom_bindgen

في حالات الاستخدام المتقدّم، يمكن استخدام bindgen كمكتبة، ما يوفّر واجهة برمجة تطبيقات يمكن معالجتها كجزء من ملف ثنائي مخصّص لـ Rust. يأخذ الحقل custom_bindgen اسم الوحدة لـ rust_binary_host، التي تستخدم واجهة برمجة تطبيقات bindgen بدلاً من البرنامج الثنائي العادي bindgen.

يجب أن يتوقّع هذا الملف الثنائي المخصّص وسيطات بطريقة مشابهة لـ 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 functions
        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: ["."],
    }