RFC-0135:套件 ABI 修訂版本

RFC-0135:套件 ABI 修訂版本
狀態已接受
區域
  • 軟體推送
說明

將系統 ABI 修訂版本編碼至套件。

問題
Gerrit 變更
作者
審查人員
提交日期 (年-月-日)2021-09-30
審查日期 (年-月-日)2021-11-05

摘要

將系統 ABI 修訂版本編碼至套件中。

提振精神

RFC-0002 介紹了 API 級別ABI 修訂版本的概念。API 級別是逐漸增加的數字,人類可以理解,對應於建構應用程式時可用的一組 API。ABI 修訂版本對應於應用程式預期平台提供的 Fuchsia 系統介面所公開的特定語意。API 級別會對應至特定 ABI 修訂版本,但多個 API 級別可以參照相同的 ABI 修訂版本。這項功能可讓 Fuchsia 平台隨著時間演進,同時繼續支援舊版應用程式。

實作平台版本控制時,套件必須對應到所需的 ABI 級別,以便平台得知套件是否與執行中的系統相容。

相關人員

協助人員:abarth@google.com

審查者:

我們選擇了以下利益相關者,因為此 RFC 會影響 Fuchsia 工具和軟體提交作業,並最終影響元件架構如何選取要呈現給應用程式的元件系統介面。

  • abarth@google.com (FEC 成員)
  • computerdruid@google.com (SWD)
  • geb@google.com (CF)
  • mkember@google.com (FIDL)
  • raggi@google.com (工具)
  • wittrock@google.com (SWD)

諮詢:

  • lijiaming (PDK)

社會化:

這項 RFC 已通過 Software Delivery 和 Fuchsia Tools 團隊的設計審查。

術語

Fuchsia package 是用於發布及整理 Fuchsia 程式、元件和服務檔案的分發單位。

meta.far 是套件中繼資料封存檔案。其中包含使用者端檔案名稱與底層內容位址 blob 的對應項目。封裝系統會使用這個值下載套件內容,並建構命名空間階層。並包含使用者提供的自訂檔案。

設計

這項設計引入了「套件 ABI 修訂版本」的概念,這是套件所用 Fuchsia 平台介面的目標 ABI 修訂版本。這個值會以無號小端序 64 位元整數的形式寫入檔案 meta/fuchsia.abi/abi-revision 中的 meta.far

所有套件產生工具都會更新,要求在建構套件時指定 API 級別或 ABI 修訂版本。指定 API 級別後,工具應編碼 SDK 版本記錄中對應的 ABI 修訂版本,或傳回錯誤。同樣地,工具應強制執行 SDK 支援指定的 ABI 修訂版本,否則會傳回錯誤。這樣一來,您就不會因為使用較舊的 SDK 建立元件,但指定僅存在於較新 SDK 中的 ABI,而面臨風險。這麼做頂多會導致元件無法執行。最糟糕的情況是,這可能會導致元件出現奇怪或危險的錯誤。

套件 ABI 修訂版本的用途

本提案僅涵蓋如何將 ABI 修訂版本編碼至套件。套件 ABI 修訂版本的使用者將在日後的設計中定義。不過,以下是一些可能的應用實例,可說明這項功能的用途:

  • RFC-0002 平台說明瞭一個機制,元件管理服務可使用套件 ABI 修訂版本,在執行元件時選取要使用的平台介面。
  • 系統組合是指從一組套件和其他編譯的構件組合 Fuchsia 系統映像檔的程序。系統映像檔可用於透過其他方式 (OTA、閃燈、鋪設等) 提供 Fuchsia。如果系統不支援所需的套件 ABI,彙整器可使用套件 ABI 修訂版本拒絕整合套件。
  • 同樣地,花瓣或應用程式開發人員可以使用套件 ABI 修訂版本,瞭解複雜樹狀結構中的所有元件是否可以在目標 Fuchsia 版本上執行。
  • 暫時套件是指隨選下載的套件,而非內建於系統映像檔。套件解析器可利用套件 ABI 修訂版本,拒絕下載其知道無法在系統上執行的套件。不過,這可能不適用於系統更新。詳情請參閱「系統更新注意事項」。

目標 API 級別或 ABI 修訂版本選取

