| RFC-0208:透過 SDK 發布套件 | |
|---|---|
| 狀態 | 已接受 |
| 區域 |
|
| 說明 | 建議如何在 SDK 中發布部分樹狀結構內套件,以供樹狀結構外子封裝使用。 |
| 問題 | |
| Gerrit 變更 | |
| 作者 | |
| 審查人員 | |
| 提交日期 (年-月-日) | 2023-02-15 |
| 審查日期 (年-月-日) | 2023-02-10 |
摘要
本 RFC 說明我們從 Fuchsia 平台存放區建構及發布套件的策略,做為 SDK 的附錄。這些套件應使用子封裝機制,在下游進行組合。
這項策略包括推出新的 .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、工具和語言整合構件),可與建構系統感知整合。 其中最值得一提的是 Fuchsia SDK with Bazel。
- 樹內是指 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 套件。
下列階段是在樹狀結構內完成:
- 選取 - 選擇要發布的套裝組合。
- 驗證 - 確保系統會發布預期的套件組合。
- 建構 - 為所有必要架構建構所選套件。
- 組合 - 產生將納入 IDK 的套件目錄結構。
SDK 套件會透過 SDK 中的套件目錄結構 (位於 sdk://packages/),在 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-package,sample-package 是 OOT 發布,因此 SDK 使用者可能會依賴 fuchsia-pkg://host/sample-package#meta/sample.cm。如果 sample.cm 重新命名或移除,該 OOT 建構作業就會失敗。這與平台映像檔中發布的樹狀結構內套件失敗模式類似。更細微地說,如果 sample.cm 需要新的傳入功能 (或任何不相容的資訊清單變更,請參閱下文),先前的用途會意外失敗。
只要提交套件名稱,我們就會遇到上述問題,但明確為套件的所有內容設定版本,負擔又太重。舉例來說,如果我們發布 archivist-for-embedding,由於 50 個依附元件中任何一個發生變更,該套件的部分內容就會隨之變更。這些變更絕大多數不會改變 archivist 元件的介面或行為。因此,我們只會對套件中選定的內容子集進行提交。
合約
理想情況下,我們驗證的套件內容子集應代表使用者可能依賴的合約。在理想情況下,合約應:
- 樹狀結構內明確表示。
- 這正是套件 OOT 使用者可能依賴的項目。
- 可供 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 的所有架構建構 (撰寫本文時為 arm64 和 x64)。視需要可能會推出 debug 和 release 等其他版本,但一開始所有套件都會建構發布版本。
這個程序的輸出內容是一組檔案內容,以及每個 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 中已有與套件存放區互動的工具 (
ffx和pm)。 - 直接參照套件資訊清單即可啟用子封裝工作流程,輕鬆直接發布至存放區。
這種做法的缺點之一是,如果套件同時宣告為頂層套件和子套件,在這種情況下,package_manifest.json 會重複。套件資訊清單檔案夠小,因此同時列為頂層具名檔案,並由套件 Merkle 識別,不會造成問題,且有助於我們隱藏子套件詳細資料。但這個版面配置整體而言相當簡單,因此瑕不掩瑜。
使用 SDK 套件
只要取用與系統架構和所需 API 級別相關的套件 package_manifest.json,即可輕鬆取用 OOT SDK 套件。應根據發布的 .api 檔案檢查套件中的檔案用途。
這樣一來,OOT 工具和建構作業就能正確警告常見的陷阱,例如:
- 視要移除的已淘汰套件而定。
- 依據套件中即將移除的已淘汰檔案。
- 依附於套件中的檔案,但該檔案並非套件明確合約的一部分。
實作這項功能是每個建構系統的 SDK 維護人員的責任,本 RFC 不會說明這項功能。
包括
OOT 存放區可能會在本地套件 (L) 中,將 SDK 套件 (R) 子套件化,如下所示:
- R 的所有 Blob 都會儲存在本機 (已下載或複製)。
- L 的子套件清單包含對 R 的
meta.farblob 的參照。 - 將 L 發布至存放區時,系統也會一併納入 R 的所有 Blob (以及 R 子套件的所有 Blob,以遞迴方式)。
如 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 階段進行端對端測試,但就底層 ffx 指令而言,不支援 Bazel。
如果樹內支援 Bazel,我們也會進行測試,擷取產生的 SDK 套件,確保可以使用內含工具將 SDK 套件子封裝。
說明文件
https://fuchsia.dev 會記錄從 SDK 套件組合新增、修改及移除套件的程序,包括如何設定 .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。與目前壓縮後為 700 MiB 的 SDK 相比,這似乎是合理的。
如果包含任何額外套件,Fuchsia API 委員會將審查內容和大小變更。此外,這個架構的未來疊代版本可能會指向遠端儲存的 Blob,進一步解決大小儲存空間的問題。
使用目錄結構的替代方案
頂層檔案可以 JSON 格式呈現相同資訊,不必使用目錄結構強制執行架構和 API 層級。相較於變更目錄結構,這種設計的優點是檔案具有延展性。不過,如要剖析這類檔案,需要額外的 SDK 工具,目前沒有這方面的優勢。
這項工具的後續疊代版本可能會改用其他目錄結構,如果發生這種情況,我們將建構並提供必要的工具。
既有技術和參考資料
Fuchsia 軟體交付是更新軟體系統的新方法,這個領域有許多先前技術,詳情請參閱下列文件: