截至 2026 年 8 月 27 日,SwiftPM 官方文件已說明 Swift Build 成為預設建置系統,並建議使用 --show-bin-path 取得輸出目錄,而不是繼續猜測 .build 內的層級;官方 Swift Build 遷移文件是這項判斷的依據。
症狀 → 最快解法: 建置指令成功,但後續上傳找不到二進位檔或測試產物 → 讓建置參數與 swift build --show-bin-path 完全一致,將查詢結果當作本次任務的真實輸出路徑。只有先證明是 Swift Build 行為差異,才用官方支援的 native 回退參數做診斷;不要把回退直接當成永久修復。
這篇適合三類人:
- 維護 Shell、Fastlane、Makefile 或 CI 程式碼,升級後遇到「建置成功但找不到產物」的開發者。
- 負責 Swift Package、建置外掛或多套件儲存庫交付流程的維護者。
- 管理遠端 Mac 節點、快取、工具鏈版本與回滾策略的 DevOps 及發布工程師。
先判斷故障屬於哪一層
不要先刪除整個工作區,也不要先把問題歸咎於遠端 Mac 權限。你需要把一次失敗拆成三個互不相同的階段:
- 編譯階段:
swift build本身失敗,通常應先保留完整錯誤輸出、工具鏈資訊與提交版本。 - 路徑解析階段:建置指令已完成,但腳本仍讀取舊的
.build子目錄。 - 工作目錄階段:查詢路徑與實際建置不在同一個儲存庫、組態、架構或 destination 下。
因此,排查紀錄至少要包含:
- 完整建置命令及其工作目錄。
- SwiftPM、Swift Build 與 Xcode 工具鏈版本。
configuration、architecture、destination 及依賴鎖定檔。- 實際執行的路徑查詢命令與輸出。
- 上傳、複製或封裝步驟所使用的來源路徑。
腳本維護者:把路徑查詢改成建置介面
Swift Build 產物路徑查詢
Swift Build 的輸出目錄應由同一組建置條件查詢,而不是由腳本拼接。以下示例使用占位符,查詢參數必須與真正建置時保持一致:
set -eu
REPO_DIR="/path/to/<repository>"
CONFIGURATION="<configuration>"
ARCH="<architecture>"
DESTINATION="<destination>"
cd "$REPO_DIR"
swift build \
--configuration "$CONFIGURATION" \
--arch "$ARCH" \
--destination "$DESTINATION"
BIN_PATH="$(swift build \
--configuration "$CONFIGURATION" \
--arch "$ARCH" \
--destination "$DESTINATION" \
--show-bin-path)"
printf 'Binary path: %s\n' "$BIN_PATH"
如果你的工具鏈不接受某個 destination 參數組合,應以該工具鏈實際支援的形式調整;重點不是複製這段命令,而是確保「建置」和「查詢」使用相同條件。不要一次用 release、另一次用 debug;不要建置 arm64,卻用未指定架構的查詢結果;也不要在一個工作目錄執行建置、在另一個工作目錄讀取路徑。
在 Shell、Makefile、Fastlane 或自訂發布程式中搜尋以下風險:
grep -R "\.build" fastlane Makefile Scripts 2>/dev/null || true
找到硬編碼後,將它改為讀取查詢結果,再由產物名稱、Target 或檔案類型完成下一步定位。公開的命令介面可以作為 CI 合約;Swift Build 的內部資料夾結構則不應直接成為合約。官方輔助程式也展示了以工具輸出協助定位建置資料的做法,可參考官方 build-script-helper.py 實作。
五步驗收順序
- 在全新工作區 checkout 指定提交,避免舊
.build內容掩蓋問題。 - 設定明確的 configuration、architecture、destination 與工具鏈選擇。
- 執行建置,保存終端輸出,不要只保存最後一個退出狀態。
- 用完全相同的參數執行
swift build --show-bin-path,記錄回傳的絕對路徑。 - 從該路徑複製、封裝或上傳產物,並在工作區清理後重新執行一次。
Swift Package 維護者:檢查外掛與資源輸出
Package 作者消費的產物不一定是單一二進位檔。你需要分開驗證建置工具外掛、二進位 Target、資源處理以及腳本外掛,因為它們可能各自保存輸出路徑假設。
外掛輸入與輸出證據
逐項檢查:
- 建置工具外掛是否自行拼接
.build路徑。 - 二進位 Target 是否以固定檔名推算輸出位置。
- 資源處理是否假設所有資源位於同一個產物目錄。
- 腳本外掛是否明確宣告輸入與輸出,或只依賴目前工作目錄。
- 失敗日誌是否同時記錄外掛收到的輸入、產生的輸出與執行目錄。
停止條件
當你無法提供以下任一證據時,不應直接修改 Package 的路徑邏輯:
- 實際建置命令。
--show-bin-path的輸出。- 外掛的輸入與輸出紀錄。
- 同一提交在另一種建置系統下的對照結果。
測試工程師:重新定義測試產物收集
本地 Swift Build 成功,但遠端 Mac CI 無法上傳產物時,測試階段尤其容易被誤判。測試啟動器、覆蓋率檔案、測試日誌與失敗用例資訊,未必共享單一固定目錄或單一執行器。
你應分別確認:
swift test的退出狀態是否代表測試成功。- 測試報告究竟來自測試執行器、日誌聚合器還是外部轉換程式。
- 覆蓋率資料的產生條件及實際來源。
- 失敗用例是否仍可由報告定位,而不是只檢查某個檔案存在。
- 上傳步驟讀取的是查詢後的實際路徑,而非預設
.build子目錄。
CI 平台團隊:隔離快取與遠端 Mac 節點
快取身份
新的快取身份至少應反映以下條件:
- SwiftPM / Swift Build 工具鏈版本。
- 建置系統選擇。
- 架構與 configuration。
- destination。
- Package.resolved 或其他依賴鎖定檔內容。
- 建置腳本與外掛版本。
.build 的清理也要配合快取策略,避免「清理了工作區,卻恢復了舊快取」的假乾淨狀態。
遠端 Mac 節點檢查
在遠端 Mac 上逐一記錄:
- 執行帳戶及其 HOME 目錄。
- 實際工作區的絕對路徑。
xcode-select指向的工具鏈。- SSH 工作階段與非互動式 CI 工作階段的環境差異。
- 節點重啟後工作區、快取掛載及權限是否恢復。
- 冷快取、熱快取與重啟後任務的產物查詢結果。
發布負責人:用雙軌矩陣決定回退
Swift Build 和 native 建置系統的輸出路徑不能只用「哪個看起來比較熟悉」判斷。比較時要觀察路徑取得方式、測試收集、簽名及最終交付,而不只是建置命令是否結束。
| 驗收面向 | Swift Build 軌 | native 軌 | 通過條件 |
|---|---|---|---|
| 建置 | 使用實際參數查詢輸出目錄 | 使用官方回退參數診斷 | 產物位置可追溯 |
| 測試 | 分別驗證退出狀態與報告來源 | 使用相同提交及測試集合 | 失敗用例可定位 |
| 快取 | 工具鏈、系統、架構及鎖定檔納入身份 | 不與 Swift Build 快取混用 | 冷、熱快取結果一致 |
| 簽名 | 從查詢後路徑取用檔案 | 以同一套簽名規則驗證 | 驗證值與交付檔一致 |
| 重啟 | 節點重啟後重新執行 | 保留對照記錄 | 不依賴人工登入或舊工作區 |
路徑修復決策表
| 觀察結果 | 應採取的動作 | 不應採取的動作 | 評分 |
|---|---|---|---|
建置成功,--show-bin-path 可取得正確路徑 | 修正產物收集與快取鍵,留在 Swift Build | 繼續猜測 .build 層級 | **5/5** |
| 建置失敗,錯誤可由最小 Package 重現 | 保存環境並對照 native,必要時追蹤官方問題 | 先清除所有節點資料 | **4/5** |
| 冷快取失敗、熱快取成功 | 重建快取身份及清理流程 | 把熱快取成功視為修復完成 | **2/5** |
| 只有遠端 Mac 失敗 | 比對帳戶、工作區、工具鏈與 SSH 環境 | 直接判定是 SwiftPM 6.4 缺陷 | **3/5** |
| native 成功、Swift Build 失敗 | 暫時隔離或診斷回退,保留最小重現 | 未驗收便切換全部生產節點 | **3/5** |
上線前可勾選驗收清單
- [ ] 全新工作區已使用指定提交重新執行。
- [ ] 建置與
--show-bin-path使用相同 configuration。 - [ ] 建置與路徑查詢使用相同 architecture。
- [ ] destination、工作目錄與工具鏈選擇已記錄。
- [ ] Shell、Fastlane、Makefile 及發布腳本不再硬編碼舊
.build層級。 - [ ] 二進位 Target、資源及腳本外掛的輸入輸出均有紀錄。
- [ ]
swift test退出狀態、測試報告與失敗用例已分開驗證。 - [ ] 冷快取、熱快取及節點重啟後任務均已執行。
- [ ] Swift Build 與 native 使用同一提交完成對照。
- [ ] 若回退,已寫明再次切回 Swift Build 的觸發條件。