FIDL 版本管理

本文件說明 FIDL 的 API 版本管理功能。如需 請參閱 Fuchsia API 演進指南

摘要

FIDL 版本管理可讓您代表 FIDL 程式庫隨時間的變化。時間 進行變更,您可以使用 @available 屬性描述時機 (例如: 版本) 會發生變更如要產生繫結,您必須傳送 --available 標記,用於指定一或多個版本的 fidlc。

FIDL 版本管理提供的是 API 版本管理,而非 ABI 版本管理。您無法 在執行階段中定義多個查詢版本變更可能導致 API 故障,但預期會出現 而且與 ABI 相容FIDL 編譯器會執行部分基本驗證,但會這麼做 不保證 ABI 相容性。

概念

版本管理的單位是一組稱為「平台」的程式庫。變更者: 命名慣例就是以平台名稱命名例如: fuchsia.memfuchsia.web 程式庫屬於 fuchsia 平台。

每個平台都有線性「版本」記錄。版本是從 1 算起的整數 變更為 2^31-1 (含),或是 NEXTHEAD 其中一個特殊版本。 NEXT 版本用於預計在下個編號進行的變更 版本。HEAD 版本用於最新的不穩定變更。

如果 FIDL 程式庫沒有任何 @available 屬性,屬於該程式庫的 unversioned 平台。這個平台只有一個版本:HEAD

指令列

FIDL 編譯器接受 --available 旗標來指定平台版本。 舉例來說,假設 example.fidlfuchsia 平台中定義了程式庫 沒有依附元件,您可以在第 8 版編譯應用程式,如下所示:

fidlc --available fuchsia:8 --out example.json --files example.fidl

您可以指定多個版本並以半形逗號分隔,例如: --available fuchsia:7,8,9

如果程式庫 A 具有來自不同平台程式庫 B 的依附元件, 您可以使用 --available 旗標兩次,指定兩個平台的版本。 不過,A 必須與固定版本記錄的整個版本記錄相容 已為 B 選擇版本。

目標版本

如果指定單一版本,繫結會包含 該版本提供的是,由 FIDL 中的 @available 引數指定 檔案。

指定一組版本時,繫結會包含 可與任何版本搭配使用對於 replaced,繫結僅包含最新的定義。

無論您指定哪種版本,只要 FIDL 編譯成功,就會: 也會保證在所有可能的組合中順利執行 單例模式

語法

@available 屬性適用於所有 FIDL 元素。虛擬機器 下列引數:

引數 類型 附註
platform string 僅允許於 library 使用
added uint64 整數、NEXTHEAD
deprecated uint64 整數、NEXTHEAD
removed uint64 整數、NEXTHEAD
replaced uint64 整數、NEXTHEAD
note string deprecated共度
renamed string removedreplaced 的形式插入僅限會員

引數有一些限制:

  • 所有引數皆為選用,但至少須提供一個引數。
  • 引數必須是常值,不能參照 const 宣告。
  • removedreplaced 引數彼此互斥。
  • 引數必須符合 added <= deprecated < removedadded <= deprecated < replaced
  • addeddeprecatedremovedreplaced 引數 如未指定,會從父項元素「繼承」

例如:

@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。不過,系統只允許使用 addedremoved 引數。

以下範例說明如何將列舉從嚴格變更為彈性:

type Color = strict(removed=2) flexible(added=2) enum {
    RED = 1;
};

所有修飾符都支援以下語法:strictflexibleresourceclosedajar,以及 open。不過,變更 strictflexible 修飾符 系統不允許沒有錯誤語法的雙向方法。

當您指定一組版本時,編譯器會使用 最新的修飾符。在上述範例中,如果設定了 包含等於或大於 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 包含詳細說明,且 note 引數的 附有簡短的指示的 @available 屬性。例如:

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 Replacement"]

身分識別

FIDL 版本管理的用途是區分移除和取代 元素。為此,我們採用 API 和 ABI 身分的概念。API 則是元素的名稱ABI 身分取決於元素的種類:

  • 位元/列舉成員:值,例如VALUE = 5;中有 5 人
  • 結構成員:偏移,例如第 0 名成員可享 0 折扣
  • 資料表/聯集成員:序數,例如5: name string;中有 5 人
  • 通訊協定成員:選取器,例如「example/Foo.Bar」英吋 library example; protocol 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) 元素是否「不包含」 來執行這項驗證僅適用於直接加註註解的元素 而不是繼承 removedreplaced 的元素 引數。

正在重新命名

如要重新命名成員,請以新的定義取代該成員,並指定 新名稱搭配舊定義使用 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 為新方法提供Open 不同的 ABI 身分,以及 (2) 在舊定義上使用 renamed。 允許版本集 {4, 5} 的繫結納入這兩種方法。

參考資料

每個 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;