מודולים של קישורי Bindgen

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

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

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

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

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

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

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

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

stem, name, crate_name

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

wrapper_src

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

source_stem

זה שם הקובץ של קובץ המקור שנוצר. חובה להגדיר את השדה הזה, גם אם משתמשים ב-bindings כ-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, שמשתמש ב-API של 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: ["."],
    }