RFC-0208:使用 SDK 發布套件

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

提出如何在 SDK 中分發樹狀結構內的套件,以便使用樹狀結構外子包。

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

摘要

這份 RFC 說明我們採用的策略,以 SDK 附件形式,從 Fuchsia 的平台存放區建構及發布套件。這些套件應使用子包裝機制在下游組合。

這項策略包括推出新的 .api 檔案格式,這本身就是 Fuchsia 的新 SDK API 途徑。格式本身和使用該格式的已簽入檔案都會接受 API 委員會審查。

提振精神

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

本 RFC 說明瞭針對一組套件提交的策略,這些套件將從 Fuchsia 平台存放區發布,並說明如何將這些套件納入下游存放區的子套件。

請注意,雖然此 RFC 著重於將這些套件分組為測試的特定情況,但這是透過 SDK 發布套件的通用機制。舉例來說,產品擁有者可能會在實際工作階段中,加入 Fuchsia 團隊編寫的元件。透過 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
  • 「In-tree」是指位於 https://fuchsia.googlesource.com/fuchsia/ 存放區中的程式碼和建構規則。這個存放區會產生 IDK 做為輸出內容。
  • 樹外 (OOT):指的是位於樹外,且使用 SDK 產生軟體和產品的存放區中的程式碼和建構規則。
  • Fuchsia 套件是 Fuchsia 裝置的軟體發行單元,可參照二進位檔/程式庫、元件資訊清單,以及在 Fuchsia 系統上執行某些程式所需的資料檔案。
  • 子包裝可讓 Fuchsia 套件透過容器,將其他 Fuchsia 套件的特定版本做為依附元件來參照。
  • 本 RFC 所述的 SDK 套件是樹狀結構內 Fuchsia 套件,已明確標示為納入 IDK,並可透過擴充功能供 SDK 使用者取用。OOT 存放區可能會使用發布名稱,針對這些套件進行建構 (例如下載或子包裝)。請注意,這項設計的擴充功能可能會允許 OOT 存放區發布自己的套件。

設計

本文件中的關鍵字「MUST」、「MUST NOT」、「REQUIRED」、「SHALL」、「SHALL NOT」、「SHOULD」、「SHOULD NOT」、「RECOMMENDED」、「MAY」和「OPTIONAL」應依 IETF RFC 2119 所述進行解讀。

SDK 套件是由樹狀結構內建構產生的,並做為 IDK 參照的新構件。由於 IDK 包含可供所有建構系統的 SDK 使用的共同構件集,因此可讓所有 Fuchsia SDK 整合功能參照 SDK 套件。

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

  1. 選取:選擇要分發的套件。
  2. 驗證:確保會分發預期的套件組合。
  3. 建構:為所有必要架構建構所選套件。
  4. 捆綁 - 產生將納入 IDK 的套件目錄結構。

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

本節的其餘部分將詳細說明各個階段的設計。

發布 SDK 套件

選項

我們不希望自動將樹狀結構內的所有套件分發給使用 OOT 的使用者,而是必須將特定套件組合提交,並與平台版本一併發布。

我們會建立名為 sdk_fuchsia_package 的新 GN 範本,需要以下資訊:

  • 要發布的 fuchsia_package 目標。
  • 套件適用的 SDK 類別 (例如「合作夥伴」)。
  • 套件加入 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.cm 的套件 sample-packagesample-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 檔案複製到適當位置。這個程序可讓您輕鬆確認預期的變更,並上傳供審查。

我們會記錄評估 SDK 套件 .api 不相符的步驟,並提供可能不安全的變更指南。

.api 檔案會在後續步驟中提供至產生的套件封存檔,以便 OOT 工具使用該檔案驗證套件內部詳細資料是否未受到影響。

這種做法可確保在 SDK 套件中發生重大變更時,要求進行 API 審查,並定義哪些變更具有重要意義。

建築

指定用於發布的套件必須針對我們產生 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 授權程序。

我們採用這個以目錄為基礎的結構,原因如下:

  • 套件可發布至存放區,或用作子套件,而不需要剖析中繼檔案。
  • 系統會自動去除 Blob (即使跨 API 層級),因此在套件含有相同 Blob (例如共用程式庫和二進位檔) 的常見情況下,可節省大量空間 (相較於個別封存每個套件)。
  • 目錄結構可為支援多個 API 級別,以及提供偵錯和發布版本奠定基礎。
  • SDK 中已提供與套件存放區互動的工具 (ffxpm)。
  • 您可以直接參照套件資訊清單,啟用子套件工作流程,輕鬆將套件直接發布至存放區。

這種做法的一個已知缺點是,當套件同時宣告為頂層套件和子套件時,在這種情況下,package_manifest.json 會重複。套件資訊清單檔案的大小不大,因此無須擔心將其列為頂層命名的檔案,並且可透過套件 Merkle 辨識,這有助於我們更好地隱藏子套件的詳細資料。這個版面配置的整體簡易性,遠勝於這個缺點。

使用 SDK 套件

使用 SDK 套件 OOT 的做法,就跟使用套件的 package_manifest.json 一樣簡單,這與架構和系統的所需 API 層級相關。請根據分發的 .api 檔案,檢查套件中檔案的用途。

這可讓 OOT 工具和版本適當地警告常見的陷阱,例如:

  • 取決於要移除的已淘汰套件。
  • 取決於您要移除的套件中淘汰的檔案。
  • 依據套件中的檔案,而該檔案並非套件的明確合約的一部分。

實作這項功能是各個個別建構系統的 SDK 維護者負責的工作,因此未在本 RFC 中說明。

包括

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

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

套件 L 將採用 RFC-0154 定義的子套件解析方式。

這個步驟可確保套件會在產品專屬的存放區中重新發布,且可能會在該程序中進行離線 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
  • archivist-for-embedding
  • log-encoding-validator
  • realm_builder_server
  • test_ui_stack

這些套件是以 x64 的發布子版本建構,未壓縮的大小約為 43 MiB,或經過預設 gzip 設定壓縮後的 18 MiB。與目前的 SDK 相比,這似乎是合理的數值,因為目前的 SDK 經壓縮後約為 700 MiB。

任何包含的額外套件都必須經過 Fuchsia API Council 審查,才能變更內容和大小。此外,這個架構日後的版本可能會指向遠端儲存的 Blob,進一步解決儲存空間大小的問題。

使用目錄結構的替代方案

頂層檔案可使用 JSON 格式呈現相同資訊,而非使用目錄結構來強制執行架構和 API 層級。與變更目錄結構相比,此設計的好處在於檔案的可塑性。不過,剖析這類檔案需要額外的 SDK 工具,而這項工具目前並未提供任何好處。

這類工具日後的版本可能會改變目錄結構,如果發生這種情況,我們會建構並提供必要的工具。

既有技術與參考資料

Fuchsia 軟體提交是更新軟體系統的新方法,這類領域有許多先前技術,請參閱以下文件進一步瞭解: