Ключевые термины: удалённый Mac, кеш компиляции Xcode, Derived Data, iOS CI, ускорение сборки. Ниже — сводная таблица условий попадания в кеш, политика прогрева и очистки, матрица «clean build vs параметры» с копируемыми командами и FAQ по откату и заполнению диска. Связанные материалы: решения ускорения macOS CI, матрица Submodule и LFS. Навигация по сайту: главная, каталог статей блога.
Принцип кеша и условия попадания: сводная таблица
Инкрементальная сборка Xcode опирается на согласованность пути Derived Data, версии инструментов, набора флагов компиляции и идентичности зависимостей. На удалённом Mac чаще всего «ломают» cache hit эфемерные каталоги, смена -destination без учёта в ключе кеша и обновление SwiftPM без закреплённого Package.resolved. Зафиксируйте в runbook, какие сигналы входят в ключ восстановления артефакта между джобами.
| Фактор | Влияние на cache hit | Что закрепить в ключе / конфиге |
|---|---|---|
| Версия Xcode / CLT | Смена toolchain сбрасывает совместимость модулей | xcodebuild -version, билд-агент; отдельный префикс Derived Data на мажор |
| Путь Derived Data | Один и тот же -derivedDataPath между прогонами одной ветки |
Общий том + подкаталог $JOB_CACHE/xcode/$(shasum Package.resolved Podfile.lock) |
| SwiftPM | Без закреплённого lock частые промахи по исходникам пакетов | Хеш Package.resolved + -clonedSourcePackagesDirPath |
| CocoaPods | Изменение checksum Pods меняет заголовки и модули | Хеш Podfile.lock + версия Ruby/Pods |
| Набор GCC_PREPROCESSOR_DEFINITIONS / SWIFT_ACTIVE_COMPILATION_CONDITIONS | Любое расхождение даёт новый набор объектных файлов | Сериализация активных конфигураций в ключ или отдельные схемы CI |
| Destination (симулятор / устройство) | Разные SDK и архитектуры — разные кеши | Отдельные префиксы для iphonesimulator vs iphoneos |
Для наблюдаемости включите в лог джобы короткий блок диагностики: версию Xcode, путь Derived Data, хеши lock-файлов и выбранный -destination. Это ускоряет разбор «внезапных» холодных сборок на iOS CI.
С точки зрения CI/CD и стабильности узла полезно считать долю инкрементальных прогонов и медиану времени этапа компиляции: резкий рост при неизменном коде почти всегда указывает на смену ключа кеша, конкуренцию за диск или смешение схем в одном префиксе Derived Data.
Прогрев удалённого узла и политика очистки
Стабильный удалённый Mac в роли CI-ноды выигрывает от разделения трёх слоёв: кеш Git (см. стратегию кеша Git и npm), каталог SwiftPM и Derived Data. Прогрев — это не «магический» первый запуск, а воспроизводимая последовательность с теми же флагами, что и боевая джоба.
export DERIVED_DATA_PATH="/Volumes/CI/derived/${XCODE_VERSION}/${LOCK_HASH}"
export SPM_CLONE_DIR="/Volumes/CI/spm/${LOCK_HASH}"
mkdir -p "$DERIVED_DATA_PATH" "$SPM_CLONE_DIR"
xcodebuild -resolvePackageDependencies \
-scheme "YourApp" \
-clonedSourcePackagesDirPath "$SPM_CLONE_DIR" \
-derivedDataPath "$DERIVED_DATA_PATH"
xcodebuild build \
-scheme "YourApp" \
-destination "platform=iOS Simulator,name=iPhone 16,OS=18.2" \
-derivedDataPath "$DERIVED_DATA_PATH" \
-clonedSourcePackagesDirPath "$SPM_CLONE_DIR" \
COMPILER_INDEX_STORE_ENABLE=NO
COMPILER_INDEX_STORE_ENABLE=NO часто снижает объём записи на диск в CI, где индексатор Xcode не нужен; проверьте, что это допустимо для ваших статических анализов. Политика очистки: удаляйте сначала неиспользуемые префиксы по возрасту (например > 14 дней) и по свободному месту. Ориентир для мониторинга — см. следующий раздел FAQ; до критического порога используйте мягкое удаление, а не полный rm -rf ~/Library/Developer/Xcode/DerivedData на общих раннерах.
- Ночной прогрев: после merge в основную ветку выполните разрешение пакетов + build эталонной схемы и зафиксируйте снимок каталога или rsync на общий том.
- Блокировка: при параллельных джобах на одном томе используйте файловые lock-и или отдельные подкаталоги на
BUILD_NUMBER, чтобы не портить кеш. - Проверка диска перед джобой:
df -h "$DERIVED_DATA_PATH"— прервите постановку в очередь, если ниже мягкого порога.
Компромисс параметров и clean build
Clean build гарантирует отсутствие «залипших» объектных файлов, но на удалённом Mac обычно стоит дороже 5–15 минут на крупном приложении. Используйте его точечно, а повседневную матрицу стройте на инкременте с жёстко заданным -derivedDataPath.
| Сценарий | Рекомендация | Исполняемый ориентир |
|---|---|---|
| Регрессии после обновления Xcode | Один полный clean на эталонном пути, затем снимок кеша | xcodebuild clean build + новый префикс DERIVED_DATA_PATH |
| Быстрая проверка PR | Инкремент + фиксированный симулятор | Без clean; тот же -destination |
| Флейки SwiftPM / модули | Таргетированный сброс + повтор | Удалить только SourcePackages и кеш схемы; затем -resolvePackageDependencies |
| Анализ без подписи | Сократить побочные артефакты | CODE_SIGNING_ALLOWED=NO (если допустимо этапом) |
Дополнительные исполняемые переменные, которые часто задают в shell-обёртке раннера: IDEBuildOperationMaxNumberOfConcurrentCompileTasks (ограничение параллельных задач компиляции под число ядер узла), SWIFT_EXEC (только при пинованном toolchain) и RCT_NO_LAUNCH_PACKAGER=1 для React Native — проверяйте релевантность под ваш стек, чтобы не размножать скрытые ветви кеша.
Согласованность аргументов. Любой дрейф в ONLY_ACTIVE_ARCH, DEBUG_INFORMATION_FORMAT или включении биткода исторически менял набор объектников; сверяйте .xcconfig CI с локальными пресетами команды.
Матрица джоб. Держите «быструю» линию на одном симуляторе и одной схеме; тяжёлые комбинации устройств выносите в ночной слот с отдельным ключом кеша, чтобы не вытеснять основной кеш компиляции Xcode.
FAQ: отказоустойчивость и пороги заполнения диска
Мягкий порог: если свободно < 25–30% тома с Derived Data или < 35 ГБ абсолютно — запланируйте фоновую уборку старых префиксов и уведомление в чат CI. Жёсткий порог: < 12–15% или < 18 ГБ — остановите постановку новых джоб, завершите текущие, выполните контролируемую очистку и один эталонный прогрев.
Откат после порчи кеша: пометьте префикс как bad (например переименуйте в .corrupt.<timestamp>), запустите джобу с пустым DERIVED_DATA_PATH, зафиксируйте успешный прогон как новый эталон. При повторении той же ошибке линковки добавьте в pipeline флаг «игнорировать восстановление кеша» для данной ветки на один прогон.
Сеть и диск: длинные копирования снимков Derived Data по NFS без достаточного кеша на клиенте могут съедать выигрыш; измеряйте p95 этапа «restore cache» и сравнивайте с холодной сборкой. Подробнее о стабильности загрузок и повторах см. FAQ по Git, Homebrew и npm.
Итог
Высокая доля попаданий в кеш компиляции Xcode на удалённом Mac достигается не «включением кеша», а согласованностью Derived Data, lock-файлов, -destination и политики прогрева. Clean build оставьте для смены toolchain и аварий; повседневный iOS CI стройте на инкременте с измеримыми порогами диска и явным откатом при порче артефактов.
CTA (внутренние страницы без логина): Тарифы аренды удалённого Mac · Оформить аренду Mac для CI · Центр помощи MacPull · Главная · Все статьи блога. Якорные формулировки для ссылок с других страниц: «Матрица кеша Xcode для iOS CI», «Прогрев Derived Data на удалённом Mac», «Пороги диска для раннера Xcode».
Удалённый Mac под стабильный iOS CI
Нативный macOS, предсказуемый диск и полный контроль над кешем Xcode — оформите аренду или откройте тарифы.