Xcode 26 build database locked:2026 遠端建置怎麼修?

某個 SSH 工作突然回報 Xcode 26 build database locked,而另一個建置工作仍在同一台遠端 Mac 上執行。

最快解法:先停止共用建置目錄的併行任務,替每個 Job 分配獨立的 DerivedData 路徑;確認沒有殘留 xcodebuild 後仍失敗,才清理受影響專案的建置資料庫,最後用單一任務 Archive 復驗。

這篇文章適合以下情況:

  • 你透過 SSH 在遠端 Mac 執行 xcodebuild,斷線或重試後遇到資料庫鎖定。
  • 你的小型團隊讓 CI 同時執行同一專案的 Build、Test 或 Archive。
  • 你負責維護一台共用 Mac,需要控制工作目錄、程序與輸出產物。

失敗現場與證據基線

不要只複製錯誤紀錄最後一行。build database locked 只能說明目前建置程序無法取得資料庫使用權,不能直接證明是 Xcode 快取損壞。先保留完整命令、工作目錄、程序時間線和第一個有效錯誤。

例如,脫敏後的紀錄應至少包含以下資料:

[09:14:02] Job-A started from /Users/runner/work/app-a
xcodebuild -workspace Sample.xcworkspace \
  -scheme Release \
  -configuration Release \
  -derivedDataPath /Users/runner/build/job-a/DerivedData \
  -archivePath /Users/runner/artifacts/job-a/App.xcarchive \
  archive

[09:14:05] Job-B started from /Users/runner/work/app-a
xcodebuild -workspace Sample.xcworkspace \
  -scheme Release \
  -configuration Release \
  -derivedDataPath /Users/runner/build/shared/DerivedData \
  -archivePath /Users/runner/artifacts/job-b/App.xcarchive \
  archive

[09:14:08] error: database is locked

同時記下這次是 Build、Test 還是 Archive;由主 xcodebuild、Run Script、套件建置工具或其他子程序發起。Apple 將建置系統、Scheme 與 Target Dependencies 分開管理,因此不能把所有鎖定都歸咎於同一類快取問題。可先對照 Apple 的 Xcode Build System 說明Xcode 26 Release Notes

先確認的項目 證據入口 不能直接下的結論
完整 xcodebuild 命令 CI 紀錄、SSH shell 歷史、工作紀錄 不能只因錯誤含 database 就刪除全部 DerivedData
工作目錄與輸出路徑 -derivedDataPath-archivePath、Build Settings 命令列與圖形介面設定可能最後解析到相同位置
啟動程序與父子關係 pspgrep、程序啟動時間 SSH 中斷不代表遠端程序已經結束
第一個有效錯誤 原始建置紀錄、.xcresult 最後一行可能只是上游衝突的結果

注意: 專案名稱、Scheme、使用者名稱、主機位址、Bundle ID、Team ID 與路徑都應先脫敏。排障紀錄若包含簽署秘密或 App Store Connect 權杖,不應直接貼到團隊頻道。

併行程序:先找真正應該留下的工作

同一台遠端 Mac 上,常見的併行來源不只是在介面上重複按下建置:

  • CI 重試已經失敗的工作,但原工作尚未取消。
  • SSH 連線中斷,遠端的 xcodebuild 仍繼續執行。
  • 一個人執行 Archive,另一個人同時執行 Test。
  • 父層 xcodebuild 啟動了測試或腳本子程序,維護者誤以為只有一個工作。
  • 不同 CI 工作使用相同的工作目錄或共享 Runner。

可以先在遠端 Mac 執行:

pgrep -alf 'xcodebuild|XCBBuildService|xctest'
ps -axo pid,ppid,lstart,stat,command | grep -E 'xcodebuild|XCBBuildService|xctest'

這些命令只用來建立證據,不是叫你無條件終止所有相關程序。你要把程序 PID、PPID、啟動時間、命令列和寫入中的輸出互相比對,判斷哪一個 Job 應保留。

優先順序通常是:先讓尚未失去進度的 Archive 或 Test 正常結束,再處理已重試且沒有新輸出的工作;若某個程序只是父程序,必須先確認它的子程序是否仍在產生測試結果或 xcresult。直接 kill 可能造成測試結果不完整、xcarchive 未封存完成、上傳工作中斷,甚至讓 CI 將一次可恢復的工作誤判為失敗。

