פלטפורמת Android מכילה הרבה קובצי XML לאחסון נתוני תצורה (לדוגמה, תצורת אודיו). הרבה מקובצי ה-XML נמצאים במחיצה vendor
, אבל הם נקראים במחיצה system
. במקרה כזה, הסכימה של קובץ ה-XML משמשת כממשק בין שתי המחיצות, ולכן צריך לציין את הסכימה באופן מפורש ולפתח אותה באופן שתואם לאחור.
לפני Android 10, הפלטפורמה לא סיפקה מנגנונים שמאפשרים לדרוש ציון של סכימת XML ושימוש בה, או למנוע שינויים לא תואמים בסכימה. Android 10 מספקת את המנגנון הזה, שנקרא Config File Schema API. המנגנון הזה מורכב מכלי שנקרא xsdc
ומכלל בנייה שנקרא xsd_config
.
הכלי xsdc
הוא קומפיילר של מסמך סכימת XML (XSD). הכלי מנתח קובץ XSD שמתאר את הסכימה של קובץ XML ומייצר קוד Java ו-C++. הקוד שנוצר מנתח קובצי XML שתואמים לסכימת XSD לעץ של אובייקטים, שכל אחד מהם מדמה תג XML. מאפייני XML מוגדרים כשדות של האובייקטים.
כלל ה-build xsd_config
משלב את הכלי xsdc
במערכת ה-build.
עבור קובץ קלט XSD נתון, כלל ה-build יוצר ספריות Java ו-C++. אפשר לקשר את הספריות למודולים שבהם קוראים ומשתמשים בקובצי ה-XML שתואמים ל-XSD. אתם יכולים להשתמש בכלל ה-build עבור קובצי ה-XML שלכם שמשמשים במחיצות system
ו-vendor
.
Build Config File Schema API
בקטע הזה מוסבר איך ליצור את Config File Schema API.
הגדרת כלל ה-build xsd_config ב-Android.bp
הכלי xsdc
יוצר את קוד הניתוח באמצעות כלל הבנייה xsd_config
. המאפיין package_name
של כלל הבנייה xsd_config
קובע את שם החבילה של קוד ה-Java שנוצר.
דוגמה לכלל בנייה xsd_config
ב-Android.bp
:
xsd_config {
name: "hal_manifest",
srcs: ["hal_manifest.xsd"],
package_name: "hal.manifest",
}
דוגמה למבנה של ספרייה:
├── Android.bp
├── api
│ ├── current.txt
│ ├── last_current.txt
│ ├── last_removed.txt
│ └── removed.txt
└── hal_manifest.xsd
מערכת ה-build יוצרת רשימת API באמצעות קוד Java שנוצר ובודקת את ה-API מול הרשימה. בדיקת ה-API הזו מתווספת ל-DroidCore ומופעלת ב-m -j
.
יצירת קבצים של רשימות API
בדיקות ה-API דורשות קבצים של רשימות API בקוד המקור.
הרשימה של הקבצים ב-API כוללת:
-
current.txt
ו-removed.txt
בודקים אם בוצעו שינויים בממשקי ה-API על ידי השוואה לקבצים של ממשקי API שנוצרו בזמן ה-build. -
last_current.txt
ו-last_removed.txt
בודקים אם ממשקי ה-API תואמים לאחור על ידי השוואה לקובצי API.
כדי ליצור את קובצי רשימות ה-API:
- יוצרים קבצים של רשימות ריקות.
- מריצים את הפקודה
make update-api
.
שימוש בקוד מנתח שנוצר
כדי להשתמש בקוד Java שנוצר, מוסיפים את :
כקידומת לשם המודול xsd_config
במאפיין Java srcs
. החבילה של קוד ה-Java שנוצר זהה לנכס package_name
.
java_library {
name: "vintf_test_java",
srcs: [
"srcs/**/*.java"
":hal_manifest"
],
}
כדי להשתמש בקוד C++ שנוצר, מוסיפים את שם המודול xsd_config
למאפיינים generated_sources
ו-generated_headers
. מוסיפים את libxml2
ל-static_libs
או ל-shared_libs
, כי libxml2
נדרש בקוד של מנתח התוכן שנוצר. מרחב השמות של קוד C++ שנוצר זהה למאפיין package_name
. לדוגמה, אם שם המודול xsd_config
הוא hal.manifest
, מרחב השמות הוא hal::manifest
.
cc_library{
name: "vintf_test_cpp",
srcs: ["main.cpp"],
generated_sources: ["hal_manifest"],
generated_headers: ["hal_manifest"],
shared_libs: ["libxml2"],
}
שימוש בכלי הניתוח
כדי להשתמש בקוד של מנתח ה-Java, משתמשים בשיטה XmlParser#read
או read{class-name}
כדי להחזיר את המחלקה של רכיב הבסיס. הניתוח מתבצע בשלב הזה.
import hal.manifest.*;
…
class HalInfo {
public String name;
public String format;
public String optional;
…
}
void readHalManifestFromXml(File file) {
…
try (InputStream str = new BufferedInputStream(new FileInputStream(file))) {
Manifest manifest = XmlParser.read(str);
for (Hal hal : manifest.getHal()) {
HalInfo halinfo;
HalInfo.name = hal.getName();
HalInfo.format = hal.getFormat();
HalInfo.optional = hal.getOptional();
…
}
}
…
}
כדי להשתמש בקוד של מנתח ה-C++, צריך לכלול קודם את קובץ הכותרת. השם של קובץ הכותרת הוא שם החבילה עם נקודות (.) שהומרו לקווים תחתונים (_).
לאחר מכן משתמשים בשיטה read
או read{class-name}
כדי להחזיר את המחלקה של רכיב הבסיס. הניתוח מתבצע בשלב הזה. הערך המוחזר הוא std::optional<>
.
include "hal_manifest.h"
…
using namespace hal::manifest
struct HalInfo {
public std::string name;
public std::string format;
public std::string optional;
…
};
void readHalManifestFromXml(std::string file_name) {
…
Manifest manifest = *read(file_name.c_str());
for (Hal hal : manifest.getHal()) {
struct HalInfo halinfo;
HalInfo.name = hal.getName();
HalInfo.format = hal.getFormat();
HalInfo.optional = hal.getOptional();
…
}
…
}
כל ממשקי ה-API שזמינים לשימוש במנתח נמצאים ב-api/current.txt
. כדי לשמור על אחידות, כל שמות הרכיבים והמאפיינים מומרים ל-camel case (לדוגמה, ElementName
) ומשמשים כמשתנה, כשיטה וכשם המחלקה המתאימים. אפשר לקבל את המחלקה של רכיב הבסיס המנותח באמצעות הפונקציה read{class-name}
. אם יש רק רכיב בסיס אחד, שם הפונקציה הוא read
. אפשר לקבל את הערך של רכיב משנה או מאפיין שנותחו באמצעות הפונקציה get{variable-name}
.
יצירת קוד של כלי ניתוח
ברוב המקרים, אין צורך להפעיל את xsdc
ישירות. במקום זאת, צריך להשתמש ב-xsd_config
build
rule, כמו שמתואר במאמר הגדרת כלל ה-build של xsd_config ב-Android.bp. בקטע הזה מוסבר על ממשק שורת הפקודה xsdc
, רק כדי לספק מידע מלא. המידע הזה יכול להיות שימושי לניפוי באגים.
צריך לספק לכלי xsdc
את הנתיב לקובץ ה-XSD ואת החבילה. החבילה היא שם חבילה בקוד Java ומרחב שמות בקוד C++. האפשרויות לקביעה אם הקוד שנוצר הוא Java או C הן -j
או -c
, בהתאמה. האפשרות -o
היא הנתיב של ספריית הפלט.
usage: xsdc path/to/xsd_file.xsd [-c] [-j] [-o <arg>] [-p]
-c,--cpp Generate C++ code.
-j,--java Generate Java code.
-o,--outDir <arg> Out Directory
-p,--package Package name of the generated java file. file name of
generated C++ file and header
פקודה לדוגמה:
$ xsdc audio_policy_configuration.xsd -p audio.policy -j