這項提案旨在讓 API 級別或 ABI 修訂版成為建構套件時的必要引數,但在推出這項功能的初期,這項功能將可選用。因此,最終開發人員應為目標 API 級別或 ABI 修訂版本的建構規則設定參數。這樣一來,您就能輕鬆指定新版本。您甚至可以設定測試輪替器,讓系統以實驗方式提升目標 API 級別,並查看是否有任何失敗情形,藉此設定新的 API 級別自動測試。如果這些失敗是意外的,系統可能會將這些資訊回饋至平台錯誤報告。

系統更新注意事項

為了讓 Fuchsia 裝置從一個版本更新至另一個版本,系統更新器會解析稱為「更新套件」的特殊套件。這個套件會說明執行新 Fuchsia 版本所需的所有基礎套件和其他構件。這些新套件並非用於目前的系統,而是在裝置重新啟動至新系統後才會使用。

因此,如果我們決定使用套件 ABI 修訂版本來拒絕下載套件,就必須小心避免中斷 OTA 程序。如以下範例所示:

請考慮兩個 Fuchsia 版本 A,其中只支援 ABI-1,以及 C,後者支援 ABI-2。如果我們根據 ABI 篩選套件,C 的更新套件就必須是 ABI-1,才能下載。不過,C 版本的基礎套件必須是 ABI-2,才能使用新的 ABI,但這些套件會遭到版本 A 套件解析工具拒絕。

避免這種情況的其中一種做法,就是透過踏步式發布版本引入新的 ABI 修訂版本。Fuchsia 的設計可讓裝置跳過多個版本,以便更新至最新版本。踏板版本是無法略過的特殊版本。這項功能旨在透過平台介面提供流暢的轉換體驗。

重做先前的範例,改為使用 3 個連續的 Fuchsia 版本:

  • A:支援 ABI-1,基礎套件位於 ABI-1
  • B:支援 ABI-1ABI-2,基礎套件位於 ABI-1
  • C:支援 ABI-2,基礎套件位於 ABI-2

如果我們將 B 標示為中繼版本,則執行 A 的裝置會先更新至 B,然後再更新至 C

這個概念還可以進一步分離套件的定義方式與 ABI 修訂版本。由於我們不打算經常對套件版面配置進行回溯不相容的變更,因此可以為套件版面配置命名版本,然後指出特定 ABI 修訂版本支援哪些套件版面配置版本。這樣一來,我們就能安裝更多更新套件,而無需建立階梯式版本。

避免在 meta.far 中發生命名空間衝突

meta.far 目前包含兩個套件中繼資料檔案 (meta/packagemeta/contents),以及使用者指定的任意檔案。也就是說,使用者檔案和我們在套件中引入的任何新中繼資料檔案之間,可能會發生衝突。為避免這種可能性,套件 ABI 修訂版本會寫入目錄 meta/fuchsia.abi。這個目錄名稱遵循 Fuchsia 中其他地方使用的慣例,例如平台 FIDL 命名空間。套件建構工具將進一步更新,以防止使用者在 meta/fuchsia.abi 中定義自訂檔案。

由於這是由平台控管的目錄,因此您可以使用這個位置儲存 ABI 相關檔案,而不會與使用者檔案衝突。這可讓我們改進對套件 ABI 修訂版本的概念。舉例來說,如果我們想在套件中支援多個 ABI 修訂版本,可以新增 meta/fuchsia.abi/abi-revision-set 來包含所有支援的修訂版本。所有使用者都遷移至新檔案後,我們可以刪除 meta/fuchsia.abi/abi-revision

儲存空間額外負擔

將套件 ABI 修訂版本加入至元資料的額外負擔,在最糟的情況下會將 8KiB 未壓縮的資料加入至 meta.far,其中前 4KiB 來自 FAR 資料區塊,如果 DIRNAMES 區段導致第一個內容區塊被推送至下一個 4KiB 對齊偏移量,則會加入第二個 4KiB。不過,在實際操作中,這應該不會造成太大影響,因為 blobfs 有 8KiB 的區塊對齊。此外,它還能搭配預設的壓縮器 zstd 達到極佳的壓縮效果。根據以下範例,這只會為 meta.far 增加 47 個位元組:

# Create an empty package.
% mkdir empty && cd empty
% pm init && pm build
% ls -l meta.far
-rw-r--r--  1 etryzelaar  primarygroup  12288 Oct  5 10:21 meta.far
% fx chunked-compress c meta.far meta.far.compressed
Wrote 447 bytes (97% compression)

