מערכת ה-build תומכת ביצירת קישורים של bindgen באמצעות סוג המודול rust_bindgen. ‏Bindgen מספק קישורי FFI של Rust לספריות C (עם תמיכה מוגבלת ב-C++‎, שדורשת הגדרה של המאפיין cppstd).

שימוש בסיסי ב-rust_bindgen

הדוגמה הבאה ממחישה איך להגדיר מודול שמשתמש בקישור איך להשתמש במודול הזה בתור ארגז. אם אתם צריכים להשתמש בקישורי קישור דרך במאקרו include!(), כמו לקוד חיצוני, מחוללי מקור הדף הזה.

דוגמה לספריית C לקריאה מ-Rust

דוגמה לספריית C שמגדירה struct ופונקציה לשימוש ב-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

הגדרה של כותרת wrapper, 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"],
}

מידע נוסף על השימוש בדגלים של קישור (linkgen) זמין בקטע הידני של הקישור במאמר התאמה אישית של קישורים שנוצרו

אם השתמשתם בקטע הזה כדי להגדיר מודול rust_bindgen כדרישה מוקדמת לשימוש במאקרו include!(), עליכם לחזור לקטע דרישה מוקדמת בדף 'גנרטורים של מקורות נתונים'. אם לא, ממשיכים לקטע הבא.

שימוש בקישור כ-crate

יוצרים את 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

קישורי ביניים מכילים בדרך כלל מספר בדיקות פריסה שנוצרו כדי למנוע אי התאמה בפריסת הזיכרון. ב-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 גולמיות במקומות אחרים בעץ.
  • הוא מונע בעיות בקישור יהלומים בשילוב של קישור סטטי ודינמי.

בדרך כלל, צריך לספק ספריית עטיפה בטוחה סביב המודול שנוצר שהוספתם לאותו עץ ספריות שבו הוספתם את הקישורים, והיא מיועדת לשימוש של מפתחים אחרים. אם הפתרון הזה לא מתאים לתרחיש לדוגמה שלכם, תוכלו להוסיף חבילות נוספות לקטע חשיפה. כשמוסיפים עוד היקפי הרשאות גישה, חשוב להקפיד לא להוסיף שני היקפים שעשויים להיות מקושרים לאותו תהליך בעתיד, כי זה עשוי הקישור נכשל.

מאפיינים בולטים של rust_bindgen

המאפיינים שמוגדרים בהמשך הם בנוסף למאפיינים משותפים חשובים שחלים על כל המודולים. הם חשובים במיוחד למודולים של Rust ב-bindgen, או שהם מציגים התנהגות ייחודית ספציפית לסוג המודול rust_bindgen.

גזע, שם, crate_name

rust_bindgen יוצר וריאנטים של ספריות, ולכן הם חולקים את אותן דרישות עם המודולים של rust_library למאפיינים stem,‏ name ו-crate_name. צפייה מאפיינים בולטים של ספריית חלודה לעיון.

wrapper_src

זהו הנתיב היחסי לקובץ כותרת מסוג wrapper שכולל את הכותרות הנדרשות לקישורים האלה. סיומת הקובץ קובעת איך לפרש את הכותרת והוא קובע באיזה דגל -std להשתמש כברירת מחדל. ההנחה היא שמדובר בכותרת C אלא אם התוסף הוא .hh או .hpp. אם לכותרת של C++ צריכה להיות תוספת אחרת, מגדירים את המאפיין cpp_std כדי לשנות את התנהגות ברירת המחדל שמניחה שהקובץ הוא קובץ C.

גזע_מקור

זהו שם הקובץ של קובץ המקור שנוצר. חובה להגדיר את השדה הזה, גם אם משתמשים בקישור כ-crate, כי המאפיין stem קובע רק את שם הקובץ של הפלט של הווריאנטים של הספרייה שנוצרו. אם המודול תלוי במספר מחוללי מקור (כמו bindgen ו-protobuf) כמקור ולא כתיבות דרך rustlibs, צריך לוודא שכל מחוללי המקור שהם יחסי תלות של המודול הזה יש ערכי source_stem ייחודיים. מודולים תלויים להעתיק מקורות מכל יחסי התלות של SourceProvider שמוגדרים srcs לספריית OUT_DIR משותפת, כך שהתנגשויות ב-source_stem כתוצאה מכך, קובצי המקור שנוצרו יוחלפו בספרייה OUT_DIR.

c_std

זוהי מחרוזת שמייצגת את גרסת סטנדרט C שבה צריך להשתמש. ערכים חוקיים הם:

  • גרסה ספציפית, כמו "gnu11".
  • "experimental", שהוא ערך שמוגדר על ידי מערכת ה-build בקובץ build/soong/cc/config/global.go, עשוי להשתמש בגרסאות טיוטה כמו C++1z כשהן זמינות.
  • ביטול ההגדרה או הערך "", שמציין שצריך להשתמש בברירת המחדל של מערכת ה-build.

אם ההגדרה הזו מוגדרת, המערכת מתעלמת מסיומת הקובץ ומניחה שהכותרת היא כותרת C. אי אפשר להגדיר את ההגדרה הזו באותו זמן שבו מוגדרת ההגדרה cpp_std.

cpp_std

cpp_std היא מחרוזת שמייצגת את הגרסה הרגילה של C. הערכים התקפים:

  • גרסה ספציפית, כמו "gnu++11"
  • "experimental", שהוא ערך שמוגדר על ידי מערכת ה-build בקובץ build/soong/cc/config/global.go, עשוי להשתמש בגרסאות טיוטה כמו C++1z כשהן זמינות.
  • ביטול ההגדרה או הערך "", שמציין שצריך להשתמש בברירת המחדל של מערכת ה-build.

אם ההגדרה הזו מוגדרת, המערכת מתעלמת מסיומת הקובץ ומניחה שהכותרת היא כותרת של C++‎. אי אפשר להגדיר את ההגדרה הזו באותו זמן שבו מוגדרת ההגדרה c_std.

cflags

cflags מספק רשימת מחרוזות של דגלים של Clang שנדרשים כדי לפרש את הכותרות בצורה נכונה.

custom_bindgen

לתרחישים מתקדמים של שימוש, אפשר להשתמש ב-bindgen כספרייה, שמספקת ממשק API שאפשר לשנות כחלק מקובץ בינארי מותאם אישית של Rust. השדה custom_bindgen לוקח את המודול של מודול rust_binary_host, שמשתמש במקום זאת ב-linkgen API מהבינארי הרגיל של bindgen.

קובץ הבינארי המותאם אישית הזה צריך לצפות לארגומנטים באופן דומה ל-bindgen, למשל:

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

רוב התהליך הזה מטופל על ידי ספריית bindgen עצמה. דוגמה לשימוש כזה מופיעה בכתובת external/rust/crates/libsqlite3-sys/android/build.rs.

בנוסף, הקבוצה המלאה של מאפייני הספרייה זמינה לשליטה את אוסף הספרייה, למרות שלעיתים נדירות יש להגדיר או לשנות נושאים כאלה.

ידית_static_inline ו-static_inline_library

שני המאפיינים האלה מיועדים לשימוש יחד, ומאפשרים הפקה של רכיבי wrapper לפונקציות סטטיות מוטבעות, שאפשר לכלול בקובץ קישורי מקשר.

כדי להשתמש בהם, צריך להגדיר את 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: ["."],
    }