RFC-0208:使用 SDK 發布套件

RFC-0208:透過 SDK 發布套件
狀態已接受
領域
  • 軟體推送
  • 測試
說明

提出如何在 SDK 中發布部分樹狀結構內套件,進行樹狀結構外外包裝。

問題
更小鳥
作者
審查人員
提交日期 (年月分)2023-02-15
審查日期 (年-月-日)2023-02-10

摘要

本 RFC 說明從Fuchsia 的平台存放區建構及發布套件做為 SDK 的附加條款。這些套件應使用子封裝機制在下游組合。

該策略包含全新的 .api 檔案格式,該檔案本身就是 Fuchsia 專用的新 SDK API 介面。格式本身和使用格式的簽入檔案則須通過 API 委員會審核。

提振精神

實作子套件以支援密封巢狀套件依附元件。這項功能的主要用途是明確納入特定套件以及樹狀結構外 (OOT) 的測試。替代方法 (包括對基本套件的參照) 已淘汰,因為它很容易損毀。例如,如果從基本映像檔中移除套件,依附元件鏈結就會損毀。

這份 RFC 說明對一組套件 (將從 Fuchsia 平台存放區發布) 的策略修訂,以及如何將這些套件納入下游存放區中的子套件。

請注意,雖然此 RFC 著重在這類套件封裝到測試的特定情況,但這是透過 SDK 發布套件的通用機制。例如,產品負責人可以在實際工作環境的工作階段中,加入 Fuchsia-team 繪製的元件。透過 SDK 發布的第一個套件可能會用於測試,因為我們能夠以某些在實際工作環境中無法接受的方式切割測試。除了安排考量事項,這麼做的目的在於完整支援測試和實際工作環境這兩種套件的發行方式。

相關人員

講師:

hjfreyer@google.com

審查者:

  • aaronwood@google.com
  • dschuyler@Google.com
  • jsankey@google.com
  • richkadel@google.com

諮詢時間:

  • etryzelaar@google.com
  • kjharland@google.com
  • shayba@google.com
  • wittrock@google.com

社會化:

這個 RFC 之前曾與軟體推送、產品組裝和元件架構的代表進行討論。

定義

  • 整合商開發套件 (IDK) 是各建構系統通用的程式碼、二進位檔及資料檔案組合,可用來建構以 Fuchsia 為目標的程式。詳情請參閱這裡的說明。
  • Fuchsia 軟體開發套件 (SDK) 是具備可感知建構系統的整合作業的 IDK (API、工具和語言整合構件)。其中最值得注意的一點就是與 Bazel 搭配使用的 Fuchsia SDK
  • 內樹狀結構是指存放區中顯示的程式碼和建構規則:https://fuchsia.googlesource.com/fuchsia/。這個存放區會產生 IDK 做為輸出。
  • Out-of-tree (OOT) 是指非樹狀結構內結構 (使用 SDK 產生軟體和產品) 存放區中的程式碼和建構規則。
  • Fuchsia 套件是 Fuchsia 裝置的軟體發布單位,其參照二進位檔/程式庫、元件資訊清單,以及在 Fuchsia 系統上執行部分程式時所需的資料檔案。
  • 分包允許 Fuchsia 套件透過隔離,將另一個 Fuchsia 套件的特定版本做為依附元件。
  • 如此 RFC 所述,SDK 套件是樹狀結構內 Fuchsia 套件,已明確標記為可納入 IDK,且擴充功能可供 SDK 消費者使用。OOT 存放區可能會使用發布時的名稱以建構這些套件 (例如,透過下載或轉包封裝)。請注意,這項設計的擴充功能可能會允許 OOT 存放區發布自己的套件。

設計

本文件中的關鍵字「必須」、「不得」、「必要」、「應」、「不應」、「應該」、「不應該」、「建議」、「可能」和「選用」等關鍵字均以 IETF RFC 2119 中所述的方式解釋。

SDK 套件是由樹狀結構內結構建構所產生,做為 IDK 參照的新構件。由於 IDK 包含所有建構系統專用的 SDK 通用構件組合,因此可讓所有 Fuchsia SDK 整合參照 SDK 套件。

以下階段會樹狀結構內完成:

  1. 選擇 - 選擇要發布的套件。
  2. 驗證 - 確保可以分配預期的套件組合。
  3. 建構 - 為所有必要架構建構選定的套件。
  4. 套裝組合 - 產生會包含在 IDK 中的套件目錄結構。

SDK 套件可透過 SDK 中的 sdk://packages/ 套件目錄結構,在 OOT 存放區中立即使用。

