RFC-0153:Fuchsia 自訂 Ninja

RFC-0153:Ninja 自訂 Fuchsia
狀態已接受
區域
  • 建構
說明

建議採用適用於 Fuchsia 自訂版本的 Ninja 建構工具加快建構速度、改善狀態報告並提升可用性。

問題
  • 11051
變更
作者
審查人員
提交日期 (年/月)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 所開發的實驗實驗1。實驗成功,轉變成一項非常實用的工具,也是 Chrome 官方版本的一部分,且屬於開放原始碼。然後,他們很快就學會運用其他許多其他廣泛部署的建構設定工具或系統,其中包括:

  • GN 建構工具,現已用於 Chrome、Fusia 和 Pigweed 專案。
  • Android 專案使用的 Blueprint 和 Kati 工具。
  • CMake 建構產生器,支援 Ninja 做為後端。
  • 有許多 Linux 開放原始碼專案使用的 Meson 建構系統。
  • LLVM,開發人員可使用 CMake 或 GN 進行建構,其中包括提供 LLVM 的 Fuchsia 團隊成員。

目前全球數千名開發人員使用 Ninja,並且在 GitHub 上以開放原始碼專案的形式進行維護。Ninja 的維護人員會試著確保專案仍真實符合其最初目標:小型、簡單、可靠且極具可攜性。

