本文件說明 FIDL 的 API 版本管理功能。如需 Fuchsia API 演進指南的相關指引,請參閱 Fuchsia API 演進指南。
摘要
FIDL 版本管理可讓您代表 FIDL 程式庫隨時間的變化。變更時,您可以使用 @available
屬性說明變更發生的時間 (即哪個版本)。如要產生繫結,請將 --available
標記傳送至 fidlc,並指定一或多個版本。
FIDL 版本管理提供 API 版本管理,而非 ABI 版本管理。無法在執行階段查詢版本。變更可能會造成 API 破壞,但應與 ABI 相容。FIDL 編譯器會執行一些基本驗證,但無法保證 ABI 相容性。
概念
版本管理的單位是一組稱為「平台」的程式庫。按照慣例,程式庫是以平台名稱開頭。舉例來說,fuchsia.mem
和 fuchsia.web
程式庫屬於 fuchsia
平台。
每個平台都有線性版本記錄。版本是介於 1 到 2^31-1 (含) 之間的整數,或特殊版本 NEXT
和 HEAD
之一。NEXT
版本用於計畫在下一個編號版本中進行的變更。HEAD
版本用於最新的不穩定變更。
如果 FIDL 程式庫沒有任何 @available
屬性,則屬於 unversioned
平台。這個平台只有一個版本:HEAD
。fuchsia.git
中的 FIDL 程式庫必須指定 @available
屬性。
目標版本
當您指定單一版本時,繫結會包含該版本中可用的所有元素,如 FIDL 檔案中的 @available
引數所指定。
指定一組版本時,繫結會包含組合中任何版本可用的所有元素。對於屬於 replaced
的元素,繫結只會包含最新定義。
無論您指定哪個版本組合,如果 FIDL 編譯成功,則該組合的所有子集和所有可能的單例組合也保證會成功。
語法
任何 FIDL 元素都可以使用 @available
屬性。它採用下列引數:
引數 | 類型 | 說明 |
---|---|---|
platform |
string |
程式庫群組名稱 (請參閱概念);僅適用於 library |
added |
uint64 |
整數、NEXT 或 HEAD |
deprecated |
uint64 |
整數、NEXT 或 HEAD |
removed |
uint64 |
整數、NEXT 或 HEAD |
replaced |
uint64 |
整數、NEXT 或 HEAD |
note |
string |
提供 deprecated 、removed 和/或 replaced 的背景資訊 |
renamed |
string |
removed 或 replaced 元素的新名稱;僅供成員使用 |
引數有一些限制:
- 所有引數皆為選用,但至少須提供一個引數。
- 引數必須是常值,而非
const
宣告的參照。 removed
和replaced
引數互斥。- 引數必須遵循
added <= deprecated < removed
或added <= deprecated < replaced
。 - 如未指定,
added
、deprecated
、removed
和replaced
引數就會「繼承」父項元素。
例如:
@available(added=1, deprecated=2, removed=3)
const ANSWER uint64 = 42;
如果 @available
用於程式庫中的任何位置,也必須顯示在程式庫宣告中。對於單一檔案的程式庫,這項作業相當簡單。如果程式庫具有兩個以上的 .fidl
檔案,則只有一個檔案能為其程式庫宣告加註。(理論上,程式庫會被視為單一「元素」,且其屬性會與各個檔案中的屬性合併,因此為多個檔案加上註解會產生重複的屬性錯誤)。FIDL 風格指南建議為此目的建立名為 overview.fidl
的檔案。
在程式庫宣告中,@available
屬性需要 added
引數,並允許 platform
引數。如果省略 platform
,系統會預設為程式庫名稱的第一個元件。例如:
// Equivalent to `@available(platform="fuchsia", added=1)`.
@available(added=1)
library fuchsia.examples.docs;
修飾符
您可以透過 FIDL 版本管理功能,在特定版本中新增或移除修飾符。修飾符之後,您可以像在 @available
之後編寫引數一樣,使用括號編寫引數。不過,系統只允許使用 added
和 removed
引數。
以下範例說明如何將列舉從嚴格變更為彈性:
type Color = strict(removed=2) flexible(added=2) enum {
RED = 1;
};
所有修飾符都支援以下語法:strict
、flexible
、resource
、closed
、ajar
和 open
。不過,如果在雙向方法中變更 strict
或 flexible
修飾符,則不允許使用錯誤語法。
指定一組版本時,編譯器會使用最新的修飾符。在上述範例中,如果組合包含任何等於或大於 2 的版本,則列舉會彈性,即使其中也包含 1。
繼承
@available
的引數會從程式庫宣告流程到頂層宣告,以及從各個頂層宣告傳至其成員。例如,如果在第 5 版新增資料表,就無須在其成員上重複這項註解,因為它們不能存在於資料表本身之前。以下是較複雜的繼承範例:
@available(added=2, deprecated=3)
open protocol Versioned {
// Equivalent to `@available(added=2, deprecated=3, removed=4)`.
@available(removed=4)
flexible Removed(table {
// Equivalent to `@available(added=3, deprecated=3, removed=4)`.
@available(added=3)
1: message string;
});
};
淘汰
淘汰用於表示元素日後會遭到移除。淘汰元素時,請在文件註解中加入 # Deprecation
部分,並提供詳細說明,並在 @available
屬性中加入 note
引數,並提供簡短操作說明。例如:
open protocol Example {
/// (Description of the method.)
///
/// # Deprecation
///
/// (Detailed explanation of why the method is deprecated, the timeline for
/// removing it, and what should be used instead.)
@available(deprecated=5, removed=6, note="use Replacement")
flexible Deprecated();
@available(added=5)
flexible Replacement();
};
自 2024 年 6 月起,廢止不會對繫結造成影響。不過,FIDL 團隊計畫讓其以目標語言產生淘汰註解。舉例來說,上述範例可以在 Rust 繫結中產生#[deprecated = "Use
替換項目."]
。
身分識別
FIDL 版本管理的用途是區分移除和取代元素。為此,它會依據 API 和 ABI 身分概念。元素的 API 身分即為元素的名稱。ABI 身分會因元素類型而異:
- 位元/列舉成員:值,例如
VALUE = 5;
中的 5 - 結構成員:偏移,例如 0 代表第一位成員
- 資料表/聯集成員:序數,例如
5: name string;
中的 5 - 通訊協定成員:選取器,例如
library example; protocol Foo { Bar(); };
中的「example/Foo.Bar」
其他元素 (例如類型宣告和通訊協定) 則沒有 ABI 身分。
取代
replaced
引數可讓您編寫全新的定義,變更特定版本的元素。這是變更 FIDL 元素特定層面的唯一方法,包括:
- 常數的值
- struct、資料表或聯集成員的類型
- 宣告的種類,例如將結構體變更為別名
- 方法上存在
error
語法 - 元素的屬性
如要取代 N
版的元素,請使用 @available(replaced=N)
為舊定義加上註解,並使用 @available(added=N)
為新定義加上註解。
以下舉例說明如何變更常數值:
@available(replaced=5)
const MAX_NAME_LEN uint32 = 32;
@available(added=5)
const MAX_NAME_LEN uint32 = 64;
以下是另一個範例,說明如何變更表格欄位的類型:
type Data = resource table {
@available(replaced=5)
1: name string:32;
@available(added=5)
1: name string:64;
};
FIDL 編譯器會驗證每個 @available(replaced=N)
元素是否有與之相符的 @available(added=N)
元素,且具有相同的身分。也會驗證每個 @available(removed=N)
元素是否「沒有」這類替代項目。這項驗證僅適用於直接加註的元素,不適用於「繼承」removed
或 replaced
引數的元素。
重新命名
如要重新命名成員,請以新定義取代該成員,並使用舊定義的 renamed
引數指定新名稱。例如:
type User = table {
@available(replaced=2, renamed="first_name")
1: name string;
@available(added=2)
1: first_name string;
};
只有成員可以使用 renamed
引數,因為 FIDL 編譯器會根據自己的 ABI 身分進行驗證。如要重新命名宣告,只需移除舊定義,以新的定義即可:
@available(deprecated=2, removed=3, note="renamed to Information")
type Info = table {};
@available(added=2)
type Information = table {};
移除後
通常 renamed
引數會與 replaced=N
搭配使用,但您也可以與 removed=N
搭配使用。這樣一來,您就能在移除成員後,以新名稱參照該成員。運作方式取決於目標版本組合:
- 如果您只指定版本低於
N
,繫結會使用舊名稱。 - 如果您只指定等於或大於
N
的版本,綁定作業就不會納入成員。 - 如果指定的組合包含版本低於
N
「且」包含大於或等於N
的版本,繫結會使用新名稱。
這樣做的其中一個原因,是為了避免使用者採用新的 API,同時繼續支援其導入作業。例如:
open protocol Door {
@available(removed=5, renamed="DeprecatedOpen")
flexible Open() -> ();
};
如果 Door
伺服器是在指定版本集 {4, 5} 的程式碼集中實作,該方法的名稱將為 DeprecatedOpen
,導致開發人員無法新增該方法。如果其他程式碼集指定 4 以下版本,方法將命名為 Open
。如果目標版本為第 5 版,此方法完全不會顯示。
使用這項功能的另一個原因,是為了重複使用新 ABI 的名稱。例如,請考慮變更 Open
方法以傳回錯誤:
open protocol Door2 {
@available(removed=5, renamed="DeprecatedOpen")
flexible Open() -> ();
@available(added=5)
@selector("NewOpen")
flexible Open() -> () error uint32;
};
我們必須定義新方法,因為若用戶端未預期發生錯誤,就會在收到錯誤回應時關閉管道。不過,只要我們 (1) 使用 @selector
為新方法提供不同的 ABI 身分,以及 (2) 在舊定義中使用 renamed
,即可繼續使用 Open
名稱,讓版本組合 {4, 5} 的繫結包含這兩種方法。
參照 FIDL 類型
每個 FIDL 元素可以參照其他元素的方式有很多種,例如:
const VALUE uint32 = 5;
const REFERENCES_VALUE uint32 = VALUE;
type Type = struct {};
type ReferencesType = table {
1: t Type;
};
alias ReferencesTypeAndValue = vector<Type>:VALUE;
參照元素時,您必須遵守 @available
屬性。舉例來說,由於 A
從第 1 版起存在,但嘗試參照僅存在於版本 2 的 B
,因此下列程式碼無效:
// Does not compile!
@available(added=1)
const A bool = B;
@available(added=2, removed=3)
const B bool = true;
同樣地,非淘汰元素也無法參照淘汰元素。舉例來說,以下程式碼在版本 1 中無效,因為 A
參照 B
,但 B
已淘汰,A
則無效。
// Does not compile!
@available(deprecated=2)
const A bool = B;
@available(deprecated=1)
const B bool = true;
fidlc
指令列
FIDL 編譯器接受 --available
標記來指定平台版本。
舉例來說,假設 example.fidl
在 fuchsia
平台中定義了一個不含依附元件的程式庫,您可以在版本 22 進行編譯,如下所示:
fidlc --available fuchsia:22 --out example.json --files example.fidl
如要指定多個版本,請以半形逗號分隔,例如 --available fuchsia:19,22,23,NEXT,HEAD
。
如果程式庫 A
具有來自不同平台的 B
程式庫的依附元件,您可以使用 --available
標記兩次指定兩個平台的版本。不過,A
必須在整個版本記錄中與 B
所選的固定版本相容。