本節的其餘部分將提供每個階段的詳細設計。

發布 SDK 套件

選項

我們不希望自動發布樹狀存放區中的所有套件以供使用 OOT,應該必須提交一組特定套件,並與平台版本一併發布。

我們會建立名為 sdk_fuchsia_package 的新 GN 範本,並提供下列資訊:

  • 要發布的 fuchsia_package 目標。
  • 套件的 SDK 類別 (例如「partner」)。
  • 將套件加入 SDK 的 API 級別
  • (選用) 排定從 SDK 移除套件的 API 級別。
  • 套件中預期的檔案清單及其「配置」。說明請見下一節。

這個 GN 範本會使用 sdk_atom 目標,以便享有 SDK 基元驗證 / 工具的建立路徑。

新的 GN 目標 sdk_package_bundle 會列出目前平台 API 級別的所有 sdk_fuchsia_package 目標。這個目標會使用 sdk_molecule 目標強制執行套件層級的檢查。

這項機制可讓平台擁有者明確承諾為特定 API 級別提供特定套件供 OOT 使用 (即允許在一段時間後淘汰及移除套件)。

至於實作詳細資料,我們應避免使用 GN 中繼資料,而是明確列出 sdk_fuchsia_package 目標中包含的所有套件。

驗證

發布樹狀結構內套件不僅對套件的「名稱」的承諾,也比至少要提供套件的部分「內容」。本節將介紹需要此程序的原因、宣告套件合約的程序,以及在建構期間驗證套件內容的機械程序。

背景

舉例來說,簽訂合約非常重要的範例,可以考慮使用包含元件 sample.cmsample-package 套件。sample-package 已發布 OOT,因此 SDK 使用者可能會依附 fuchsia-pkg://host/sample-package#meta/sample.cm。如果 sample.cm 已重新命名或移除,該 OOT 版本現在將會失敗。這與在平台映像檔變更的樹狀套件中,樹狀結構的故障模式類似。更重要的是,如果 sample.cm 需要新的傳入功能 (或任何一些不相容的資訊清單變更,請見下文),先前的使用就會意外失敗。

直接修訂套件的名稱後,我們就可以發現上述問題,但另外,如果套件中所有內容的明確版本設定方式較嚴重,就過於繁瑣。例如,如果我們發布 archivist-for-embedding,該套件的某些內容會隨著 50 個依附元件中的其中一個變更而變更。上述多數變更不會變更 archivist 元件的介面或行為。因此,我們只想對套件內容中選定的內容進行修訂。

合約

在理想情況下,我們驗證的套件內容部分代表了套件使用者可能依附的合約。在理想情況下,合約會採取以下措施:

  1. 是樹狀結構內明確表示。
  2. 此為套件 OOT 使用者可能依賴的具體內容。
  3. 為 OOT 提供,以便工具僅允許使用者依賴合約。

精確界定「真正的合約」並不容易,也沒有一體適用的定義。在考量元件資訊清單等檔案內容時更能看出這點。對能力轉送或宣告進行一些變更不會造成相容性問題 (例如提供新能力),有些變更則通常也會造成相容性問題,例如取決於新的傳入能力。其他變更的相容性則與上下文密切相關 (例如變更元件的指令列引數)。目前並沒有提供說明元件變更相容性的指南。

我們預計只會在 SDK 中發布少數經過仔細審查的套件,直到能根據 Fuchsia API 理事會審核結果提供明確指南。

本節其餘部分說明的機制,用於偵測對套件「真實合約」有不當 (過度通用) 的變更。包含一組目前顯示檔案,以及部分檔案的雜湊,如果在此組合中偵測到任何變更,就會觸發 API 審查作業。我們會在 SDK 中發布這個表示法和套件,讓 OOT 使用者瞭解您所提供的合約,不過我們僅允許 OOT 使用者使用包含雜湊的子集。

這表示對於套件合約中的異動,我們將稍微敏感 (以免錯過) 和過度保守的套件使用權限 (進而限制許可用途)。這樣一來,我們就能樹狀結構內結構中驗證 SDK 中隨附的項目,並透過 OOT 建構時間確保不會發生錯誤的執行階段錯誤。

請注意,SDK 套件中元件所用 FIDL API 的語意變更與這個驗證階段無關。您可以使用相容性測試來解決這個問題。未來工作也可能包含進一步擴充合約來涵蓋參照的 FIDL 檔案。

聲明合約

如上一節定義的 SDK 套件建構規則會包含套件中預期檔案的清單,以及這些檔案的「配置」。選擇處理方式可讓您靈活地定義每個檔案如何符合套件的合約。

