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

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

בהמשך מופיעה דוגמה להגדרת מודול שמשתמש ב-bindgen, וגם דוגמה לשימוש באותו מודול כ-crate. אם אתם צריכים להשתמש בקישורי מקשר דרך מאקרו 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

מגדירים כותרת 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"],
}

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

אם השתמשתם בקטע הזה כדי להגדיר מודול 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

קישורי 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.

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

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

custom_bindgen

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

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

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

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

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

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: ["."],
    }