FIDL 版本管理

本文件說明 FIDL 的 API 版本管理功能。如要瞭解如何改善 Fuchsia API,請參閱 Fuchsia API 演進指南

提振精神

FIDL 版本管理功能可讓您隨著時間變更 FIDL 程式庫,同時保有為舊版程式庫產生繫結的功能。您可以透過下列方式手動執行此操作:

  • .fidl 檔案儲存在 v1/ 目錄中。如要變更,請將 v1/ 複製到 v2/,然後在該處變更檔案。如要產生舊版本的繫結,請使用 v1/ 程式庫,而非 v2/

  • .fidl 檔案儲存在 Git 存放區,並在修訂版本中進行變更。如要產生舊版本的繫結,請查看存放區的舊版本。

第一個解決方案相當繁瑣,且會產生許多重複項目。除了特定 FIDL 程式庫 (例如主要的 Fuchsia 存放區) 以外,第二個解決方案在包含許多項目的大型存放區中無法正常運作。

FIDL 版本設定也有同樣的效果,但不能有這些缺點。進行變更時,請使用 @available 屬性來描述變更發生的時間 (即哪個版本)。如要產生舊版本的繫結,請將 --available 標記傳遞至 fidlc,並指定較舊版本。

使用 FIDL 版本管理功能時,請注意兩點重要事項:

  • 這項設定會影響「僅限 API」。版本僅於編譯期間存在,對執行階段行為沒有影響。

  • 這可以代表任何語法的有效變更。只因為版本管理表示可以表示變更,並不代表該變更就安全。

概念

版本管理的單位是一組程式庫,稱為「平台」。按照慣例,程式庫的名稱會以平台名稱開頭。例如,fuchsia.memfuchsia.web 程式庫屬於 fuchsia 平台。

每個平台都有線性「版本」記錄。版本是介於 1 到 2^63-1 (含) 之間的整數,或特殊版本 HEADLEGACY 之一。HEAD 版本適用於最新的不穩定版變更。LEGACY 版本與 HEAD 類似,但包含舊版元素

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

指令列

FIDL 編譯器接受 --available 標記來指定平台版本。 舉例來說,假設 example.fidlfuchsia 平台中定義了一個不含依附元件的程式庫,您可以在第 8 版進行編譯,如下所示:

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

無論您選取哪個版本,Fidlc 一律會驗證所有可能的版本。舉例來說,即使只有版本 5 發生錯誤,上述指令仍可回報錯誤。

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

語法

@available 屬性適用於所有 FIDL 元素。該函式採用的是下列引數:

引數 類型 附註
platform string 僅允許 library 存取
added uint64 整數或 HEAD
deprecated uint64 整數或 HEAD
removed uint64 整數或 HEAD
replaced uint64 整數或 HEAD
note string deprecated一起出現
legacy boolean removed一起出現

引數有一些限制:

  • 所有引數皆為選用,但至少須提供一個引數。
  • 引數必須是常值,而非 const 宣告的參照。
  • removedreplaced 引數互斥,無法共同存在。
  • 引數必須遵循 added <= deprecated < removedadded <= deprecated < replaced
  • 如未指定,addeddeprecatedremovedreplacedlegacy 引數會繼承父項元素。

例如:

@available(added=1, deprecated=2, removed=3, legacy=true)
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;

繼承

@available 的引數資料流從程式庫宣告到頂層宣告,以及從每個頂層宣告到其成員。舉例來說,如果在第 5 版新增資料表,則無須對成員重複此註解,因為這些註解不存在於資料表本身之前。以下是更複雜的繼承範例:

@available(added=2, deprecated=3)
protocol Versioned {
    // Equivalent to `@available(added=2, deprecated=3, removed=4, legacy=true)`.
    @available(removed=4, legacy=true)
    Removed(table {
        // Equivalent to `@available(added=3, deprecated=3, removed=4, legacy=true)`.
        @available(added=3)
        1: message string;
        // Equivalent to `@available(added=2, deprecated=3, removed=4, legacy=false)`.
        @available(legacy=false)
        2: count uint32;
    });
};

淘汰

淘汰用於表示元素日後將遭到移除。淘汰元素時,則應在文件註解中加入 # Deprecation 區段和詳細說明,並在 @available 屬性中加入 note 引數,以便簡單指示。例如:

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")
    Deprecated();

    @available(added=5)
    Replacement();
};

但自 2022 年 5 月起,淘汰對繫結沒有任何影響。然而,FIDL 團隊會規劃使其發出譯文語言的淘汰註解。舉例來說,上述範例可能會在 Rust 繫結中產生 #[deprecated = "use Replacement"]

取代中

replaced 引數可讓您編寫全新的定義,以變更特定版本的元素。這是變更 FIDL 元素特定部分的唯一方法,包括:

  • 常數的值
  • 結構體、資料表或聯集成員的類型
  • 資料表或聯集成員的序數
  • 宣告的種類,例如將結構體變更為別名
  • 方法上有 error 語法
  • 元素中的其他屬性,例如 @selector
  • 修飾符,例如 strictflexibleresource

如要取代版本 N 的元素,請使用 @available(replaced=N) 為舊定義加上註解,並使用 @available(added=N) 為新定義加上註解。例如,以下是將列舉從 strict 變更為 flexible 的方法:

@available(replaced=5)
type Color = strict enum {
    RED = 1;
};

@available(added=5)
type Color = flexible enum { // Note: flexible instead of strict
    RED = 1;
};

您也可以使用取代功能,清除因長期 API 變更記錄而雜亂的 FIDL 程式庫。只要將所有元素替換為全新的定義,然後將舊定義移至另一個名稱 (例如 history.fidl) 的檔案即可。

FIDL 編譯器會驗證每個 @available(replaced=N) 元素都有相符的 @available(added=N) 取代項目。也會驗證每個 @available(removed=N) 元素是否「沒有」這類替換。這項驗證僅適用於直接加註的元素,不適用於沿用 removedreplaced 引數的元素。

Legacy

移除元素時,您可以使用 legacy=true 將其保留在 LEGACY 版本中。由於 Fuchsia 系統映像檔是以 LEGACY FIDL 繫結為基礎,因此可讓您針對指定 API 級別的用戶端保留 ABI。例如:

protocol LegacyExample {
    @available(deprecated=5, removed=6, legacy=true, note="...")
    LegacyMethod();
};

在本例中,LegacyMethod 不會顯示在 6 以上版本或 HEAD 的繫結中,但會加回 LEGACY 版本。

參考資料

有很多 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 版起,但嘗試參照的 B 只存在於第 2 版:

// 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;