בדף הזה מוסבר איך המערכת תומכת בקוד מקור שנוצר ואיך אפשר להשתמש בו במערכת build.
כל מחוללי המקור מספקים פונקציונליות דומה של מערכת בנייה. שלושת תרחישי השימוש הנתמכים ליצירת מקורות במערכת הבנייה הם יצירת קשרי C באמצעות bindgen, ממשקי AIDL וממשקי protobuf.
ארגזים ממקור שנוצר
אפשר להשתמש בכל מודול Rust שיוצר קוד מקור כחבילת crate, בדיוק כמו אם הוא הוגדר כ-rust_library. (כלומר, אפשר להגדיר אותו כתלות במאפיינים rustlibs, rlibs ו-dylibs). הדרך הכי טובה להשתמש בקוד של פלטפורמה היא להשתמש במקור שנוצר כ-crate. למרות שיש תמיכה בפקודת המאקרו include! בקוד המקור שנוצר, המטרה העיקרית שלה היא לתמוך בקוד של צד שלישי שנמצא ב-external/.
יש מקרים שבהם קוד הפלטפורמה עדיין עשוי להשתמש במקור שנוצר באמצעות מאקרו include!(), למשל כשמשתמשים במודול genrule כדי ליצור מקור בצורה ייחודית.
שימוש בפונקציה include!() כדי לכלול מקור שנוצר
השימוש במקור שנוצר כתיבת קוד מכוסה בדוגמאות בכל דף מודול ספציפי (בהתאמה). בקטע הזה מוסבר איך להפנות למקור שנוצר באמצעות מאקרו include!(). שימו לב שהתהליך הזה דומה לכל הגנרטורים של מקורות.
דרישות מוקדמות
הדוגמה הזו מבוססת על ההנחה שהגדרתם מודול rust_bindgen (libbuzz_bindgen) ואפשר להמשיך אל השלבים להכללת מקור שנוצר כדי להשתמש בפקודת המאקרו include!(). אם לא עשיתם את זה, אתם צריכים לעבור אל הגדרת מודול rust bindgen, ליצור libbuzz_bindgen ואז לחזור לכאן.
שימו לב שהחלקים בקובץ הבנייה שמתוארים כאן רלוונטיים לכל מחוללי המקור.
איך לכלול מקור שנוצר
יוצרים את הקובץ 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) }
}
למה כדאי להשתמש ב-crates עבור קוד מקור שנוצר
בניגוד לקומפיילרים של 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.
- מומלץ להימנע מאינטראקציות מרומזות2 בין קוד שנוצר לבין ה-crate שמסביב.
- כדי להפחית את העומס על הזיכרון והדיסק, מקשרים באופן דינמי בין מקורות שנוצרו בדרך כלל.
כתוצאה מכך, כל סוגי המודולים ליצירת קוד המקור של Rust ב-Android יוצרים קוד שאפשר לקמפל ולהשתמש בו כחבילת crate.
Soong עדיין תומך בתיקיות של צד שלישי ללא שינוי, אם כל יחסי התלות של המקור שנוצרו עבור מודול מועתקים לספרייה אחת לכל מודול, בדומה ל-Cargo. במקרים כאלה, Soong מגדיר את משתנה הסביבה OUT_DIR לספרייה הזו כשמהדרים את המודול, כדי שאפשר יהיה למצוא את המקור שנוצר.
עם זאת, מהסיבות שכבר תוארו, מומלץ להשתמש במנגנון הזה בקוד של הפלטפורמה רק כשאין ברירה אחרת.
-
הדבר לא יוצר בעיות בשפות C/C++ ובשפות דומות, כי הנתיב למקור שנוצר מסופק ישירות לקומפיילר. ↩
-
הפונקציה
include!פועלת על ידי הכללה של טקסט, ולכן היא עשויה להפנות לערכים ממרחב השמות המקיף, לשנות את מרחב השמות או להשתמש במבנים כמו#![foo]. יכול להיות שיהיה קשה לשמור על האינטראקציות המרומזות האלה. תמיד עדיף להשתמש בפקודות מאקרו כשנדרשת אינטראקציה עם שאר ה-crate. ↩