סגנון הקוד HIDL דומה לקוד C++ במסגרת Android, עם 4 רווחים כניסת פסקה ושמות קבצים מעורבים. הצהרות על חבילות, ייבוא ו-docstring דומים לאלו שב-Java, עם שינויים קלים.
הדוגמאות הבאות עבור IFoo.hal
ו-types.hal
ממחישים סגנונות של קוד HIDL ומספקים קישורים מהירים לפרטים על כל סגנון
(IFooClientCallback.hal
, IBar.hal
וגם
IBaz.hal
הושמטו).
hardware/interfaces/foo/1.0/IFoo.hal |
---|
/* * (License Notice) */ package android.hardware.foo@1.0; import android.hardware.bar@1.0::IBar; import IBaz; import IFooClientCallback; /** * IFoo is an interface that… */ interface IFoo { /** * This is a multiline docstring. * * @return result 0 if successful, nonzero otherwise. */ foo() generates (FooStatus result); /** * Restart controller by power cycle. * * @param bar callback interface that… * @return result 0 if successful, nonzero otherwise. */ powerCycle(IBar bar) generates (FooStatus result); /** Single line docstring. */ baz(); /** * The bar function. * * @param clientCallback callback after function is called * @param baz related baz object * @param data input data blob */ bar(IFooClientCallback clientCallback, IBaz baz, FooData data); }; |
hardware/interfaces/foo/1.0/types.hal |
---|
/* * (License Notice) */ package android.hardware.foo@1.0; /** Replied status. */ enum Status : int32_t { OK, /* invalid arguments */ ERR_ARG, /* note, no transport related errors */ ERR_UNKNOWN = -1, }; struct ArgData { int32_t[20] someArray; vec<uint8_t> data; }; |
מוסכמות מתן שמות
שמות הפונקציות, שמות המשתנים ושמות הקבצים צריכים להיות תיאוריים. להימנע
בקיצור יתר. יש להתייחס לראשי תיבות כמילים (לדוגמה, יש להשתמש ב-INfc
במקום זאת
מתוך INFC
).
מבנה הספרייה ומתן שמות לקבצים
מבנה הספרייה אמור להיראות כך:
ROOT-DIRECTORY
MODULE
SUBMODULE
(אופציונלי, יכול להיות יותר מאירוע אחד רמה)VERSION
Android.mk
IINTERFACE_1.hal
IINTERFACE_2.hal
…
IINTERFACE_N.hal
types.hal
(אופציונלי)
איפה:
ROOT-DIRECTORY
הוא:hardware/interfaces
לחבילות ליבה HIDL.vendor/VENDOR/interfaces
לחבילות של ספקים, כאשרVENDOR
מתייחס לספק SoC או OEM/ODM.
MODULE
צריכה להיות מילה קטנה אחת שמתארת במערכת המשנה (לדוגמה,nfc
). אם נדרשת יותר ממילה אחת, אפשר להשתמש בSUBMODULE
בתצוגת עץ. יכולה להיות יותר מרמה אחת של באמצעות סידור פנימי.VERSION
צריכה להיות אותה הגרסה בדיוק (major.minor) כפי שמתואר בגרסאות.IINTERFACE_X
צריך להיות שם הממשק עםUpperCamelCase
/PascalCase
(לדוגמה,INfc
) כפי שמתואר בשמות הממשקים.
דוגמה:
hardware/interfaces
nfc
1.0
Android.mk
INfc.hal
INfcClientCallback.hal
types.hal
הערה: כל הקבצים חייבים להיות עם תקרה להפעלה הרשאות (ב-Git).
שמות של חבילות
שמות החבילות צריכים לכלול את השם הבא שהוגדר במלואו
(FQN) (נקרא PACKAGE-NAME
):
PACKAGE.MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION
איפה:
PACKAGE
היא החבילה שממופה אלROOT-DIRECTORY
. באופן ספציפי,PACKAGE
הוא:android.hardware
לחבילות ליבה HIDL (מיפוי ל:hardware/interfaces
).vendor.VENDOR.hardware
לחבילות של ספקים, שבהן המאפייןVENDOR
מתייחס לספק SoC או ל-OEM (יצרן ציוד מקורי)/ODM (מיפוי). אלvendor/VENDOR/interfaces
).
MODULE[.SUBMODULE[.SUBMODULE[…]]]@VERSION
הם בדיוק אותם שמות תיקיות במבנה שמתואר מבנה הספרייה.- שמות החבילות צריכים להיות באותיות קטנות. אם הן באורך של יותר ממילה אחת, המאפיין
מילים צריכות לשמש כמודולים משנה או לכתוב אותן ב-
snake_case
. - אין לכלול רווחים.
ה-FQN תמיד משמש בהצהרות על חבילות.
גרסאות
הגרסאות צריכות להיות בפורמט הבא:
MAJOR.MINOR
גם הגרסה של MAJOR וגם הגרסה MINOR צריכות להיות אחידות מספר שלם. HIDL משתמשת בסמנטיקה ניהול גרסאות.
ייבוא
לייבוא יש אחד משלושת הפורמטים הבאים:
- ייבוא של כל החבילה:
import PACKAGE-NAME;
- ייבוא חלקי:
import PACKAGE-NAME::UDT;
(או, אם הייבוא בוצע הסוג נמצא באותה חבילה,import UDT;
- ייבוא סוגים בלבד:
import PACKAGE-NAME::types;
הערך PACKAGE-NAME
מופיע לפי הפורמט ב-
שמות של חבילות. של החבילה הנוכחית
types.hal
(אם קיים) מיובא באופן אוטומטי (אין לייבא אותו)
את הערך הזה באופן מפורש).
שמות בעלי הרשאות מלאות (FQN)
רק במקרה הצורך, כשמדובר בייבוא של סוג מוגדר על ידי המשתמש, צריך להשתמש בשמות מוגדרים במלואם.
יש להשמיט את PACKAGE-NAME
אם סוג הייבוא זהה
חבילה. FQN לא יכול להכיל רווחים. דוגמה לשם שמוגדר במלואו:
android.hardware.nfc@1.0::INfcClientCallback
בקובץ אחר תחת android.hardware.nfc@1.0
, יש לעיין ב
מעל לממשק כ-INfcClientCallback
. אחרת, משתמשים רק
מוגדר במלואו.
ייבוא של קבוצות והזמנות
צריך להוסיף שורה ריקה אחרי הצהרת החבילה (לפני הייבוא). כל ייבוא צריכות לתפוס שורה אחת ולא להוסיף כניסת פיסקה. ייבוא קבוצתי ב הסדר הבא:
- חבילות
android.hardware
אחרות (יש להשתמש בשמות מוגדרים במלואם). - חבילות אחרות של
vendor.VENDOR
(בשימוש מלא שמות).- כל ספק צריך להיות קבוצה.
- סדרו ספקים לפי סדר אלפביתי.
- מיובא מממשקים אחרים באותה חבילה (שימוש בשמות פשוטים).
צריך להוסיף שורה ריקה בין הקבוצות. אפשר למיין את הייבוא בתוך כל קבוצה לפי סדר האלף-בית. דוגמה:
import android.hardware.nfc@1.0::INfc; import android.hardware.nfc@1.0::INfcClientCallback; /* Importing the whole module. */ import vendor.barvendor.bar@3.1; import vendor.foovendor.foo@2.2::IFooBar; import vendor.foovendor.foo@2.2::IFooFoo; import IBar; import IFoo;
שמות הממשק
שמות הממשק צריכים להתחיל ב-I
ואחריו ב-
שם UpperCamelCase
/PascalCase
. ממשק עם שם
חובה להגדיר את IFoo
בקובץ IFoo.hal
. הקובץ הזה
יכול להכיל הגדרות רק לממשק של IFoo
(הממשק
INAME
צריך להיות בINAME.hal
).
פונקציות
לשמות של פונקציות, לארגומנטים ולשמות של משתנים, משתמשים
lowerCamelCase
דוגמה:
open(INfcClientCallback clientCallback) generates (int32_t retVal); oneway pingAlive(IFooCallback cb);
שמות שדות של מבנה ואיחוד
לשמות של שדות מבניים או איחוד, משתמשים ב-lowerCamelCase
. דוגמה:
struct FooReply { vec<uint8_t> replyData; }
שמות של סוגי תווים
שמות של סוגים מתייחסים להגדרות של מבני ואיחוד, הגדרות של סוג טיפוס טיפוסים בני מנייה (enum) ו
typedef
שנ'. בשמות האלה, משתמשים
UpperCamelCase
מתוך PascalCase
. למשל:
enum NfcStatus : int32_t { /*...*/ }; struct NfcData { /*...*/ };
ערכי טיפוסים בני מנייה (enum)
הערכים של הטיפוס בן המנייה צריכים להיות UPPER_CASE_WITH_UNDERSCORES
. כשמעבירים את העכבר,
ערכי enum כארגומנטים של פונקציה והחזרתם כארגומנטים של פונקציה, השתמשו
סוג טיפוסים בני מנייה (enum) בפועל (לא סוג המספר השלם הבסיסי). דוגמה:
enum NfcStatus : int32_t { HAL_NFC_STATUS_OK = 0, HAL_NFC_STATUS_FAILED = 1, HAL_NFC_STATUS_ERR_TRANSPORT = 2, HAL_NFC_STATUS_ERR_CMD_TIMEOUT = 3, HAL_NFC_STATUS_REFUSED = 4 };
הערה: הסוג הבסיסי של טיפוס טיפוסים בני מנייה (enum) הוא שהוצהר במפורש אחרי הנקודתיים. מכיוון שזה לא תלוי מהדר, שימוש ב- הסוג של enum בפועל ברור יותר.
לשמות של ערכי enum שמוגדרים במלואם, צריך להשתמש בנקודתיים. בין השם של סוג enum לבין שם של ערך enum:
PACKAGE-NAME::UDT[.UDT[.UDT[…]]:ENUM_VALUE_NAME
אסור שיהיו רווחים בשם שמוגדר במלואו. שימוש בהרשאת גישה מלאה את השם רק במקרה הצורך והשמטה של חלקים מיותרים. דוגמה:
android.hardware.foo@1.0::IFoo.IFooInternal.FooEnum:ENUM_OK
תגובות
לתגובה בשורה אחת, //
, /* */
ו-/** */
בסדר.
// This is a single line comment /* This is also single line comment */ /** This is documentation comment */
-
אפשר להשתמש ב
/* */
כדי לכתוב תגובות. אמנם HIDL תומך ב-//
לתגובות, לא מומלץ להוסיף אותם כי הם לא מופיעים בפלט שנוצר. - אפשר להשתמש ב-
/** */
למסמכי התיעוד שנוצרו. אפשר להחיל את האפשרויות האלה רק להצהרות מסוג 'סוג', 'שיטה', 'שדה' ו'ערך טיפוסים בני מנייה (enum)'. דוגמה:/** Replied status */ enum TeleportStatus { /** Object entirely teleported. */ OK = 0, /** Methods return this if teleportation is not completed. */ ERROR_TELEPORT = 1, /** * Teleportation could not be completed due to an object * obstructing the path. */ ERROR_OBJECT = 2, ... }
- אפשר להתחיל תגובות מרובות שורות באמצעות
/**
בשורה נפרדת. השתמשו ב-*
בתחילת כל שורה. מסיימים את התגובה עם*/
בשורה נפרדת, מיישרים את הכוכביות. דוגמה:/** * My multi-line * comment */
- הודעה לגבי רישוי ויומני שינויים צריכים להתחיל שורה חדשה עם
/*
(כוכבית יחידה), משתמשים ב-*
בתחילת כל שורה ומציבים*/
בשורה האחרונה בלבד (הכוכבים אמורים ליישר). דוגמה:/* * Copyright (C) 2017 The Android Open Source Project * ... */ /* * Changelog: * ... */
תגובות לקבצים
עליכם להתחיל כל קובץ עם הודעת הרישוי המתאימה. במהדורות ליבה עם HAL
להיות רישיון AOSP Apache
development/docs/copyright-templates/c.txt
חשוב לעדכן את השנה ולהשתמש בטקסט /* */
בסגנון תגובות מרובות שורות
כפי שהוסבר למעלה.
אפשר גם להוסיף שורה ריקה אחרי הודעת הרישיון, ואחריה
באמצעות מידע מסוג יומן שינויים/גרסה. שימוש בסגנון /* */
מרובה שורות כפי שהוסבר למעלה, מציבים את השורה הריקה אחרי
יומן שינויים, ופועלים לפי הצהרת החבילה.
תגובות TODO
המשימות לביצוע משימות צריכות לכלול את המחרוזת TODO
באותיות גדולות בלבד, ואחריהן
נקודתיים. דוגמה:
// TODO: remove this code before foo is checked in.
תגובות TODO מותרות רק במהלך הפיתוח; הם חייבים לא קיימות בממשקים שפורסמו.
הערות בממשק ובפונקציות (docstrings)
כדי להשתמש ב-docstrings עם מספר שורות ושורה אחת, צריך להשתמש ב-/** */
. אני לא רוצה להשתמש
//
לקובצי docstring.
קובצי Docstring לממשקים צריכים לתאר מנגנונים כלליים של ממשק, רציונליות עיצוב, מטרה וכו'. Docsstring לפונקציות צריך להיות ספציפי לפונקציה (תיעוד ברמת החבילה נכנס לקובץ README בתוך ספריית החבילות).
/** * IFooController is the controller for foos. */ interface IFooController { /** * Opens the controller. * * @return status HAL_FOO_OK if successful. */ open() generates (FooStatus status); /** Close the controller. */ close(); };
צריך להוסיף @param
ו-@return
לכל אחד מהם
פרמטר/ערך החזרה:
- צריך להוסיף את המאפיין
@param
לכל פרמטר. הוא צריך להיות ואחריו שם הפרמטר ואז docstring. - צריך להוסיף את המאפיין
@return
לכל ערך של החזרה. הוא אחרי הערך המוחזר צריך להופיע השם של הערך המוחזר ואז ה-docstring.
דוגמה:
/** * Explain what foo does. * * @param arg1 explain what arg1 is * @param arg2 explain what arg2 is * @return ret1 explain what ret1 is * @return ret2 explain what ret2 is */ foo(T arg1, T arg2) generates (S ret1, S ret2);
עיצוב כללים
כללי פורמט כלליים כוללים:
- אורך השורה. כל שורת טקסט צריכה להיות לכל היותר באורך של 100 עמודות.
- רווחים לבנים. ללא רווח לבן בסוף הקווים; שורות ריקות אסור לכלול רווחים לבנים.
- מרחבים לעומת כרטיסיות. מותר להשתמש רק במרחבים משותפים.
- גודל כניסת פיסקה. צריך להשתמש ב-4 רווחים לבלוקים ו 8 רווחים לרוחב של קווים
- מרוצים. מלבד הערה
ערכים, סוגריים מסולסלים פתוחים מופיעים באותה השורה הקודמת.
אלא תופסן סגירה והנקודה-פסיק הבאה תופסת
את כל השורה. דוגמה:
interface INfc { close(); };
הצהרה על חבילה
הצהרת החבילה צריכה להיות בחלק העליון של הקובץ אחרי הרישיון הודעה, צריכה לתפוס את כל השורה ואין בה כניסה של כניסת פסקה. החבילות הן מוצהר באמצעות הפורמט הבא (לפורמט השם, ראה שמות של חבילות):
package PACKAGE-NAME;
דוגמה:
package android.hardware.nfc@1.0;
הצהרות לגבי פונקציות
שם הפונקציה, הפרמטרים, generates
והערכים המוחזרים
יהיו באותו קו אם הן מתאימות. דוגמה:
interface IFoo { /** ... */ easyMethod(int32_t data) generates (int32_t result); };
אם הם לא מתאימים לאותה שורה, נסו להוסיף פרמטרים ולהחזיר
באותה רמת כניסה ולהבחין בין generate
כדי לעזור
שהקורא רואה במהירות את הפרמטרים ומחזיר ערכים. דוגמה:
interface IFoo { suchALongMethodThatCannotFitInOneLine(int32_t theFirstVeryLongParameter, int32_t anotherVeryLongParameter); anEvenLongerMethodThatCannotFitInOneLine(int32_t theFirstLongParameter, int32_t anotherVeryLongParameter) generates (int32_t theFirstReturnValue, int32_t anotherReturnValue); superSuperSuperSuperSuperSuperSuperLongMethodThatYouWillHateToType( int32_t theFirstVeryLongParameter, // 8 spaces int32_t anotherVeryLongParameter ) generates ( int32_t theFirstReturnValue, int32_t anotherReturnValue ); /* method name is even shorter than 'generates' */ foobar(AReallyReallyLongType aReallyReallyLongParameter, AReallyReallyLongType anotherReallyReallyLongParameter) generates (ASuperLongType aSuperLongReturnValue, // 4 spaces ASuperLongType anotherSuperLongReturnValue); }
פרטים נוספים:
- סוגר פתוח תמיד יהיה באותה שורה כמו שם הפונקציה.
- אין רווחים בין שם הפונקציה לבין הסוגריים הפתוחים.
- ללא רווחים בין הסוגריים והפרמטרים, חוץ ממקרים שבהם יש הם פידים של שורות ביניהם.
- אם
generates
נמצאת באותה שורה עם סגירת הסגירה הקודמת בסוגריים, צריך להשתמש ברווח קודם. אם הערך שלgenerates
זהה בתור סוגר הפתוח הבא, ואחריו רווח. - צריך להתאים את כל הפרמטרים ולהחזיר ערכים (אם אפשר).
- ברירת המחדל לכניסת הפיסקה היא 4 רווחים.
- פרמטרים שמקיפים מותאמים לפרמטרים הראשונים בשורה הקודמת, אחרת, יש להם כניסת רווח של 8.
הערות
יש להשתמש בפורמט הבא להוספת הערות:
@annotate(keyword = value, keyword = {value, value, value})
ממיינים את ההערות בסדר אלפביתי ומשתמשים ברווחים סביב סימני השווה. דוגמה:
@callflow(key = value) @entry @exit
מוודאים שהערה תופסת את כל השורה. לדוגמה:
/* Good */ @entry @exit /* Bad */ @entry @exit
אם אי אפשר להציג הערות באותה שורה, אפשר להוסיף 8 רווחים לכניסת פסקה. דוגמה:
@annotate( keyword = value, keyword = { value, value }, keyword = value)
אם לא ניתן להכניס את כל מערך הערכים לאותה שורה, יש להוסיף מעברי שורה אחרי
פותחים סוגריים מסולסלים {
ואחרי כל פסיק בתוך המערך. סגירת מקום
בסוגריים מיד אחרי הערך האחרון. אל תצמידו את הסוגריים אם יש
רק ערך אחד.
אם כל מערך הערכים יכול להתאים לאותה שורה, אין להשתמש ברווחים אחרי זה לפתוח סוגריים מסולסלים ולפני סגירת סוגריים מסולסלים ולהשתמש ברווח אחד אחרי כל פסיק. לדוגמה:
/* Good */ @callflow(key = {"val", "val"}) /* Bad */ @callflow(key = { "val","val" })
אסור שיהיו שורות ריקות בין ההערות לבין הפונקציה הצהרה. לדוגמה:
/* Good */ @entry foo(); /* Bad */ @entry foo();
הצהרות Enum
להצהרות מסוג 'טיפוסים בני מנייה (enum)', צריך להשתמש בכללים הבאים:
- אם ההצהרות מסוג 'טיפוסים בני מנייה (enum)' משותפות עם חבילה אחרת, צריך למלא את ההצהרות
ב-
types.hal
במקום להטמיע בתוך ממשק. - צריך להשתמש ברווח לפני ואחרי הנקודתיים וברווח אחרי הסוג הבסיסי לפני הסוגר הפתוח.
- יכול להיות שבערך enum האחרון אין פסיק מיותר.
הצהרות מבנה
צריך להשתמש בכללים הבאים להצהרות Build:
- אם הצהרות מבנה משותפות עם חבילה אחרת, צריך להוסיף את ההצהרות
ב-
types.hal
במקום להטמיע בתוך ממשק. - צריך להוסיף רווח אחרי השם של סוג ה-build לפני הסוגריים הפתוחים.
- יישור שמות של שדות (אופציונלי). דוגמה:
struct MyStruct { vec<uint8_t> data; int32_t someInt; }
הצהרות מערך
אין להוסיף רווחים בין הרכיבים הבאים:
- סוג הרכיב וסוגר מרובע פתוח.
- פתיחת סוגר מרובע וגודל מערך.
- גודל המערך וסגירת סוגר מרובעת.
- אם יש יותר מסוגריים מרובעים, סוגרים את הסוגר הריבועי ואת הסוגר הפתוח הבא קיים.
לדוגמה:
/* Good */ int32_t[5] array; /* Good */ int32_t[5][6] multiDimArray; /* Bad */ int32_t [ 5 ] [ 6 ] array;
וקטורים
אין להוסיף רווחים בין הרכיבים הבאים:
vec
ותו סוגר זוויתי פתוח.- סוגר זוויתי פתוח וסוג רכיב (חריג: גם סוג רכיב
vec
). - סוג הרכיב וסוגר זווית סגור (יוצא מן הכלל: גם סוג הרכיב הזה
vec
).
לדוגמה:
/* Good */ vec<int32_t> array; /* Good */ vec<vec<int32_t>> array; /* Good */ vec< vec<int32_t> > array; /* Bad */ vec < int32_t > array; /* Bad */ vec < vec < int32_t > > array;