起始處理方式如下:

  • exact:確切的檔案內容是指合約。請務必按照下一節的指定方式明確確認檔案內容的變更。
  • internal:檔案不在套件的合約中。

exact 比對功能適用於其內容屬於合約的檔案,且檔案內容必須長期維持才能達到相容性 (例如元件資訊清單)。

internal 表示檔案並非合約的一部分,但可在本機建構中。

如果需要做為此 RFC 的擴充功能,可以加入其他處理法。

驗證合約

我們會採用熟悉的 .api 黃金檔案的機制,並要求明確的 API 審查,藉此驗證 SDK 套件的合約。

例如:

// sample-package.api
// The name of the file matches the name of the package with '.api' added.
// The file format is JSON.
// Note: Comments will not be present in the real .api files.
{
    // Each top-level key is a file path in the resulting package.
    "meta/sample.cm": {
        // Hash is specified for files with "exact" disposition.
        hash: "...",
    },
    "data/some-file.json": {
        // If internal, this file is simply checked for presence
        // at build time.
        internal: true,
    }
}

建構期間,系統會根據輸入建構規則和產生的套件內容 (其中所有未宣告的檔案輸出均隱含 internal) 產生新的 .api 檔案。如果這個檔案的內容與 //sdk/packages 下來源控制的對應 .api 檔案不相符 (由深 JSON 相等性),建構作業就會失敗。與其他 .api 檔案不相符一樣,新的 .api 檔案會儲存為建構輸出,並在列印新的黃金時提供複製 .api 檔案的建議指令。如此一來,您就能輕鬆確認變更並上傳以供審查

我們會記錄評估 .api 套件與 SDK 套件不符的步驟,並指示哪些變更可能不安全。

在接下來的步驟中,系統會在產生的套件封存中提供 .api 檔案,讓 OOT 工具可能會使用此檔案驗證套件的內部詳細資料並未依附於該套件。

當 SDK 套件發生有意義的變更時,這種方法可確保開發人員能要求進行 API 審查,同時能定義哪些變更具有意義。

建築

針對我們為 IDK 產生 IDK 的所有架構,必須針對這些架構 (在寫入時提供 arm64x64) 建構這些套件。我們可能會視需要導入 debugrelease 等其他變種版本,但一開始一律會建構所有套件進行發布。

此程序的輸出內容是一組檔案內容,以及每個 SDK 套件的套件資訊清單。

這個程序和現有建構程序的唯一差別在於,會有獨立的套件資訊清單清單,適用於下一個階段使用的 SDK 套件。

系統會產生子套件中二進位檔和共用程式庫的偵錯符號,並上傳至適當的 GCS 值區。這與直接在 IDK 組合中發布二進位檔和共用程式庫的程序相同。

郵件分類

建構完成後,套件將會分發在目錄結構中,如下所示:

sdk://
├── blobs
│   ├── CONTENT_MERKLE_1
│   └── CONTENT_MERKLE_2
└── packages
    ├── arm64
    │   ├── 10
    │   │   ├── debug
    │   │   │   ├── PACKAGE_BAR
    │   │   │   │   ├── api
    │   │   │   │   └── package_manifest.json
    │   │   │   └── PACKAGE_FOO
    │   │   │       ├── api
    │   │   │       └── package_manifest.json
    │   │   └── release
    │   │       ├── PACKAGE_BAR
    │   │       │   ├── api
    │   │       │   └── package_manifest.json
    │   │       └── PACKAGE_FOO
    │   │           ├── api
    │   │           └── package_manifest.json
    │   └── 11
    │       ├── debug
    │       │   ├── PACKAGE_BAR
    │       │   │   ├── api
    │       │   │   └── package_manifest.json
    │       │   ├── PACKAGE_BAZ
    │       │   │   ├── api
    │       │   │   └── package_manifest.json
    │       │   └── PACKAGE_FOO
    │       │       ├── api
    │       │       └── package_manifest.json
    │       └── release
    │           ├── PACKAGE_BAR
    │           │   ├── api
    │           │   └── package_manifest.json
    │           ├── PACKAGE_BAZ
    │           │   ├── api
    │           │   └── package_manifest.json
    │           └── PACKAGE_FOO
    │               ├── api
    │               └── package_manifest.json
    ├── x64
    │   ├── 10
    │   │   ├── debug
    │   │   │   ├── PACKAGE_BAR
    │   │   │   │   ├── api
    │   │   │   │   └── package_manifest.json
    │   │   │   └── PACKAGE_FOO
    │   │   │       ├── api
    │   │   │       └── package_manifest.json
    │   │   └── release
    │   │       ├── PACKAGE_BAR
    │   │       │   ├── api
    │   │       │   └── package_manifest.json
    │   │       └── PACKAGE_FOO
    │   │           ├── api
    │   │           └── package_manifest.json
    │   └── 11
    │       ├── debug
    │       │   ├── PACKAGE_BAR
    │       │   │   ├── api
    │       │   │   └── package_manifest.json
    │       │   ├── PACKAGE_BAZ
    │       │   │   ├── api
    │       │   │   └── package_manifest.json
    │       │   └── PACKAGE_FOO
    │       │       ├── api
    │       │       └── package_manifest.json
    │       └── release
    │           ├── PACKAGE_BAR
    │           │   ├── api
    │           │   └── package_manifest.json
    │           ├── PACKAGE_BAZ
    │           │   ├── api
    │           │   └── package_manifest.json
    │           └── PACKAGE_FOO
    │               ├── api
    │               └── package_manifest.json
    └── subpackage_manifests
        ├── META_FAR_MERKLE_1
        └── META_FAR_MERKLE_2