處理選項 適合條件 代價與驗收
等待正常退出 程序仍有輸出,Archive 或 Test 正在推進 保留結果的機會最高;要持續觀察輸出與程序狀態
終止特定 Job 已確認它是重試工作、沒有新輸出,且不再需要其結果 可能留下不完整的 xcresult;必須標記該 Job 已取消
終止整個程序樹 明確確認父子程序均屬同一個失控工作 可能連帶中斷測試、封存或上傳;不能作為第一個修復動作
立即清除全部快取 沒有合理證據支持此動作 會擴大診斷範圍,還可能刪除仍有價值的中間產物

DerivedData 與輸出路徑

兩個 Job 是否能共用 DerivedData,不能只看它們是否來自同一份原始碼。當兩個程序同時更新相同建置資料庫、模組快取或中間產物時,共用目錄就是高風險邊界;在併行 CI 中,應為每個 Job 建立獨立且可追蹤的路徑。

除了 -derivedDataPath,還要核對以下位置:

  • OBJROOT:物件檔與中間建置內容。
  • SYMROOT:Build Products 和相關符號輸出。
  • -archivePath:最終的 xcarchive
  • -resultBundlePath:測試與建置結果,例如 xcresult
  • 腳本自行指定的輸出目錄。
  • 圖形介面 Scheme 或工作區設定實際解析出的 Build Settings。

Apple 的 Build Settings Reference 可用來核對設定名稱與作用範圍;設定 Target Build Settings 的官方說明 則適合用來確認 Target 層級是否覆蓋了工作區或 CI 傳入值。

可採用這類命名方式:

JOB_ROOT="$PWD/.ci/jobs/${CI_JOB_ID}"
mkdir -p "$JOB_ROOT"

xcodebuild \
  -workspace Sample.xcworkspace \
  -scheme Release \
  -configuration Release \
  -derivedDataPath "$JOB_ROOT/DerivedData" \
  -archivePath "$JOB_ROOT/Archive/App.xcarchive" \
  -resultBundlePath "$JOB_ROOT/Results/App.xcresult" \
  archive

實際 CI 變數名稱依你的系統而定,重點不是複製變數,而是讓每個 Job 的中間產物、封存檔與結果包都能透過同一個 Job 識別碼追蹤。兩個 Job 同時執行時,通過標準應是:兩者都沒有寫入相同的 DerivedData 和 Archive 路徑,並且各自產生可以獨立開啟與檢查的結果包。

最終 Artifacts 可以集中保存,但應在工作完成後才集中;不要為了共用輸出而讓建置中的中間產物共用目錄。既有 xcarchive、dSYM 和簽署資產的保存方式,也應參照 Apple 的簽署 Distribution Code 說明 重新核對。

路徑類型 併行 Job 是否應獨立 建議保存方式
DerivedData 以 Job 識別碼建立專屬目錄
OBJROOT、SYMROOT 不要讓不同 Job 指向同一個中間產物目錄
xcresult 每個 Job 產生獨立結果包,再集中上傳
xcarchive 使用唯一 Archive 路徑,驗證完成後保存
dSYM 與發布 Artifacts 建置期間獨立,完成後可集中 以 Commit、版本或 Job 識別碼歸檔

隱藏巢狀建置

如果程序清單沒有兩個明顯的頂層 xcodebuild,下一個方向是檢查主建置內是否又啟動了建置命令。常見位置包括 Run Script、跨專案 Target Dependencies、Swift Package 建置工具與發布腳本。

先搜尋腳本和 CI 設定:

grep -R "xcodebuild" Scripts .github ci 2>/dev/null
grep -R "archive\|build-for-testing\|test-without-building" Scripts .github ci 2>/dev/null

接著回答三個問題:

  • 子建置是否真的需要獨立的 Scheme 或工作目錄?
  • 父程序與子程序各自負責哪些輸出?
  • 子建置完成後,父程序是否仍會重新寫入同一個 DerivedData?

不要把「關閉平行建置」當成預設修復。它可能只是讓競爭出現得較少,卻沒有消除路徑或腳本邊界問題。Apple 的 Run Script 建置文件 說明了腳本輸入與輸出宣告;Scheme 自訂文件 則可協助你確認 Build、Test、Profile 與 Archive 動作的實際組合。

