經典,Flutter iOS 又修復了一個建置問題,還是很抽象

最近,Flutter 合併了 [*\#185868*](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Fflutter%2Fflutter%2Fpull%2F185868) 這個 PR,這個 PR 的作用是**將 Flutter iOS 工具鏈二進位檔升級為 Fat Binary,從而同時支援 `x86_64` 和 `arm64` 兩種架構**,也就是讓 Apple Silicon(M 系列晶片)不需要 Rosetta 也能原生執行這些工具。

你說 Flutter 不是很早就支援 Apple Silicon 了?其實這是兩個不同概念:

  • Flutter 編譯的 .app 產物(flutter build ios)編譯目標是 iOS 裝置,用的是 arm64 架構(iPhone/iPad 的 CPU 架構),和 Mac 主機是什麼架構完全無關
  • 模擬器上跑(Simulator)在 Apple Silicon Mac 上也是原生的,因為 Flutter 會給模擬器另外建置 arm64-simulator

而這次的 PR 修的是 Flutter 工具鏈本身,也就是執行在 Mac 上的 Flutter 工具鏈裡的二進位工具:

工具用途idevicesyslog讀取 iOS 裝置的即時日誌idevicescreenshot擷取 iOS 裝置螢幕iproxyUSB 埠轉發(連接 Dart VM Service)libimobiledevice / libusbmuxd 等上述工具依賴的底層函式庫這些工具主要負責和 iOS 裝置通訊,它們基本由第三方開源專案(libimobiledevice 系列)編譯過來的,而由於歷史原因,之前一直只編譯了 x86_64,在 Apple Silicon Mac 上只能靠 Rosetta 2 轉譯才能執行。

所以,要讓這些工具支援 Apple Silicon,就需要修改 Flutter 基礎設施(infra)的建置 Recipe(執行在 Chromium CI 的 Python),而 libimobiledevice 生態本身的建置腳本需要正確支援 CFLAGS="-arch arm64 -arch x86_64",然後透過 autoconf/automake 工具鏈編譯出 Fat Binary,再用 lipo 合併:

實際上這個問題在 2022 年的 #121178 就被提出了,但 infra 端的修改(build recipe 的 CL)太容易弄出 bug,所以是直到最近才完成。

當時 #121178 就發現,在 Apple Silicon Mac 上透過 Rosetta 2 轉譯執行會有一些列潛在問題:

  • 效能損耗比較大
  • 可能觸發 Rosetta 相容性問題(程序通訊、socket 行為可能異常)
  • 需要 Mac 安裝了 Rosetta 2

那為什麼最近又想起來要適配了?因為 Rosetta 要退出歷史舞臺了,所以如果再不適配,後面你想跑也跑不了,所以這次也是不得不上。

可能你覺得,不就是加一個編譯 target 罷了?還能整出什麼問題?是的,還真的可以,這個 PR 從提交到合併又整出了問題,簡單來說就是:

  • #181932:想用新版 libimobiledevice + Fat Binary,但新版在 x86 Bot 上有 bug 導致日誌中斷
  • #185384:記錄這個 flake 問題,同時 revert,並推動調查和修復
  • #185868:最終解法是回退到舊版 libimobiledevice,但這次把它正確地編譯成 Fat Binary,放到新的 GCS 路徑

對,最後也不知道為什麼,反正合併即 Revert,然後回退舊版本解決問題,是不是很抽象?但是又有莫名的熟悉感?你說他們合併的時候為什麼不測試?

怎麼可能不測試,PR 在 AI 輔助 Code Review + CI 驗證都跑了好幾輪,但是問題就在於它不是必現,然後 idevicesyslog 可以啟動正常,但是啟動後不知道為什麼日誌流突然就停止了,所以只能在 merge 發布的時候被人發現後才知道這個缺陷。

所以基建鏈條,特別是 CI 鏈條,總有升級必掛、修改必崩的墨菲定律。

事實上整個 IosUsbArtifacts 的工作流程也挺複雜,除了建置成品之後,還需要在流程上每次合理下載和搭配成品,比如在 FlutterCache 裡就有 6 個 IosUsbArtifacts 實例:

arduino 体验AI代码助手 代码解读复制代码static const artifactNames = <String>[
  'libimobiledevice',   // 包含 idevicescreenshot、idevicesyslog
  'libusbmuxd',         // 包含 iproxy
  'libplist',
  'openssl',
  'libimobiledeviceglue',
  'ios-deploy',
];

對於下載後的 zip 需要做「可執行檔存在性檢查」和 stamp 正確檢查,下載解壓後的 .dylib 動態函式庫路徑需要注入到子程序的 DYLD_LIBRARY_PATH 中,否則執行 idevicesyslog 時 macOS 的動態連結器找不到 libimobiledevice.dylib 等相依套件等問題:

當然,最重要的是,這個 PR 目前還 CP 到了 3.41 上面,只能說兜兜轉轉,3.41 上合併了多少 iOS 編譯和除錯問題?雖然過程很抽象,但是結果還是好的,至少未來 Flutter 終於可以完全不需要 Rosetta,也是跟上了新的適配進度。

Flutter 3.41 還真成了 iOS 的主力修復戰場。


原文出處:https://juejin.cn/post/7637433660883714090


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝16   💬11   ❤️1
518
🥈
alicec
📝1   ❤️2
70
🥉
我愛JS
💬1  
4
#5
Gigi
2
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登