За два дня до релиза команде срочно понадобился дополнительный удалённый Mac. Репозиторий клонировался за несколько минут, но первая сборка в Xcode затянулась; вторая прошла заметно быстрее, однако после переключения ветки появились устаревшие ресурсы, проблемы с индексом или повторное разрешение зависимостей. Обычно причина не в производительности машины, а в том, что DerivedData, загруженные пакеты Swift и результаты сборки находятся в общих каталогах по умолчанию. В такой конфигурации трудно понять, сработал ли кеш, и выборочно экспортировать нужные данные до окончания аренды.
Сначала зафиксируйте сопоставимую базовую сборку
Перед оптимизацией кеша необходимо исключить влияние посторонних переменных. Запишите вывод xcodebuild -version, версию macOS, хеш коммита, Scheme, конфигурацию сборки и целевую платформу. Во время тестов не обновляйте зависимости, не переключайте версию Xcode и не меняйте параметры компиляции, иначе результаты двух запусков нельзя будет корректно сравнить.
Выполните как минимум три теста:
- Удалите относящийся к проекту каталог DerivedData и выполните холодную сборку.
- Не меняя код, повторите сборку той же командой, чтобы проверить прогретый кеш.
- Измените обычный файл Swift и выполните инкрементальную сборку.
Для каждого запуска сохраняйте время начала и завершения, код возврата и пакет результатов. Индикатор выполнения в графическом интерфейсе удобен для наблюдения, но не подходит для точных сравнений; автоматизированные замеры следует выполнять через xcodebuild.
Задача кеша — не показать минимальное время одной конкретной сборки, а обеспечить предсказуемые время и результат при одинаковых входных данных.
Разделите кеши по назначению
В рабочем каталоге рекомендуется создать четыре группы путей: для исходного кода, DerivedData, загруженных пакетов Swift, а также результатов и журналов. Не используйте один каталог DerivedData для нескольких проектов и не позволяйте конфигурациям Debug и Release перезаписывать данные друг друга. Если проектов много, в структуру каталогов можно дополнительно включить основную версию Xcode и идентификатор ветки.
| Каталог | Содержимое | Рекомендуемая стратегия |
|---|---|---|
Source |
Рабочее дерево Git | Отдельно для каждого проекта |
DerivedData |
Индексы и промежуточные артефакты | Разделять по версии, Scheme и конфигурации |
SourcePackages |
Загруженные и извлечённые пакеты Swift | Переиспользовать, пока не изменился lock-файл |
Results |
Пакеты результатов и журналы | Архивировать по времени сборки |
Каталог пакетов Swift можно переиспользовать только при неизменном lock-файле зависимостей. Если он изменился, позвольте системе штатно завершить разрешение зависимостей и не копируйте старый каталог извлечённых пакетов в новый проект. 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, хеш lock-файла зависимостей, 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, явно укажите тот, который действительно участвует в сборке. Если lock-файл не найден, не подставляйте постоянный пустой ключ, иначе разные состояния зависимостей попадут в один каталог.
Найдите лишнюю перекомпиляцию по этапам
Если сборка с прогретым кешем всё ещё выполняется долго, сначала определите этап, на котором теряется время. При постоянном повторном разрешении зависимостей проверьте, не перезаписывает ли скрипт lock-файл и правильно ли настроены права на каталог пакетов. Если повторяется компиляция, проверьте условия компиляции, автоматически создаваемые файлы и скрипты этапов сборки. При проблемах на этапе линковки убедитесь, что сведения о версии или встраиваемое содержимое не изменяются при каждом запуске.
Чаще всего проблема возникает из-за того, что для этапа со скриптом не объявлены входные и выходные файлы. Если скрипт при каждом запуске обновляет временную метку сгенерированного файла, Xcode считает зависимые цели устаревшими и пересобирает их. Скрипт должен заменять файл только при фактическом изменении содержимого, а в соответствующем этапе сборки Xcode необходимо точно указать входные и выходные пути.
Не превращайте «очистку всех кешей» в обязательный этап. Она позволяет выяснить, связан ли сбой с повреждённым кешем, но не объясняет источник проблемы. Сначала отдельно переместите каталог DerivedData проекта. Если проблема сохранится, переходите к каталогу пакетов Swift. Такой порядок сохраняет данные для диагностики и избавляет от повторной загрузки всех зависимостей при каждой проверке.
Сохраните действительно полезные данные до окончания аренды
Перед переносом остановите выполняющиеся сборки, убедитесь, что журналы полностью записаны, а затем упакуйте lock-файлы зависимостей, необходимые загруженные пакеты Swift, пакеты результатов и проектные скрипты. Эталонной копией исходного кода должны служить зафиксированные в репозитории изменения; незакоммиченные файлы в рабочем дереве следует проверить отдельно.
Не рекомендуется хранить DerivedData целиком в течение длительного времени. Этот каталог обычно занимает много места и содержит индексы, объектные файлы и промежуточные артефакты, привязанные к конкретному набору инструментов. При следующем запуске с другой версией Xcode надёжнее создать их заново. Не включайте в архив кеша конфиденциальные сертификаты, закрытые ключи, токены и временные файлы окружения. Сначала очистите историю Shell, временные связки ключей и созданные проектом файлы учётных данных, а затем ещё раз проверьте состав экспортируемого архива.
В завершение сохраните краткую памятку: версию набора инструментов, ключ кеша, последний успешно собранный коммит, команду сборки, экспортированные пути и пути, которые намеренно не экспортировались. Восстановить следующую облачную машину Mac по такой памятке проще и надёжнее, чем копировать весь домашний каталог.
Часто задаваемые вопросы
Можно ли использовать один DerivedData с разными версиями Xcode?
Нет. Разделяйте каталоги по версии Xcode, проекту, схеме и конфигурации. После смены инструментов создавайте DerivedData заново, чтобы старые индексы и промежуточные файлы не влияли на сборку.
Какие данные кеша стоит экспортировать перед окончанием аренды?
Сначала сохраните файлы фиксации зависимостей, повторно используемые загрузки пакетов Swift, пакеты результатов и диагностические журналы. Объектные файлы и индексы надёжнее создать заново.
Поможет ли полное удаление кеша при медленной сборке?
Так можно подтвердить повреждение кеша, но это не постоянное решение. Отдельно измерьте разрешение зависимостей, компиляцию, компоновку и тесты, затем очистите только связанный каталог.
ArmMacs Cloud Mac
Используйте выделенный физический узел в течение срока проекта
Выберите фиксированные чип, объём памяти, хранилище, срок аренды и доступный узел — при наличии на складе заказ перейдёт к оформлению.