FIDL 版本管理

本文件說明 FIDL 的 API 版本管理功能。如需 Fuchsia 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 平台。這個平台只有一個版本:HEADfuchsia.git 中的 FIDL 程式庫必須指定 @available 屬性。

目標版本

當您指定單一版本時,繫結會包含該版本中可用的所有元素,如 FIDL 檔案中的 @available 引數所指定。

指定一組版本時,繫結會包含組合中任何版本可用的所有元素。對於屬於 replaced 的元素,繫結只會包含最新定義。

無論您指定哪個版本組合,如果 FIDL 編譯成功,則該組合的所有子集和所有可能的單例組合也保證會成功。

語法

任何 FIDL 元素都可以使用 @available 屬性。它採用下列引數:

引數 類型 說明
platform string 程式庫群組名稱 (請參閱概念);僅適用於 library
added uint64 整數、NEXTHEAD
deprecated uint64 整數、NEXTHEAD
removed uint64 整數、NEXTHEAD
replaced uint64 整數、NEXTHEAD
note string 提供 deprecatedremoved 和/或 replaced 的背景資訊
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;
};

所有修飾符都支援以下語法:strictflexibleresourceclosedajaropen。不過,如果在雙向方法中變更 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 部分,並提供詳細說明,並在 @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) 元素是否「沒有」這類替代項目。這項驗證僅適用於直接加註的元素,不適用於「繼承」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 為新方法提供不同的 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.fidlfuchsia 平台中定義了一個不含依附元件的程式庫,您可以在版本 22 進行編譯,如下所示:

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

如要指定多個版本,請以半形逗號分隔,例如 --available fuchsia:19,22,23,NEXT,HEAD

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