每個套件資訊清單都會放在由目標架構和 API 級別建構的目錄路徑中:sdk://packages/<ARCH>/<API_LEVEL>/{debug/release}/<PACKAGE_NAME>。這些路徑是產品可依附的穩定 SDK 合約。這個目錄會包含套件的 package_manifest.json 以及用於驗證的 API 檔案。

此外,任何子套件都會由套件 Merkle 將其資訊清單儲存在單獨的頂層目錄中:sdk://packages/subpackage_manifests/<META_FAR_MERKLE>

最後,所有 blob 都會以內容地址形式儲存在 sdk://blobs/<CONTENT_MERKLE> 中。blob 和資訊清單目錄是套件的實作詳細資料,可能會隨時間改變。產品不應仰賴這些路徑來穩定。

這些套件使用的所有二進位檔和程式庫的 LICENSES 檔案都會封裝並納入標準 SDK 授權故事中。

這種目錄型架構使用的原因包括:

  • 您可以將套件發布至存放區,或是做為子套件使用,無需剖析中繼檔案。
  • 即使跨 API 級別,系統還是會自動刪除重複 Blob,因此在包含相同 blob (例如共用程式庫和二進位檔) 的常見套件中,大幅節省空間 (相較於單獨封存每個套件)。
  • 目錄結構奠定了支援多個 API 級別的基礎,並提供了偵錯和發布版本。
  • SDK (ffxpm) 中已有用於與套件存放區互動的工具。
  • 藉由直接參照套件資訊清單,啟用分裝工作流程,讓您可輕鬆發布至存放區。

這個方法的一項已知缺點是將套件宣告為頂層套件和子套件。在這種情況下,系統會複製 package_manifest.json。套件資訊清單檔案很小,即使將其同時列為頂層、具名、檔案,以及透過套件的套件加以識別,並不構成問題,因此我們可以妥善隱藏子封裝詳細資料。這個版面配置的整體簡潔性高於這個負數。

使用 SDK 套件

使用 SDK 套件 OOT 非常簡單,只需使用與系統架構和所需 API 級別相關的套件的 package_manifest.json 即可。應針對分散式 .api 檔案檢查套件中的檔案使用情況。

這可讓 OOT 工具和建構作業正確地針對常見陷阱發出警告:

  • 視要移除的已淘汰套件而定。
  • 視系統將移除的套件中,已淘汰的檔案而定。
  • 取決於不在套件明確合約中的套件檔案。

實作這項功能是每個建構系統的 SDK 維護人員負責,並未在本 RFC 中說明。

包含

OOT 存放區可能會在本機套件 (L) 中將 SDK 套件 (R) 子封裝,如下所示:

  1. R 的所有 blob 都會儲存在本機 (下載或複製)。
  2. L 的子套件清單包含 R 適用的 meta.far blob 參照。
  3. 將 L 發布至存放區時,R 的所有 blob 以及 R 的所有子套件 (以遞迴方式) 也會全部加入。

RFC-0154 中定義的子套件解析度將會適用於套件 L。

這個步驟可確保套件會在產品專屬的存放區中重新發布,過程中可能會受到離線 blob 壓縮和其他最佳化作業的規範。包含已發布套件的遠端 TUF 存放區「不得」直接做為 Fuchsia 裝置的套件來源,因為內含的 blob 可能需要進一步處理,才能符合特定產品的需求。

