גנרטורים של מקורות

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

כל הגנרטורים של מקורות מספקים פונקציונליות דומה של מערכת build. שלושת תרחישי השימוש ליצירת מקורות שנתמכים במערכת ה-build הם יצירת קישורי C באמצעות bindgen, ממשקי AIDL וממשקי protobuf.

ארגזים ממקור שנוצר

אפשר להשתמש בכל מודול Rust שיוצר קוד מקור כ-crate, בדיוק כמו שהוא מוגדר כ-rust_library. (כלומר, אפשר להגדיר אותו כיחס תלות במאפיינים rustlibs, ‏ rlibs ו-dylibs). דפוס השימוש הטוב ביותר בקוד של פלטפורמה הוא להשתמש במקור שנוצר כ-crate. למרות שיש תמיכה במאקרו include! למקור שנוצר, המטרה העיקרית שלו היא לתמוך בקוד של צד שלישי שנמצא ב-external/.

יש מקרים שבהם קוד הפלטפורמה עדיין עשוי להשתמש בקוד מקור שנוצר באמצעות המאקרו include!(), למשל כשמשתמשים במודול genrule כדי ליצור קוד מקור באופן ייחודי.

שימוש ב-include!() כדי לכלול מקור שנוצר

הדוגמאות לדף של כל מודול ספציפי (מתאים) כוללות הסבר על שימוש במקור שנוצר כ-crate. בקטע הזה מוסבר איך להפנות למקור שנוצר באמצעות המאקרו include!(). חשוב לזכור שהתהליך הזה דומה לכל הגנרטורים של מקורות הנתונים.

דרישה מוקדמת

הדוגמה הזו מבוססת על ההנחה שהגדרתם מודול rust_bindgen (libbuzz_bindgen) ואפשר להמשיך לשלבים להכללת מקור שנוצר כדי להשתמש במאקרו include!(). אם עדיין לא עשיתם זאת, תוכלו לעבור אל הגדרת מודול של rust bindgen, ליצור את libbuzz_bindgen ולחזור לכאן.

חשוב לזכור שהחלקים האלה של קובץ ה-build רלוונטיים לכל הגנרטורים של מקורות קוד.

שלבים להכללת מקור שנוצר

יוצרים את הקובץ external/rust/hello_bindgen/Android.bp עם התוכן הבא:

rust_binary {
   name: "hello_bzip_bindgen_include",
   srcs: [
         // The primary rust source file must come first in this list.
         "src/lib.rs",

         // The module providing the bindgen bindings is
         // included in srcs prepended by ":".
         ":libbuzz_bindgen",
    ],

    // Dependencies need to be redeclared when generated source is used via srcs.
    shared_libs: [
        "libbuzz",
    ],
}

יוצרים את הקובץ external/rust/hello_bindgen/src/bindings.rs עם התוכן הבא:

#![allow(clippy::all)]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(unused)]
#![allow(missing_docs)]

// Note that "bzip_bindings.rs" here must match the source_stem property from
// the rust_bindgen module.
include!(concat!(env!("OUT_DIR"), "/bzip_bindings.rs"));

יוצרים את הקובץ external/rust/hello_bindgen/src/lib.rs עם התוכן הבא:

mod bindings;

fn main() {
    let mut x = bindings::foo { x: 2 };
    unsafe { bindings::fizz(1, &mut x as *mut bindings::foo) }
}

למה כדאי להשתמש בקונטיינרים לקוד מקור שנוצר

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

include!("/path/to/hello.rs");

קהילת Rust מסתמכת על סקריפטים של build.rs ועל הנחות לגבי סביבת ה-build של Cargo כדי לפתור את ההבדל הזה. במהלך ה-build, הפקודה cargo מגדירה משתנה סביבה OUT_DIR, שאליו סקריפטים של build.rs אמורים להציב את קוד המקור שנוצר. משתמשים בפקודה הבאה כדי לכלול את קוד המקור:

include!(concat!(env!("OUT_DIR"), "/hello.rs"));

זהו אתגר עבור Soong, כי הפלט של כל מודול ממוקם בספריית out/ משלו1. אין OUT_DIR יחיד שבו יחסי התלות מניבים את המקור שנוצר.

בקוד פלטפורמה, ב-AOSP מעדיפים לארוז את המקור שנוצר בקונטיינר (crate) שאפשר לייבא, מכמה סיבות:

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

כתוצאה מכך, כל סוגי המודולים ליצירת קוד מקור ב-Rust ב-Android יוצרים קוד שאפשר לקמפל ולהשתמש בו כ-crate. Soong עדיין תומך ב-crates של צד שלישי ללא שינוי, אם כל יחסי התלות שנוצרו בקוד המקור של מודול מסוים מועתקים לספרייה אחת לכל מודול, בדומה ל-Cargo. במקרים כאלה, Soong מגדיר את משתנה הסביבה OUT_DIR לספרייה הזו בזמן הידור המודול, כדי שאפשר יהיה למצוא את המקור שנוצר. עם זאת, מהסיבות שכבר מתוארות, מומלץ להשתמש במנגנון הזה בקוד הפלטפורמה רק כשזה הכרחי באופן מוחלט.


  1. אין בעיה עם שפות C/C++ ושפות דומות, כי הנתיב למקור שנוצר מסופק ישירות למהדר. 

  2. מכיוון ש-include! פועלת על ידי הכללה טקסטואלית, היא עשויה להפנות לערכים ממרחב השמות המקיף, לשנות את מרחב השמות או להשתמש במבנים כמו #![foo]. יכול להיות שיהיה קשה לשמור על האינטראקציות המשתמעות האלה. תמיד עדיף להשתמש במאקרו כשהאינטראקציה עם שאר ה-crate נדרשת באמת.