基於許多原因,上游 Ninja 專案的進度仍然緩慢,同時有許多值得審查的提取要求數月 (甚至數年) 都尚待審查。目前的維護人員已私下聯絡,也注意到您十分遺憾,這主要是因為沒有時間適當審查及測試一般變更,而這也屬於迴歸測試套件的狀態,在許多情況下需要手動測試提取要求。此外,對於提升 Ninja 的維護與發展速度,他們也樂於提供任何協助提升 GitHub CI 中測試套件的相關協助。舉例來說,只要能保證程式碼可在較舊版本 (亦即 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:為 GNU Make taskserver 新增對 Ninja 的支援,這是一種用於 Ninja 的方式,以及其執行的指令來協調其 CPU 程序配置。這項資訊來自 Ninja 的 Kitware 分支版本。請留意與先前 PR 有相似之處作者並未發布任何時間,也較不實用,因為叫用程式必須明確支援這項功能。

修正 Ninja 的建構輸出內容以便執行長指令。

在建構期間,Ninja 只會針對已完成的指令顯示狀態行。實際上,如果長時間執行指令停止建構正在執行,則:

  • 如果指令逾時,如同 CI 機器人在非常長的 Rust 連結指令期間發生,輸出或 Ninja 記錄檔中都不會有任何內容,也無法判斷哪個目標/指令實際已過期!

  • 在終端機上,單行狀態看起來會停滯,但仍顯示最後一個已完成的目標名稱,這會造成「混淆」 (許多開發人員會認為這是延遲的根本原因)。如要看到實際情況,唯一的方法是在另一個終端機中使用「ps」,但實際上很少人知道。

第一個點是 Fchsia 版本的問題,這個問題必須經過修改 Ninja 才能解決。第二項是可用性不相關的干擾,多年來不斷向使用者發出錯誤,直到有多項提取要求,以不同方式進行修正:

這是因為 Ninja 的輸出採用文字格式,但發生錯誤時,格式有限,其中包含任意指令輸出。這麼做會建立所有細微問題,並讓輸出內容難以可靠地剖析

事實上,目前的行為是在 2015 年刻意導入,是為瞭解決 Ninja 的建構輸出內容無法機器剖析的問題。因此,向上游會將輸出格式的任何變更視為非常風險。

然而,在 Fuchsia 的情況下,剖析 Ninja 輸出內容的指令碼會由 Fuchsia 基礎架構團隊控管,如果能完全解決問題,就應該稍微變更輸出格式。

序列化狀態更新,更妥善篩選記錄檔

Android 自訂功能的另一個有趣的功能是輸出限制的解決方法,這種功能會將狀態更新以結構化二進位資料串流的形式,傳送至外部「前端」程式。

這讓 Android 團隊儲存不同記錄層級的多個輸出串流、將錯誤訊息收集至個別檔案,以及產生可使用 chrome://tracing 載入的準確建構追蹤記錄。

對於 Fuchsia 版本,您應從 Android 分支版本擷取該功能,以獲得相同的好處。

請注意,這已提議做為提取要求,但在經過一些長時間 討論之後遭到拒絕,上游的結論是,解決輸出剖析問題的更佳方式是將 Ninja 轉換為程式庫,以「可能」更大範圍,嘗試使用需要 120 個修訂版本的實驗性 PR。

改善開發人員體驗

Android 分支版本以最終傳遞合併的廣告,透過執行緒剖析輸入的分割,因此 Android 分支版本能夠以極快的速度 (最多 22 倍) 剖析 Ninja 輸入檔案。

之所以這麼做,是因為 Android 建構工具在 Ninja 建構計畫中,產生約 1.2 GiB 的 Android 建構工具,需要 12 秒才能先剖析再執行任何動作 (在具有熱檔案系統快取的強大工作站上)。

可惜的是,Fchsia 建構作業現在遇到類似情況,會產生近 800 MiB 的 Ninja 建構計畫,需要長達 10 秒才能剖析,因而導致漸進式建構的不悅。Fuchsia 建構團隊認為這些改善項目十分顯著,因此應整合至該版本使用的 Ninja 版本。

編寫程式碼時,編譯器錯誤訊息和警告是給開發人員的第一行意見回饋。這在靜態語言中尤其重要,而 Rust 等較新語言更適用於在編譯期間檢查多種條件。因此,取得意見回饋的時間是提升開發人員工作效率的關鍵。每花多少時間等待編譯器輸出內容,就代表開發人員不用再猜測程式碼是否正確或可能會受到其他元件幹擾。快速意見回饋對於仍在學習語言的程式設計人員特別重要。

最有效率的環境會在儲存檔案後立即在開發環境中提供意見回饋。這樣的「流程」感覺是,Fuchsia 開發人員建立了工具,讓他們能夠使用完全不同的建構系統 (Cargo),以便快速取得意見回饋並測試週期。儘管系統已定期停止支援並公平破壞,但部分開發人員仍在使用這項工具。

對於 Fuchsia,Ninja 在每次叫用時最多需要 10 秒,才能剖析自己的建構檔案 (build.ninja 檔案和其他檔案)。這個界限非常小,我們能以極快的速度提供回饋,而且速度可能比目前我們能夠提供貨物的速度慢 25 倍以上。將剖析時間縮減 25 至 100 倍,加上本季推出的其他工具工作流程,我們可運用從「黑板上帆船慢速」等經驗,將 Fuchsia 的程式碼從「黑板上寫得變慢」,變成「偷滑又有回應」等經驗。因此能夠加快開發週期、讓工程師更愉快,以及品質更高的軟體。

下方的螢幕截圖說明瞭目前在 IDE 中變更程式碼及查看編譯錯誤的經驗。

螢幕畫面擷取中,顯示速度緩慢的 IDE 意見回饋

開發人員意見回饋的延遲時間幾乎完全歸因於 Ninja 剖析檔案的時間。歡迎透過下方的示範影片,瞭解使用修補的 Ninja 可能的 IDE 體驗。

螢幕截圖顯示快速的 IDE 意見回饋

同樣地,建立 fx setup-go 的目的在於在本機開發期間只使用 Go 建構系統,這是因為使用 Ninja 的漸進式建構作業速度太慢。

請注意,Android 團隊已於 2019 年提議這項實際功能,但當時由於 C++11 對 C++11 的支援規定,目前我們並不受理。此後經過的時間已過,且可考慮到這種切換動作,從而強制執行動機一節中所述的保證。

改善使用者介面

提交下列 PR,以 Buck 建構系統為靈感的互動終端機實作類似資料表的狀態輸出內容 (請參閱動畫範例)。

PR 遭到其作者捨棄,但可在本機建構 Fuchsia 時,重新建構以提供非常優異的 UI 改善項目。

相關人員

講師:

pascallouis@google.com 已由 FEC 任命,透過 RFC 程序阻斷這個 RFC。

審查者:

shayba@google.com,版本 maruel@google.com,Fuchsia Platform EngProd abarth@google.com,平台主管

積極參與先前討論這個主題的其他團隊成員:

brettw@google.com, (做為 Fuchsia 工具貢獻者)。 fangism@google.com、Build haowei@google.com、Fuchsia jayzhuang@google.com、Build olivernewman@google.com、Fuchsia CI phosek@google.com

顧問:

Fangism@google.com,版本 jayzhuang@google.com, Build tmandry@google.com, Rust on Fuchsia rudymathu@google.com、Fuchsia EngProd

社群媒體化:

這個想法最初是由 Fuchsia 團隊的 Rust 提出,接著在 Google 內部電子郵件討論串上進行交流,現在則出現在這個可公開存取的論壇中,以便進一步討論及核准。

設計

系統會使用 Fuchsia 控制的 Git 存放區,建立上游 Ninja master 分支版本的分支,以便管理包含 Fuchsia 專屬自訂項目的分支版本,同時追蹤上游 origin/master。修補程式會由分支的 OWNERS 審查,應視個案的處理情形個別考慮:

  • 提供的福利
  • 修補程式可維護性
  • 從上游的不同之處,會影響可維護性。

以及擁有者認為適合的其他條件。

本機修改清單會以標準做法在 FUCHSIA.readme 檔案中追蹤。

該分支的維護人員將成為 Fuchsia 建構團隊,負責 Fuchsia 建構系統及其正確性、可維護性和效能。

這個 RFC 的目的是要確保這個分支版本不會偏離導致上游變更難以整合的點。為達成這個目標,系統會採取下列策略:

讓 Fuchsia 分店鄰近上游的策略

Fuchsia Ninja 分支版本應謹慎而行,也就是說,為了盡可能與上游保持近距離,我們會定期建立「usptream-sync」分支,在近期的上游版本之上,以一系列簡潔修補的方式呈現變更項目。其中一些圖片應該有助於您瞭解

建立自訂 Git 分支時,一個通常會在現有上游版本上方建立新的分支版本,藉此新增修訂版本。下圖說明將「上游」記錄分支到一個「紫紅色」分支的情況,並在上方加入 3 個修訂版本:

upstream  ___U1__U2___
                      \
fuchsia                \__F1__F2__F3

修訂版本會由維護人員新增至上游專案。上游和紫紅色的歷史記錄現在已變得不同,如下所示:

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

fuchsiaupstream-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 原始碼的程式碼。

回溯相容性

樹狀結構內工具可採用由 Ninja 的 CLI 所公開的新功能。凡是打算在福奇亞樹外使用的工具,都不得假設 Ninja 的分支存在。

然而,這些 Fuchsia 專用功能應限制在嚴格下限,因為能夠復原至上游是此 RFC 的重要目標。

舉例來說,在某些用途中,有效的替代方法是撰寫離線工具,直接剖析 Ninja 建構計畫,然後根據這些計畫執行計算 (例如 https://fuchsia-review.googlesource.com/c/fuchsia/+/644561)。

安全性考量

這項變更應該不會對安全性造成任何重大影響。Ninja 現在可以在開發人員的系統上執行任意指令。Ninja 的任何安全漏洞都可能和上游簡單,而我們的分支也要由 Fuchsia 工程師負責審閱每項變更,而這些變化能夠取決於現狀。

透過使用 Google 的標準原始碼套件工具,我們可減輕可能發生的任何原始碼供應鏈問題。

隱私權注意事項

本提案不得以任何方式影響隱私權。

測試

目前,Ninja 測試套件將針對任何提交至 Fuchsia Ninja 分支的變更執行,包括上游同步合併。變更必須遵循 Ninja 的標準測試做法,包括單元測試。

說明文件

系統會在 Fuchsia Git 分支版本頂端新增標準 README.fuchsia 檔案,說明分支版本與上游的目前狀態差異。

其中包含這個 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,我們會將這類遷移作業視為超出範圍。