請注意,這個步驟會使用現有的套件和 blob 發布工具 (例如 ffx package),而該工具可能會執行「insert-if-absent」作業,針對重複的 blob 進行最佳化。

效能

這項設計不會影響裝置端效能。

這項設計可能會對建構效能造成負面影響,因為這麼做會導致建立新的封存檔案。但只有在建立 SDK 封存 (在 GN 引數中的 build_sdk_archives=true) 時才會產生影響。與完整建構所花的時間相比,建立新封存的負擔應該可以忽略。

人體工學

對平台擁有者而言,此設計提供一個慣用介面,以定義哪些套件應做為 Fuchsia 平台版本的一部分發布。此外,這個應用實例引進的 .api 檔案能讓您更輕鬆地預測程式碼變更對 SDK 套件組合的影響。內建的自動檢查功能可進一步確保平台擁有者能完全掌控 SDK 套件代表的合約。

回溯相容性

發布適用於 OOT 用量的套件是致力提供一致的語意和命名機制。此 RFC 提議使用 API 版本管理來支援軟性遷移作業,並提供可要求移除或大幅修改合約的路徑。

安全性考量

本提案允許以 Fuchsia SDK 編寫的 OOT 軟體,直接納入及參照與 SDK 版本一併發布的軟體。雖然軟體本身是開放原始碼,但在編譯和發布二進位檔時仍須小心。

這項程序可能類似於目前在 IDK 中發布預先建構的二進位檔,其中提供包含我們所產生二進位檔的封存檔案雜湊值。由於封存檔案含有包含 SDK 套件的輔助封存檔案雜湊值,因此 IDK 發布也會提交至該輔助封存中已編譯的二進位檔。

隱私權注意事項

這項設計不會影響使用者資料的處理方式。

測試

在樹狀結構中,我們將提供要從樹狀結構公開的套件 (.api) 測試 (請參閱上方的「驗證」)。

我們會將 OOT 測試新增至範例存放區,顯示使用 SDK 內含的套件。我們將提供樹狀結構內測試,嘗試模仿這項 OOT 測試的行為,盡可能達到最大程度。這會是上文所述的 OOT 階段的端對端測試,但對於沒有 Bazel 支援的基礎 ffx 指令而言,會是端對端測試。

內部支援 Bazel 時,我們會進一步執行測試來擷取產生的 SDK 套件,確保我們可以使用其中的工具用子封裝 SDK 套件。

說明文件

有關新增、修改及移除 SDK 套件組合中套件的程序,請參閱 https://fuchsia.dev 網站。其中包括如何設定 .api 測試的操作說明。

缺點、替代項目和未知

直接在 SDK 中發布套件的替代方案

先前這項 RFC 的疊代提出了透過 CIPD 個別封存和上傳 SDK 套件的機制,減少對 SDK 套件大小做出的任何變更。同樣地,SDK 使用者也不需要套件發布,因此如果擁有選用的封存封存檔,使用者就能避免完整下載套件。

雖然這個模式對日後工作很有幫助,但此模式並非 SDK 建構作業的標準功能,而且需要用於強制執行 SDK 類別、.api 測試的新建構工具,以及可用來下載和使用這些 SDK 套件 OOT 的新工具。

為解決 SDK 的大小問題,系統只會加入少數用於啟用測試案例的 SDK 套件。舉例來說,這可視為包含:

  • driver_test_realm
  • Embedivist-for-embedding
  • 記錄編碼驗證工具
  • realm_builder_server
  • test_ui_stack

這些套件是作為 x64 的發布子版本建構而成,最多可新增約 43 MiB,或以預設 gzip 設定壓縮的 18 MiB。與目前的 SDK 壓縮 700 MiB 相比,這個結果似乎合理。

如已納入任何額外套件,我們也必須審查 Fuchsia API 委員會的內容與大小變更。此外,這個架構的未來疊代可能會指向遠端儲存的 blob,以進一步協助解決大小儲存空間的問題。

使用目錄結構的替代方案

比起使用目錄結構強制執行架構和 API 級別,位於頂層的檔案可能會以 JSON 格式提供相同的資訊。相較於變更目錄結構,這項設計的優點在於檔案有多麼易於使用。不過,剖析這類檔案需要使用額外的 SDK 工具,因此目前不具備這麼做的優點。

這項工具日後的疊代作業可能會從目錄結構轉出,因此如果發生此情況,將會建構並提供必要工具。

優先藝術與參考資料

Fuchsia Software Delivery 是更新軟體系統的新方式,這類空間較先前的圖片詳細介紹: