RFC-0153:Fuchsia 的 Ninja 自訂設定 | |
---|---|
狀態 | 已接受 |
區域 |
|
說明 | 建議使用 Fuchsia 專用的自訂版 Ninja 建構工具,加快建構速度、改善狀態回報和可用性。 |
問題 | |
Gerrit 變更 | |
作者 | |
審查人員 | |
提交日期 (年-月-日) | 2022-01-22 |
審查日期 (年-月-日) | 2022-03-17 |
摘要
本 RFC 建議使用 Fuchsia 平台建構作業所使用的開放原始碼 Ninja 工具的暫時自訂版本,以便整合多個現有的第三方修補程式,大幅改善其效能和可用性,而上游維護者基於各種原因(詳見「動機」一節),尚未接受這些修補程式。
具體來說,這麼做可讓您:
透過最佳化 Ninja 使用的排程演算法,大幅縮短總建構時間。
改善狀態回報和記錄功能,並解決在 CI 版本中經常觸發的嚴重可用性問題。
在不變更 Ninja 行為的情況下,改善其回應速度和整體效能。例如,將某些 Ninja 作業加快 22 倍。
這項功能可讓您在短時間內重新產生 IDE 繫結,並提供絕佳的互動式程式碼編輯體驗,進而大幅加快建構系統與 Visual Studio Code、Vim 等 IDE 之間的整合作業。
您可以透過在 https://fuchsia.googlesource.com/third_party/ninja
上維護自訂 Git 分支來實現自訂化,這些分支會透過嚴謹的分支策略進行管理,密切追蹤上游,並以一系列小型修補程式呈現,以便在最近的上游記錄中顯示。
我們預計在 2022 年第 3 季修訂這個實驗分支的情況,因為我們有計畫協助上游維護者接受感興趣的提取要求,但這取決於許多超出本 RFC 範圍的因素。不過,屆時您可能就不需要自訂分支了。
提振精神
Ninja 建構工具是由 Google 的 Evan Martin 開發,當時他為 Chrome 1 進行實驗。實驗成功,並轉變為非常實用的工具,成為官方 Chrome 版本的一部分,並開放原始碼。隨後,許多其他廣泛部署的建構設定工具或系統也迅速採用了這個功能,包括:
- GN 建構工具,目前由 Chrome、Fuchsia 和 Pigweed 專案使用。
- Android 專案使用的 Blueprint 和 Kati 工具。
- CMake 建構產生器,支援 Ninja 做為後端。
- 許多 Linux 開放原始碼專案使用的 Meson 建構系統。
- LLVM,開發人員可以使用 CMake 或 GN 進行建構,其中包括為 LLVM 做出貢獻的 Fuchsia 團隊成員。
Ninja 目前已被全球數千名開發人員使用,並以開放原始碼專案的形式在 GitHub 上維護。Ninja 維護人員會盡力確保專案維持原始目標,也就是小巧、簡單、可靠且極具可攜性。
由於許多原因,上游 Ninja 專案的進度仍緩慢,許多有趣的提取要求已等待審查數月,甚至數年。我們已私下與現任維護者聯絡,並瞭解這項問題主要是因為他們沒有足夠的時間,無法妥善審查及測試非瑣碎的變更,且這也是回歸測試套件的狀態所導致的結果,因為該套件目前非常基本,在許多情況下都需要手動測試提取要求。上述維護人員也希望能盡快在 GitHub CI 中增加上述測試套件,以便簡化並加快 Ninja 的維護和進化作業。舉例來說,只要能保證程式碼可在舊版發行版本 (即 Centos 7、Debian 8、Ubuntu 18.04 和 OSX 10.12) 上使用預設工具鍊編譯並正常運作,並透過適當的持續整合驗證強制執行,他並不反對將程式碼集從 C++03 切換為 C++11。
很遺憾,我們遇到許多類似先前所述的阻斷問題,而解決所有問題需要花費大量心力,而 Fuchsia 建構團隊目前沒有能力做到這點。
本 RFC 提供的技術提案可暫時解決此限制,但我們強烈希望能盡快專責處理此情況,並與上游維護人員合作。這類人數可能來自 Fuchsia 專案,或是與 Google 其他團隊共同合作,這些團隊已表示對 Ninja 未來的演進和改善感興趣。不過,這類工作的經費不在本 RFC 的範圍內。
在此同時,自訂的 Ninja 分支可讓您挑選最具影響力的提取要求,為 Fuchsia 開發人員帶來最大效益。為確保這個分支能盡可能與上游保持一致,我們會實施嚴謹的分支策略,確保 Fuchsia 分支一律以一系列修補程式呈現,並以最近的上游版本為基礎。
請注意,這種情況與自訂 Android Ninja 分支截然不同,後者自建立以來已大幅偏離上游,且缺乏 Fuchsia 版本所需的功能。
以下是促成這項 RFC 的引人入勝的提取要求:
使用更優異的排程演算法加快建構作業
三個開放式提取要求可能會大幅縮短建構時間。這兩個選項都會修改 Ninja 根據不同條件挑選要啟動的全新指令方式:
PR 2019:為要啟動的指令指派優先順序,並使用建構記錄來估計指令的執行時間。作者表示,這項做法可將大型建構專案的建構時間從 20 分鐘縮短為 15 分鐘!
PR 1949:限制產生新指令,以免負載限制飽和。這樣一來,當產生太多程序爭奪相同 CPU 資源時,就能避免產生建構機器過載的情況。作者聲稱在特定情況下,可將測試版本的建構時間從 22 分鐘縮短至 15 分鐘。
PR 1140:為 Ninja 新增支援 GNU Make jobserver,這是一種讓 Ninja 和其執行的指令協調 CPU 程序分配方式。這是 Ninja 的 Kitware 分支。請注意與先前 PR 的相似之處。作者未發布任何時間,且這項功能需要叫用的程式明確支援,因此不太可能實用。
修正 Ninja 的長指令建構輸出內容。
在建構期間,Ninja 只會針對已完成的指令列印狀態行。實際上,當執行長時間運作指令時,建構作業會暫停,然後:
如果指令逾時 (在執行長長的 Rust 連結指令時,我們的 CI 機器人就會發生這種情況),輸出內容或 Ninja 記錄檔中絕對不會有任何內容,指出實際逾期的目標/指令!
在終端機上,單行狀態似乎會凍結,並繼續顯示上次完成目標的名稱,這會造成混淆 (許多開發人員認為這是延遲的根本原因)。要查看實際情況,唯一的方法是在另一個終端機中使用「ps」,但很少人知道這一點。
第一點是Fuchsia 版本的問題,如果不修改 Ninja,就無法解決。第二個是使用者多年以來一直抱怨的使用便利性問題,以至於有幾個提取要求以不同方式修正這個問題:
- 為長時間執行的指令列印「still waiting」#678。
- 已提案/仍在執行 #629。
- 列印中斷邊緣的完成狀態 #1026。
- 在指令開始時列印 #1158。
- 新增
NINJA_STATUS_STARTED
和_FINISHED
#1191。 - 在等待單一邊緣時,請列印該邊緣的狀態。
這是因為 Ninja 的輸出內容是文字格式,格式相當有限,而且在發生錯誤時,也會包含任意指令輸出內容。這會導致各種細微問題,並使輸出內容難以可靠地剖析。
事實上,我們在 2015 年有意引入目前的行為,以解決 Ninja 的建構輸出內容無法由機器解析的問題。因此,上游會認為任何輸出格式變更都非常危險。
不過,在 Fuchsia 的情況下,解析 Ninja 輸出的指令碼是由 Fuchsia 基礎架構團隊控制,因此如果輸出格式稍微變更就能完全解決問題,這麼做就很有意義。
序列化狀態更新,以便更有效地篩選記錄
Android 自訂設定的另一個有趣功能,是針對輸出限制的解決方法,可將狀態更新傳送至外部「前端」程式,做為結構化二進位資料串流。
這讓 Android 團隊可以儲存多個輸出串流,並以不同層級記錄,收集錯誤訊息至個別檔案,以及產生可透過 chrome://tracing 載入的精確建構追蹤記錄。
對於 Fuchsia 版本,您應該可以從 Android 分支中擷取該功能,以獲得相同的優點。
請注意,這項提案是做為提取要求提出,但在經過一些長時間的討論後遭到拒絕,因為上游團隊認為,解決輸出剖析問題的最佳方法是將 Ninja 轉換為程式庫,而這項作業的難度相當高,因為程式碼庫的狀態 (有人嘗試使用需要 120 次提交的實驗性 PR)。
改善開發人員體驗
Android 分支已實作大幅加快 (最高可達 22 倍) 的 Ninja 輸入檔案剖析功能,方法是使用執行緒剖析輸入內容的切割內容,並在適當的符記邊界中判斷,並在最後一輪中合併。
這是因為 Android 建構工具會產生約 1.2 GiB 的 Ninja 建構計畫,而這些計畫需要 12 秒才能剖析完畢,才能執行任何操作 (在配備熱門檔案系統快取功能的強大工作站上)。
不幸的是,Fuchsia 版本也遇到類似情況,目前產生的 Ninja 建構計畫約為 800 MiB,解析時間長達 10 秒,因此增量版本的體驗令人不悅。Fuchsia 建構團隊認為這些改善項目相當重要,應整合至建構作業使用的 Ninja 版本。
編寫程式碼時,編譯器錯誤訊息和警告是給開發人員的第一條意見回饋。這在靜態語言中尤其明顯,在 Rust 等新語言中更是如此,因為這些語言的設計目的是在編譯時檢查各種條件。因此,開發人員能否及時取得意見回饋,對他們的工作效率至關重要。等待編譯器輸出的每秒鐘,都是開發人員猜測程式碼是否正確,或可能遭到其他事物分心的時間,而快速提供意見回饋對仍在學習語言的程式設計人員來說尤其重要。
最有效率的環境會在檔案儲存後,立即在開發環境中提供意見回饋。這種「流暢」的感受非常重要,因此 Fuchsia 開發人員已建立工具,讓他們可以使用完全不同的建構系統 (cargo),以便快速取得意見回饋和測試週期。雖然這項工具未受支援且經常發生故障,但仍有部分開發人員在使用。
在 Fuchsia 中,Ninja 會在每次叫用開始時花費最多 10 秒,用於剖析自己的建構檔案 (build.ninja
檔案和其中包含的其他檔案)。這項限制是我們提供意見回饋的最低速度,至少比目前 Cargo 提供的速度慢 25 倍。透過將剖析時間縮短 25 到 100 倍,並結合本季推出的其他工具工作流程,我們可以將 Fuchsia 的程式碼編寫體驗,從「在黑板上寫字般緩慢」提升至「快速且反應靈敏」。結果是開發週期加快、工程師更滿意,以及軟體品質提升。
您可以參考下方的螢幕截圖,瞭解在 IDE 中變更程式碼及查看編譯錯誤的目前體驗。
開發人員意見回饋的延遲,幾乎完全歸因於 Ninja 花費在剖析檔案的時間。請比較下方修補過的 Ninja 可提供的 IDE 體驗示範。
同樣地,fx setup-go
的建立目的是為了在本機開發期間專門使用 Go 建構系統,因為使用 Ninja 的增量建構作業速度太慢。
請注意,Android 團隊在 2019 年提出這項功能,但當時上游維護人員拒絕採用,因為這項功能需要使用 C++11,而這項功能尚未被視為可接受的做法。自那時起,我們已過了一段時間,因此可以考慮進行此類切換,前提是我們會確保實施動機部分所述的保證。
改善使用者介面
我們已提交以下 PR,以便在互動式終端機中實作類似表格的狀態輸出內容,這項功能的靈感來自 Buck 建構系統 (請參閱動畫範例)。
原作者已撤銷此 PR,但可以重新設定基底,在本機建構 Fuchsia 時提供非常不錯的 UI 改善功能。
相關人員
協助人員:
pascallouis@google.com
已獲 FEC 指派,負責透過 RFC 程序引導這項 RFC。
審查者:
shayba@google.com、Build maruel@google.com、Fuchsia Platform EngProd abarth@google.com、平台負責人
其他積極參與過這個主題討論的團隊成員:
brettw@google.com (Fuchsia 工具貢獻者)。fangism@google.com、Build:haowei@google.com、Fuchsia 的 LLVM:jayzhuang@google.com、Build:olivernewman@google.com、Fuchsia CI:phosek@google.com、Fuchsia 工具鍊
諮詢:
fangism@google.com、Build jayzhuang@google.com、Build tmandry@google.com、Fuchsia 上的 Rust rudymathu@google.com、Fuchsia EngProd
社會化:
這個想法最初是由 Fuchsia 團隊的 Rust 團隊成員 tmandry 提出,接著在 Google 內部電子郵件會話串中廣為流傳,現在則在公開論壇中提出,供大家進一步討論及核准。
設計
我們會使用 Fuchsia 控管的 Git 存放區,將上游 Ninja master
分支分支,以便在追蹤上游 origin/master
的同時,管理包含 Fuchsia 專屬自訂項目的分支。修補程式會由分支代碼的擁有者審查,並應視個別情況考量下列事項:
- 提供的福利
- 修補程式的可維護性
- 與上游版本不一致,影響可維護性。
以及擁有者認為適當的其他條件。
按照標準做法,本機修改項目清單會在 FUCHSIA.readme
檔案中追蹤。
該分支的維護者將是 Fuchsia 建構團隊,該團隊負責管理 Fuchsia 建構系統,並對其正確性、可維護性和效能負責。
本 RFC 的目的是確保這個分支不會分歧到難以整合上游變更的程度。為達成此目標,我們會採用下列策略:
讓 Fuchsia 分支與上游保持一致的策略
Fuchsia Ninja 分支應嚴格遵守規則,也就是說,為了盡可能讓分支與上游保持一致,我們會定期建立「usptream-sync」分支,將變更以一系列清晰的修補程式呈現,並置於最近的上游版本之上。以下幾張圖片應該有助於瞭解這裡的意思。
建立自訂 Git 分支時,通常會在現有上游版本之上建立新分支,以便新增新的修訂版本。下圖說明「上游」記錄分支為「紫紅色」分支,並在其上新增 3 次提交的情況:
upstream ___U1__U2___
\
fuchsia \__F1__F2__F3
維護者會將提交內容新增至上游專案。上游和 Fuchsia 的版本歷史現在已分開,如下所示:
upstream ___U1__U2______U3__U4__U5
\
fuchsia \__F1__F2__F3
將上游變更納入 fuchsia 分支的簡單方法,就是執行合併作業,這可能會顯示需要手動解決的提交內容之間的衝突,如下所示:
upstream ___U1__U2______U3__U4___U5___
\ \
fuchsia \__F1__F2__F3____\F4__
其中 F4
代表合併提交。現在,'fuchsia'分支已納入來自上游的所有改善項目,但無法再以一系列修補程式呈現,這些修補程式位於上游最新版本 (即 U5) 之上。
為達成這個目標,您可以避免將上游直接合併至 Fuchsia 分支。請改為建立 fuchsia 分支的副本,然後將其重新對齊至 U5
,解決途中的任何衝突 (這可先在本機上使用 git rebase
作業完成)。我們將這個分支版本稱為 upstream-sync-U5
,如下所示:
upstream ___U1__U2______U3__U4__U5
\ \
upstream-sync-U5 \ \__F1'__F2'__F3'
\
fuchsia \__F1__F2__F3
此時,fuchsia
和 upstream-sync-U5
的功能應相同 (可透過測試強制執行),新提交版本 F1' 到 F3' 的內容甚至可能與 F1 到 F3 相同,除非必須解決某些衝突。
您現在可以將後者合併至前者 (只要從 upstream-sync-U5
取得變更內容,即可解決任何衝突),例如:
upstream ___U1__U2______U3__U4__U5
\ \
upstream-sync-U5 \ \__F1'__F2'__F3'
\ \
fuchsia \__F1__F2__F3_______________\F4
日後同步處理其他上游變更時,可以重複執行這項操作,例如:
upstream ___U1__U2______U3__U4__U5__________________U6__U7
\ \ \
upstream-sync-U5 \ \__F1'__F2'__F3' \
\ \ \
upstream-sync-U7 \ \ \__F1"__F2"__F3"__F5"
\ \ \
fuchsia \__F1__F2__F3_______________\F4__F5______________________\F6
每個新的 upstream-sync-XXX
分支都會將自訂狀態表示為最近的上游版本的一系列新修補程式。這麼做可讓 Fuchsia 變更更容易理解,並在必要時改善將其傳回上游的可能性。
Fuchsia 分支的維護者應盡可能建立上游同步分支。
實作
這個分支會在 https://fuchsia.googlesource.com/third_party/ninja (已存在) 的 Git-on-Borg 例項上維護,而其公開 Gerrit 例項則會用於審查及接受修補程式。
用於為 Fuchsia 平台建構作業建立 Ninja 預先建構的二進位檔的 LUCI 食譜會相應切換其來源 GIT 網址,因為目前該網址會指向上游 GitHub 存放區的鏡像。
成效
縮短 Fuchsia 平台建構時間是這項 RFC 的主要動機,同時也能透過更快的工具回應時間改善開發人員體驗。
人體工學
縮短開發人員意見回饋的時間,我們就能提高開發人員的處理量,並減少環境切換。
低延遲性也為 Fuchsia 的建構系統開啟了新的用途,例如針對依賴產生檔案的原始碼,在 IDE 中回報編譯時的錯誤。這包括使用 FIDL 的程式碼,以及所有 Rust 和 Go 原始程式碼。
回溯相容性
樹狀結構內的工具可依賴 Fuchsia 分支 Ninja 的 CLI 公開的新功能。任何要在 Fuchsia 樹狀結構外使用,且不假設存在 Fuchsia 的 Ninja 分支工具。
不過,這些 Fuchsia 專屬功能應限於最少的必要功能,因為能夠回溯至上游是此 RFC 的重要目標。
舉例來說,針對某些用途,可行的替代做法是編寫離線工具,直接剖析 Ninja 建構計畫並執行相關運算 (如 https://fuchsia-review.googlesource.com/c/fuchsia/+/644561)。
安全性考量
這項變更對安全性應該不會有任何重大影響。Ninja 已可在開發人員的系統上執行任意指令。Ninja 中的任何安全漏洞都可能來自上游,而我們的分支會讓 Fuchsia 工程師審查每項變更,這比現況更為完善。
我們會使用 Google 的標準原始碼工具套件,盡量減少可能發生的原始碼供應鏈問題。
隱私權注意事項
這項提案不應以任何方式影響隱私權。
測試
目前情況是,如果有任何變更提交至 Fuchsia 的 Ninja 分支,系統就會執行 Ninja 測試套件,包括上游同步合併。變更必須遵循 Ninja 的標準測試做法,包括單元測試。
說明文件
標準 README.fuchsia
檔案會新增至 Fuchsia git 分支頂端,說明分支的目前狀態與上游之間的差異。
其中會附上本 RFC 的連結,說明這裡所述的分支管理策略。
缺點、替代方案和未知事項
這項提案的主要缺點,是為了讓分支保持與上游同步,必須盡可能頻繁執行上游同步分支,每個這類作業都可能觸發一或多個重新設定衝突,需要由 Fuchsia 分支的維護人員手動解決。
請注意,要讓這些問題更容易修正,最好的方法是盡可能將 Fuchsia 分支中的變更分解為多個小修補程式 (每個修補程式都會產生可完全測試的來源樹狀結構)。
我們曾考慮嘗試使用 Android Ninja 分支做為起點,但其差異性非常大。只要將最近的上游變更合併至其中,就需要解決大量的衝突 (例如,他們將所有內容切換為 C++17,並完全移除 C++03 支援程式碼),而且要將其重新定基至上游的一系列完整修補程式,將會非常困難。
因此,從上游開始,並在其上重新定義一些 Android 專屬功能,似乎是更好的解決方案,可在幾週內提供結果。
此外,我們也嘗試過其他幾種方法,希望在不變更上游 Ninja 的情況下,獲得與某些上游修補程式相同的好處,但都失敗了。最值得一提的是,我們進行多年調查,並對 GN 進行一系列變更,因此能夠縮短 Ninja 無操作作業的建構時間。不過,我們很遺憾地發現,我們只達成微幅改善。
請注意,Google 的多個團隊都會使用 Ninja,而有些人對以協調方式管理公司內部 Ninja 的演進,或尋找協助上游維護者的做法感興趣。這份 RFC 不會阻止任何這些行為,但其優先事項是先為 Fuchsia 平台帶來效益。
最後,Fuchsia 現已提供 Bazel SDK,並已展示 Fuchsia 元件的 Bazel 建構 (請參閱 RFC-0139)。長期來說,Fuchsia 應考慮使用 Ninja 以外的工具,做為平台建構後端,並探索可能的多年遷移作業。為了符合本 RFC 的目的,我們認為這類遷移作業超出範圍。