# Then create a meta.far that contain's the abi-revision.
% cd .. && mkdir with-abi && cd with-abi
% pm init
% mkdir meta/fuchsia.abi
% python3 -c "
import struct;
abi_revision = int('0xC7003BF9', 16)
f = open('meta/fuchsia.abi/abi-revision', 'wb')
f.write(struct.pack('<Q', abi_revision))
"
% pm build
% ls -l meta.far
-rw-r--r--  1 etryzelaar  primarygroup  16384 Oct  5 10:22 meta.far
% fx chunked-compress c meta.far meta.far.compressed
Wrote 494 bytes (97% compression)

實作

所有套件建構工具都會擴充,以便在指令列上指定 API 級別或 ABI 修訂版本。

pm CLI

pm CLI 將擴充,以便在套件初始化和建構期間指定 ABI。

叫用範例:

# Create a package with an API level. Under the covers this will look up the
# ABI revision from the SDK.
% pm init --api-level 5

# Create a package with an ABI revision. These commands are equivalent.
% pm init --abi-revision 0xC7003BF9
% pm init --abi-revision 3338681337

# Build a package with an ABI revision.
% pm build --abi-revision 0xC7003BF9

選項:

  • --api-level LEVEL:要編碼至套件中的 API 級別。SDK 必須支援這個值。這個選項與 --abi-revision 標記發生衝突。
  • --abi-revision REVISION:將納入套件的 ABI 修訂版本。這可能為十進制或十六進制整數,必須由 SDK 支援。這個選項與 --api-level 標記發生衝突。

最初,--api-level--abi-revision 標記將為選用項目,讓花瓣和最終開發人員在一段時間內實作支援功能。一旦生態系統轉換為指定旗標,這就會成為必要的引數。

成效

這個 RFC 會在建構套件時引入少量額外工作,並對儲存空間造成輕微負載,因此效能影響應該微乎其微。

人體工學

這項提案本身不會大幅改變 Fuchsia 的人體工學設計。不過,這最終可讓產品開發人員針對元件指定特定系統介面。這應該可提供所需的穩定性,同時不會阻礙 Fuchsia 的演進能力。

回溯相容性

這項變更具有回溯相容性,因為這項設計會引入沒有消費者的新檔案。不過,這項功能最終會讓系統淘汰舊版 ABI 修訂版本的支援,並最終移除相關支援。

安全性考量

需要剖析 meta/fuchsia.abi/abi-revision 檔案。不過,這個格式很容易剖析,而且應該很容易驗證剖析器是否正確。

詳情請參閱 RFC-0002 安全性考量

隱私權注意事項

這項提案對隱私權沒有重大影響。

測試

封裝工具將擴充,以便驗證套件是否已設定預期的 ABI。由於會有一段過渡期,因此套件解析器測試會延長,以便驗證套件是否能與含有或不含 meta/fuchsia.abi/abi-revision 檔案的套件搭配運作。

說明文件

包裝說明文件將進行更新,討論 ABI 如何在套件中表達,以及使用者如何在建構套件時選取 API 級別或 ABI 修訂版本。

缺點、替代方案和未知事項

其他 ABI 修訂檔案格式

我們可以改用以下方式,而非將 ABI 修訂版本編碼為小端整數:

  • 使用者容易理解的整數字串
  • JSON
  • 持久性 FIDL

之所以選用 little endian 整數,是因為:

  • 我們不希望人類使用這個值。而是應參照人類可理解的 API 級別。
  • 這個值會以小端序整數的形式傳遞,因此可避免額外轉換。
  • 剖析方式也相當簡單。

包含一組 ABI 修訂版本的套件

RFC-0002 Applications 指出,套件可包含多個元件,每個元件都會指定不同的 ABI 修訂版本。為支援此功能,套件 ABI 可以是所有元件 ABI 修訂版本的集合,而非在套件中編碼單一 ABI 修訂版本。

不過,截至本文撰寫時,並沒有任何用途需要套件包含以不同系統 ABI 為目標的元件。單一數字可簡化 ABI 支援的初始實作。如果這類情況變得重要,擴充這項設計也不難。

將 ABI 修訂版本直接編碼至 meta.far

