遠端建置節點連續運行數天後,即使專案程式碼沒有變更,Xcode 偶爾仍會突然建置失敗:日誌顯示無法開啟 build.db、磁碟映像格式錯誤或資料庫損壞,而且重新執行後仍停在同一階段。此時直接刪除整個 DerivedData 雖然省事,卻也會一併清除索引、模組快取和可重複使用的產物,導致後續建置變慢,並抹除最有價值的故障現場。
在 NUMACS 雲端 Mac 上處理這類問題時,更穩妥的順序是:固定專案入口、保全日誌、確認沒有建置程序占用,再檢查資料庫完整性,最後只重建目標專案的 XCBuildData。
先確認故障確實來自建置資料庫
不要只根據 Xcode 介面的最後一行判斷。先從相同目錄,使用相同的 scheme 和設定執行命令列建置,並儲存完整輸出:
set -o pipefail
mkdir -p "$HOME/build-evidence"
xcodebuild \
-workspace App.xcworkspace \
-scheme App \
-configuration Debug \
-destination 'generic/platform=iOS Simulator' \
build 2>&1 | tee "$HOME/build-evidence/xcodebuild.log"
status=${PIPESTATUS[0]}
echo "$status" > "$HOME/build-evidence/exit-code.txt"
exit "$status"
重點搜尋 build.db、database、malformed、disk image 和 XCBuildData。如果日誌明確指向原始碼編譯錯誤、相依性解析失敗或磁碟空間不足,應先處理對應問題,不能將所有失敗都歸因於資料庫。
修復前至少應保留完整日誌、結束碼和錯誤發生時間。清理完成後,通常無法重現資料庫的原始狀態。
找出目前專案實際使用的 DerivedData
同一台雲端 Mac 可能保留多個同名專案的 DerivedData。根據目錄修改時間猜測,很容易誤刪資料;應讓 xcodebuild 傳回目前的建置設定,再從 BUILD_DIR 推導專案根目錄。
BUILD_DIR=$(
xcodebuild \
-workspace App.xcworkspace \
-scheme App \
-configuration Debug \
-showBuildSettings |
awk -F ' = ' '$1 ~ /^[[:space:]]*BUILD_DIR$/ {print $2; exit}'
)
DERIVED_ROOT=$(dirname "$(dirname "$BUILD_DIR")")
XCBD="$DERIVED_ROOT/Build/Intermediates.noindex/XCBuildData"
printf 'DerivedData: %s
XCBuildData: %s
' \
"$DERIVED_ROOT" "$XCBD"
test -d "$XCBD"
如果管線使用了 -derivedDataPath,應直接重複使用該明確路徑,不要再掃描使用者目錄。這樣可讓指令碼在互動式工作階段和無人值守工作中得到相同結果。
記錄待處理檔案
清理前,先記錄資料庫及相關檔案的大小和修改時間:
find "$XCBD" -maxdepth 1 -type f \
-exec stat -f '%Sm %z %N' -t '%Y-%m-%dT%H:%M:%S%z' {} \; \
> "$HOME/build-evidence/xcbuilddata-files.txt"
這份清單可以確認資料庫是否在失敗前剛被改寫,也便於比較重複發生的故障是否位於同一路徑。
排除程序占用並以唯讀模式檢查 build.db
資料庫被進行中的建置占用時,不應直接刪除。先檢查相關程序和檔案控制代碼:
pgrep -alf 'Xcode|xcodebuild|XCBBuildService' || true
lsof "$XCBD/build.db" || true
如果仍有屬於目前工作的建置程序,應先透過管線的正常取消機制停止工作,並等待子程序結束。不要無差別終止整台機器上的同名程序,因為其他工作目錄可能仍在執行有效工作。
確認沒有程序占用後,以唯讀模式檢查 SQLite 資料庫:
sqlite3 "file:$XCBD/build.db?mode=ro" \
'PRAGMA quick_check;'
結果為 ok 時,表示資料庫結構未發現明顯損壞,應回到日誌繼續檢查權限、空間和專案設定。只有在命令傳回非零值、無法讀取或輸出完整性錯誤時,才進入重建步驟。
| 檢查結果 | 下一步 |
|---|---|
lsof 顯示進行中的建置 |
正常停止工作並重新檢查 |
quick_check 傳回 ok |
保留資料庫並調查其他錯誤 |
| 無占用且完整性檢查失敗 | 重建目標 XCBuildData |
| 路徑不存在 | 核對 workspace、scheme 與 DerivedData 參數 |
最小範圍重建 XCBuildData
先將損壞的目錄移至證據目錄,而不是立即永久刪除。移動操作應在同一個檔案系統內進行,以縮短複製時間:
STAMP=$(date '+%Y%m%d-%H%M%S')
QUARANTINE="$HOME/build-evidence/XCBuildData-$STAMP"
mv "$XCBD" "$QUARANTINE"
mkdir -p "$(dirname "$XCBD")"
接著使用同一組建置參數重新執行。Xcode 會建立新的 XCBuildData 和 build.db,而 DerivedData 中的其他目錄仍會保留。
set -o pipefail
xcodebuild \
-workspace App.xcworkspace \
-scheme App \
-configuration Debug \
-destination 'generic/platform=iOS Simulator' \
build 2>&1 | tee "$HOME/build-evidence/recovery-build.log"
如果最小範圍重建仍然失敗,不要立刻擴大刪除範圍。先比較新舊日誌,確認錯誤是否仍指向資料庫。如果錯誤已轉為相依性、權限或原始碼問題,表示資料庫故障已排除,後續應依照新的錯誤處理。
透過兩次建置完成驗收並預防復發
一次成功不足以證明狀態穩定。第一次建置用來產生新資料庫,第二次增量建置則用來驗證資料庫能否再次讀取和更新。兩次建置都應固定 workspace、scheme、configuration、destination 和 DerivedData 路徑,並分別儲存日誌與結束碼。
驗收時檢查以下項目:
- 新的
build.db已產生,且唯讀quick_check傳回ok。 - 乾淨建置與增量建置都正常完成。
- 日誌不再出現資料庫格式或 XCBuildData 讀取錯誤。
- 建置產物來自預期的設定和目標目錄。
- 確認恢復前,故障現場未被刪除。
預防的重點不是定期清空快取,而是讓每個工作目錄都有明確的 DerivedData 路徑,並確保取消工作時能等待建置子程序結束。執行節點也應持續檢查可用磁碟空間,避免資料庫在寫入期間因空間耗盡而留下不完整檔案。
如果同一路徑反覆損壞,應記錄觸發前的異常結束、磁碟狀態和 Xcode 版本,而不是將清理命令設為每次建置的固定前置步驟。每次都重設快取會掩蓋真正的問題,也會失去增量建置的價值。
最終可將流程收斂為四個動作:儲存證據、確認路徑、唯讀檢查、最小範圍重建。只有這些步驟仍無法恢復時,才考慮清理更大範圍的 DerivedData。這樣既能縮短故障復原時間,也能保留足夠資訊以找出復發原因。
常見問題
build.db 出錯時可以直接刪除整個 DerivedData 嗎?
不建議作為第一步。應先停止相關建置並保存日誌,再只刪除目標專案的 XCBuildData,讓 Xcode 重建資料庫。
如何區分資料庫損壞與程序仍在占用?
先用 lsof 檢查 build.db 是否仍被開啟,再以唯讀模式執行 SQLite quick_check;沒有占用但檢查失敗時才按損壞處理。
如何確認修復不是偶然成功?
固定 workspace、scheme、configuration 與 DerivedData 路徑,依序執行乾淨建置和增量建置,並保存結束碼與結果。
NUMACS 雲端 Mac
將建置工作交給獨享實體工作站
兩種 Apple Silicon 配置皆為獨享實體機,並非虛擬機器,可按日、週、月或季租用,涵蓋新加坡、東京、首爾與香港節點。