修改腳本前,先把子任務的輸出改到專屬目錄,並保留原本的回退方式;修改後重新執行並檢查建置紀錄,確認同一個 Scheme 不再被父子任務重複啟動。若腳本只是為了產生一份可由父建置取得的檔案,優先改成明確的輸入輸出依賴,而不是在腳本內重新呼叫完整建置。

無併行程序時的局部清理

只有在確認相關 xcodebuild、測試程序、索引程序與腳本子程序都已退出後,才處理殘留或損壞狀態。清理要由小到大:

  • 先備份原始建置紀錄、.xcresultxcarchive、dSYM 和 CI 輸出。
  • 只移除發生鎖定的 Job 臨時目錄。
  • 若仍失敗,再移除該專案專屬的 DerivedData。
  • 重新建立工作區需要的輸出目錄。
  • 使用完全相同的 xcodebuild 命令做對照測試。
  • 只有在專案專屬清理仍無法恢復時,才評估更廣泛的使用者快取。

刪除 DerivedData 不等於刪除既有 Archive 或簽署憑證,但風險在於你的腳本可能把 Archive、dSYM 或其他發布檔案放在 DerivedData 之下。因此,不能只根據目錄名稱判斷可刪除範圍。

提醒: 清理後若命令成功,仍不能立即宣布修復。你需要把「清理快取」與「隔離併行工作」分開驗證,否則下一次 CI 重試仍可能在相同共享路徑再次觸發鎖定。

復原驗收與 FAQ

驗收應從最小範圍逐步恢復,而不是清理後立刻把所有工作重新開啟:

  • [ ] 單一 Job 執行 Build,確認新 DerivedData 路徑能完整建立。
  • [ ] 單一 Job 執行 Test,確認 xcresult 位置與內容正常。
  • [ ] 兩個 Job 併行執行,確認 DerivedData、Archive 和結果包均不相同。
  • [ ] 執行一次真實 Archive,確認 xcarchive 與 dSYM 都被保存。
  • [ ] 中斷 SSH 連線後重新登入,確認遠端工作狀態可查、輸出仍留存。
  • [ ] 取消一個 Job,確認另一個 Job 不會被連帶終止。
  • [ ] 重啟遠端 Mac 後,確認舊程序不會被錯誤重用,目錄可重新建立。
  • [ ] 檢查 CI 是否把失敗重試限制在原 Job,而不是複製一個仍在執行的工作。

測試結果的判讀可參考 Apple 的測試與結果解讀文件;若專案含有 Swift Package,也應檢查 Apple 的持續整合建置說明,確認套件建置沒有另外使用共享目錄。

若問題只在共享 Runner 出現,優先修正 Job 路徑與取消邏輯;若每次 SSH 斷線後都留下無法控制的程序,應檢查工作啟動方式與遠端工作管理;若主機無法提供獨立工作目錄、完整程序控制或穩定常駐執行,再考慮拆分專用 Runner。

對於現有的遠端 Mac 環境,你可以先參考 遠端 Mac 使用情境,把「併行 Job、斷線重連、主機重啟」列為部署前驗收項目。若目前的主機必須由多人共用、工作目錄無法隔離,或重試機制經常讓舊程序留存,這些限制比單次快取損壞更值得處理;此時查看 KVMFLUX 的遠端 Mac 方案 會比反覆刪除快取更接近根因。

自購 Mac 適合長期穩定重負載、需要實體 USB 裝置或必須完全掌握硬體的人;但共用現有主機常見的缺點是路徑互相覆寫、程序權限難以分隔,以及 SSH 斷線後缺少可靠的恢復邏輯。若你只需要臨時建置、測試獨立的 iOS 打包流程,或希望快速取得可常駐的遠端 Mac,租用 KVMFLUX 能把獨立工作目錄與持續運行環境納入部署考量,而不必先為一台專用打包機承擔完整硬體成本。

延伸閱讀

為遠端建置準備穩定可靠的 Mac 環境

透過 KVMFLUX 租用遠端 Mac,為開發、測試與 Archive 工作提供獨立的建置環境。 減少共用建置目錄與多重程序互相干擾,讓 SSH 遠端工作及持續整合流程更順暢。 按專案需求選擇合適的 Mac 算力與租用方案,無需自行添置及維護實體設備。 立即了解 KVMFLUX 的遠端 Mac 方案,為團隊建立更可控、更穩定的遠端建置流程。

Mac Mini M4 · 16GB / 256GB
按日$19.3 /天
按週$52.2 /週
按月$96.7 /月
按季$263 /季