距離發布只剩兩天時臨時增租一台遠端 Mac,拉取程式碼只花了幾分鐘,第一次用 Xcode 建置卻遲遲無法完成;第二次雖然快了不少,但切換分支後,又遇到舊資源殘留、索引異常或相依套件重新解析。問題通常不在於機器效能不足,而是 DerivedData、Swift 套件下載內容與建置結果全都混在預設目錄裡,不僅難以確認快取是否命中,也無法在租期結束前選擇性匯出。
先建立可比較的建置基準
調校快取前,必須先固定各項變因。請記錄 xcodebuild -version、macOS 版本、提交雜湊、Scheme、建置設定與目標平台。測試期間不要同時升級相依套件、切換 Xcode 或修改編譯參數,否則兩次建置所花的時間便無法直接比較。
至少執行以下三組測試:
- 刪除專案專用的 DerivedData 後執行冷建置。
- 不修改程式碼,以相同命令執行暖建置。
- 修改一個一般 Swift 檔案,再執行增量建置。
每次都要保存開始時間、結束時間、結束代碼與結果套件。圖形介面的進度列適合用來觀察狀態,卻不適合精確比較;自動化記錄應統一透過 xcodebuild 完成。
快取的目的不是讓某一次建置看起來特別快,而是讓相同輸入能得到可預期的耗時與結果。
依照用途拆分快取目錄
建議在工作目錄下建立四類路徑,分別存放原始碼、DerivedData、Swift 套件下載內容,以及結果與記錄。不要讓多個專案共用同一個 DerivedData,也不要讓 Debug 與 Release 互相覆寫。專案數量較多時,還可以加入 Xcode 主要版本與提交分支識別資訊。
| 目錄 | 保存內容 | 建議策略 |
|---|---|---|
Source |
Git 工作樹 | 各專案獨立 |
DerivedData |
索引與中間產物 | 依版本、Scheme、設定隔離 |
SourcePackages |
Swift 套件下載與簽出內容 | 鎖定檔未變更時重複使用 |
Results |
結果套件與記錄 | 依建置時間封存 |
Swift 套件目錄可以重複使用,但前提是相依套件鎖定檔完全一致。若鎖定檔有所變更,應先讓解析程序正常完成,不要直接把舊的簽出目錄複製到新專案。DerivedData 對工具鏈更為敏感;切換 Xcode 後應建立新目錄,而不是覆寫原目錄後繼續使用。
使用固定命令執行建置
以下指令碼會明確指定所有關鍵路徑。請將工作區、Scheme 與目標平台替換成專案實際使用的值。結果套件路徑在執行前必須不存在,因此指令碼會先刪除同名目錄。
#!/bin/bash
set -euo pipefail
ROOT="${HOME}/BuildWorkspace"
SCHEME="Application"
CONFIGURATION="Release"
DERIVED="${ROOT}/DerivedData/${SCHEME}-${CONFIGURATION}"
PACKAGES="${ROOT}/SourcePackages"
RESULT="${ROOT}/Results/${SCHEME}.xcresult"
LOG="${ROOT}/Results/${SCHEME}.log"
mkdir -p "${DERIVED}" "${PACKAGES}" "${ROOT}/Results"
rm -rf "${RESULT}"
xcodebuild \
-workspace "${ROOT}/Source/Application.xcworkspace" \
-scheme "${SCHEME}" \
-configuration "${CONFIGURATION}" \
-destination "generic/platform=iOS" \
-derivedDataPath "${DERIVED}" \
-clonedSourcePackagesDirPath "${PACKAGES}" \
-resultBundlePath "${RESULT}" \
build | tee "${LOG}"
請先在終端機執行 xcodebuild -list,確認工作區確實公開了目標 Scheme。若專案使用的是工程檔而不是工作區,請將 -workspace 改成 -project,不要同時傳入兩者。
為快取目錄產生穩定識別碼
快取鍵至少應包含 Xcode 版本、相依套件鎖定檔摘要、Scheme 與建置設定。也可以加入分支名稱,但不能只使用分支名稱,因為同一分支中的相依套件狀態仍可能改變。
XCODE_KEY="$(xcodebuild -version | shasum -a 256 | cut -c1-12)"
PACKAGE_KEY="$(shasum -a 256 Package.resolved | cut -c1-12)"
printf '%s-%s-%s-%s
' "${XCODE_KEY}" "${PACKAGE_KEY}" "${SCHEME}" "${CONFIGURATION}"
若工作區中有多個 Package.resolved,應明確選擇實際參與建置的檔案。找不到鎖定檔時,不要產生固定的空白鍵值,否則不同的相依套件狀態會被寫入同一目錄。
分階段找出不必要的重新編譯
如果暖建置依然很慢,應先確認時間究竟耗在哪個階段。若相依套件解析反覆執行,請檢查鎖定檔是否被指令碼重寫,以及套件目錄權限是否正確;若編譯階段一再執行,請檢查編譯條件、自動產生的檔案與建置階段指令碼;若連結階段出現異常,則應確認版本資訊或嵌入內容是否每次都被改動。
最常見的問題,是指令碼階段未宣告輸入與輸出。只要指令碼每次都改寫產生檔案的時間戳記,下游目標就會被判定為需要重新建置。解決方式是讓指令碼僅在內容實際變更時替換檔案,並在 Xcode 建置階段中填入準確的輸入與輸出路徑。
不要把「清空所有快取」當成固定操作。這項操作只能確認快取是否受到污染,卻無法解釋污染來源。請先單獨移走專案的 DerivedData;若問題仍然存在,再處理 Swift 套件目錄。如此既能保留診斷證據,也能避免每次都重新下載全部相依套件。
在租期結束前保留真正有用的內容
匯出前,請先停止仍在執行的建置,確認記錄已完整寫入,再封裝相依套件鎖定檔、必要的 Swift 套件下載內容、結果套件與專案指令碼。原始碼應以程式碼儲存庫中已提交的內容為準;工作樹裡尚未提交的修改必須另外檢查。
不建議長期保存完整的 DerivedData。其容量通常很大,內含索引、目的檔,以及與特定工具鏈綁定的中間產物。下次改用不同版本的 Xcode 時,重新產生通常更可靠。敏感憑證、私密金鑰、權杖與暫存環境檔案都不應混入快取封存檔;請先清除 Shell 歷史記錄、暫存鑰匙圈與專案產生的憑證檔案,再核對匯出套件的內容清單。
最後請保留一份簡短記錄,列出工具鏈版本、快取鍵、最後一次成功建置的提交、建置命令、已匯出的路徑,以及明確未匯出的路徑。下一台雲端 Mac 依照這份記錄還原環境,會比直接複製整個家目錄更容易重現。
常見問題
不同 Xcode 版本可以共用 DerivedData 嗎?
不建議。至少應依 Xcode 版本、專案、Scheme 與建置設定分開目錄;切換工具鏈後重新產生 DerivedData,可避免舊索引及中間產物影響結果。
租期結束前最值得匯出的快取是什麼?
優先匯出依賴鎖定檔、可重用的 Swift 套件下載、結果封裝與診斷日誌。物件檔及索引高度依賴工具鏈,通常在新機器重新產生更可靠。
清除全部快取可以解決建置變慢嗎?
它適合用來確認快取污染,但不應成為日常做法。先分別量測依賴解析、編譯、連結與測試時間,再只清理對應階段的目錄。
ArmMacs 雲端 Mac
依專案週期使用獨享實體節點
選擇固定晶片、記憶體、儲存空間、租期與在售節點;庫存可用時即可進入交付流程。