meta.far 可以直接嵌入 ABI 修訂版本,方法如下:

  • 將 ABI 新增至 FAR 索引區塊
    • 優點:
    • 只會增加 8 個位元組的額外負荷。
    • 缺點:
    • 索引區塊沒有任何保留位元組,因此將這個區塊加到索引中,就必須對 FAR 格式進行重大變更。為了進行這項變更,我們必須先實作支援功能,讓系統能夠在不使用新 FAR 格式的情況下讀取這類格式,然後建立墊腳石版本,最後再遷移至該格式。
    • 這樣一來,使用者就更難讀取 ABI 修訂版本。套件解析器不會直接將 meta.far 公開給使用者做為檔案,因此需要新增 API 來公開這項資訊。
  • 建立新的 ABI 修訂版本區塊類型。
    • 優點:
    • 應能回溯相容,以便新增新的區塊類型。
    • 由於我們不需要建立 4096 位元組對齊的內容區塊,因此位元組的額外負擔應會小得多。區塊會以 64 位元對齊,且索引額外開銷項目大小為 24 個位元組,因此這個方法只會在未壓縮的 FAR 格式中增加 32 個位元組。
    • 缺點:
    • 我們並未在 FAR 格式中新增區塊類型,因此如果 FAR 程式庫遇到新區塊類型,可能會發生錯誤或異常行為。
    • 我們必須更新所有 FAR 程式庫,才能瞭解新的區塊類型。以往對 FAR 程式庫進行任何變更都相當耗時。
    • 消費者將更難讀取 ABI 修訂版本。套件解析器不會直接將 meta.far 公開給使用者做為檔案,因此需要新增 API 來公開這項資訊。

由於儲存空間的開銷很少,尤其是在壓縮後,我們認為節省空間的做法不值得定義新的區塊類型。

存放區中繼資料中的 ABI 修訂版本

我們可以將 ABI 修訂版本加入套件存放區中的套件中繼資料,而非直接將 ABI 編碼至套件。這有以下缺點:

  • ABI 修訂版本是套件的內在屬性。您可能很難確保 ABI 修訂版本在 TUF 存放區或基本套件清單中正確顯示。
  • 包裝中繼資料仍會包含 ABI 修訂版本,因此包裝系統仍須負擔額外負擔。

即將推出的永久性 FIDL 套件中繼資料中的 ABI 修訂版本

軟體提交團隊正在撰寫 RFC,提出新的 持續性 Fidl 封裝中繼資料。我們可以改為將 ABI 修訂版本儲存在這個中繼資料中,方法是將建議的結構定義更新為以下內容:

flexible union Contents {
    1: ContentsV1 v1;
};

table ContentsV1 {
    1: vector<fuchsia.io.Path>:MAX paths;
    2: vector<fuchsia.pkg.BlobId>:MAX hashes;
    3: vector<uint64>:MAX blob_sizes;
    4: uint64 abi_revision;
};

優點:

  • 將所有封裝中繼資料合併至單一 FIDL 檔案的好處,在於我們可以利用 FIDL 工具讓文件和驗證作業保持最新狀態。使用多個檔案會使說明文件和驗證作業更容易彼此脫節。
  • 我們只會在未壓縮的 meta.far 中加入少量位元組,而非 ~4 KiB。

使用 FIDL 的缺點:

  • 我們無法保證會接受以持續性 Fidl 為基礎的 RFC,也無法保證實作這項 RFC 需要多久的時間。
  • 雖然以持續性 Fidl 為基礎的方法會加上版本號碼,但內部版本無法協助我們將封裝中繼資料遷移至其他檔案格式。舉例來說,如果我們決定從持續性 FIDL 切換至 JSON 檔案,使用新的 ABI 修訂版本,可更輕鬆地通知封裝系統尋找與先前不同的檔案格式。

既有技術與參考資料

Android

Android 應用程式會在應用程式資訊清單中指定 uses-sdk 元素,以指定 SDK 版本。

Windows

Windows 應用程式會指定應用程式資訊清單中列出的 SupportedOS GUID,以指定 OS 版本。

macOS

macOS 應用程式會在應用程式套件的 Info.plist 檔案中指定 LSMinimumSystemVersion,藉此指定 OS 版本。

iOS

iOS 應用程式會在應用程式套件的 Info.plist 檔案中指定 MinimumOSVersion,藉此指定目標作業系統版本。