ИНЖЕНЕРНАЯ СТАТЬЯ

Диагностика сбоя компилятора Swift на облачном Mac

Диагностика сбоя компилятора Swift на облачном Mac

Один и тот же коммит успешно собирается на компьютере разработчика, но во время непрерывной сборки на облачном Mac процесс swift-frontend аварийно завершается. Повторный запуск иногда проходит успешно, а после очистки кеша ошибка возникает уже в другом файле. Самое опасное в такой ситуации — одновременно менять исходный код, обновлять зависимости и удалять все кеши: исходные условия изменятся сразу по нескольким направлениям, и настоящий триггер сбоя компилятора может исчезнуть. Надёжнее сначала определить тип неисправности, а затем сужать область поиска, изменяя только одну переменную за раз.

Сначала убедитесь, что это действительно сбой компилятора

Ошибка на этапе CompileSwiftSources не обязательно означает падение компилятора. Синтаксическая ошибка, неудачный вывод типов или отсутствующий модуль также приводят на этом этапе к ненулевому коду завершения. Переходить к описанной ниже процедуре имеет смысл при следующих признаках:

  • В журнале прямо указано, что swift-frontend аварийно завершился, либо присутствует signal 11.
  • Вывод содержит Stack dump, сообщение о нарушении внутреннего утверждения или стек вызовов компилятора.
  • В каталоге ~/Library/Logs/DiagnosticReports/ появился соответствующий по времени отчёт swift-frontend.
  • Один и тот же исходный код и одна и та же команда многократно вызывают сбой, то есть проблема не сводится к однократному разрыву удалённого сеанса.

Сначала зафиксируйте сведения об инструментальной цепочке и хосте. Не ограничивайтесь последними десятью строками журнала:

mkdir -p diagnostics
{
  date -u
  sw_vers
  uname -m
  xcode-select -p
  xcodebuild -version
  xcrun swiftc --version
} | tee diagnostics/environment.txt

find "$HOME/Library/Logs/DiagnosticReports" \
  -maxdepth 1 -type f -name 'swift-frontend*' \
  -exec cp {} diagnostics/ \;

Отчёт о сбое, полная команда сборки и вызвавший ошибку исходный код должны относиться к одному и тому же запуску. Если объединить материалы из разных попыток, легко получить ложные зацепки, которые невозможно проверить.

Сохраните исходные условия с помощью изолированной сборки

Для воспроизведения подготовьте отдельный каталог DerivedData и сохраните полный вывод терминала. Не удаляйте глобальные кеши в самом начале: их участие в возникновении сбоя само по себе является переменной, которую ещё предстоит проверить.

set -o pipefail
rm -rf "$PWD/.diagnostics-derived-data"

xcodebuild \
  -workspace App.xcworkspace \
  -scheme App \
  -configuration Debug \
  -destination 'generic/platform=iOS Simulator' \
  -derivedDataPath "$PWD/.diagnostics-derived-data" \
  -jobs 1 \
  build 2>&1 | tee diagnostics/build-single-job.log

Параллелизм здесь снижается до одного задания только для проверки связи сбоя с параллельной компиляцией. Это не рекомендация для постоянной конфигурации. Если компилятор стабильно падает и при одном задании, сохраните этот журнал. Если проблема исчезла, при неизменном исходном коде и фиксированном каталоге DerivedData отдельно проверьте -jobs 2 и обычное для проекта значение параллелизма. Каждый вариант следует выполнить не менее трёх раз, отмечая успешную сборку, обычную ошибку компиляции или падение компилятора. Записи «сборка завершилась ошибкой» недостаточно.

При воспроизведении на облачном Mac OVPS также необходимо зафиксировать фактически выбранный путь к Xcode. Сравнивать только названия версий в интерфейсе недостаточно: в пределах одной основной версии могут различаться номер сборки и инструментальная цепочка Swift.

Составьте сравнительную матрицу с одной изменяемой переменной

Проверку следует начинать с переменных, которые требуют минимальных затрат и меньше всего нарушают исходные условия. За один запуск изменяйте только один параметр и сохраняйте отдельную копию журнала.

Сравниваемый параметр Базовое значение Изменённое значение На какой вопрос нужно ответить
Параллелизм -jobs 1 Обычное значение параллелизма Возникает ли сбой только при параллельной компиляции
DerivedData Изолированный каталог Новый пустой каталог Зависит ли ошибка от существующих промежуточных результатов
Режим компиляции Текущее значение проекта Временное контрольное значение Связана ли ошибка с инкрементальной или цельномодульной компиляцией
Уровень оптимизации Настройка Debug Допустимая для проекта контрольная настройка Возникает ли ошибка только на этапе оптимизации
Инструментальная цепочка Текущая зафиксированная версия Другая проверенная версия Является ли проблема регрессией конкретной инструментальной цепочки

Не следует одновременно переключать Xcode, обновлять зависимости и очищать кеши. Если после смены инструментальной цепочки проблема исчезла, это доказывает только её связь с конкретной комбинацией инструментов, но не подтверждает, что в новой версии ошибка уже исправлена. В одной записи также нужно указывать файл, на котором произошёл сбой, режим компиляции и количество срабатываний.

Распознавайте ложные эффекты кеша

Проверять кеш модулей и базу данных сборки следует только в том случае, если сборка с пустым каталогом DerivedData проходит успешно, а со старым каталогом стабильно завершается ошибкой. Сначала архивируйте проблемный каталог, а затем выполняйте точечную очистку. Массовое удаление сразу лишит возможности сравнить старые и новые результаты сборки.

Сократите проблемный файл до минимального воспроизводимого примера

Найдите в полном журнале завершившееся ошибкой задание компиляции Swift. Если проблему можно воспроизвести отдельно от проекта, скопируйте связанные объявления в Repro.swift и сначала выполните проверку типов:

xcrun swiftc -typecheck Repro.swift 2>&1 | tee diagnostics/repro.log

Сокращайте пример в направлении, обратном зависимостям: сначала удаляйте не относящиеся к проблеме методы, затем реализации протоколов, ограничения обобщённых типов и обёртки свойств. После удаления каждого фрагмента запускайте проверочный сценарий. Не пытайтесь на глаз определить, какой участок синтаксиса «выглядит сложным»: падение компилятора часто вызывается сочетанием двух обычных возможностей языка.

#!/bin/zsh
set -o pipefail

output="$(mktemp)"
xcrun swiftc -typecheck Repro.swift >"$output" 2>&1

if grep -Eiq 'signal 11|segmentation fault|stack dump|swift-frontend.*failed' "$output"; then
  cp "$output" diagnostics/latest-crash.log
  rm -f "$output"
  exit 0
fi

rm -f "$output"
exit 1

Этот сценарий считает успешным результатом сохранение целевого сбоя. Благодаря этому его удобно использовать при ручном бинарном поиске или вместе с инструментами сокращения исходного кода. После каждого шага также проверяйте, что сигнатура ошибки не изменилась. Если внутреннее утверждение сменилось обычной ошибкой типов, значит, одно из ключевых условий уже удалено.

Если воспроизвести проблему в одном файле не удаётся, сохраните минимально необходимую границу модуля: сокращённый проект, обязательные настройки сборки, зафиксированные версии зависимостей и одну команду запуска. Не включайте рабочие данные, учётные данные доступа и посторонние ресурсы.

Подготовьте проверяемый пакет для передачи

Итоговые материалы должны позволять проверить проблему на другом облачном Mac без устных пояснений. Рекомендуется оставить в каталоге только следующие файлы:

  • README.md: ожидаемое поведение, команда запуска, количество повторов и фактические результаты.
  • environment.txt: архитектура системы, номер сборки Xcode и версия Swift.
  • Repro.swift или сокращённый проект: только необходимый исходный код.
  • build.log: полный, необрезанный стандартный вывод и поток ошибок.
  • Отчёт о сбое: диагностический файл, соответствующий времени данного запуска.
  • check.sh: сценарий, код завершения которого однозначно показывает, воспроизведён ли целевой сбой.

Перед передачей ещё раз выполните проверку в новом каталоге. Убедитесь, что сценарий не зависит от абсолютных путей исходного проекта, пользовательского каталога или оставшегося кеша модулей. Если проблема возникает только при определённом уровне параллелизма, явно укажите параметр параллелизма и вероятность воспроизведения. Если сбой наблюдается только с конкретным номером сборки инструментальной цепочки, также зафиксируйте контрольную версию, с которой сборка проходит успешно. В результате получится не расплывчатое сообщение «Swift иногда падает», а набор инженерных доказательств, которые можно воспроизводить, сравнивать и использовать для дальнейшего исправления ошибки.

Часто задаваемые вопросы

Всегда ли ошибка CompileSwiftSources означает падение компилятора?

Нет. На этом этапе также проявляются ошибки синтаксиса, типов и зависимостей. Нужны признаки аварийного завершения swift-frontend: сигнал, Stack dump либо соответствующий системный отчёт.

Нужно ли сохранять полный проект Xcode для воспроизведения?

Нет. Если один файл Swift стабильно вызывает сбой командой swiftc, достаточно файла и точной команды. Сокращённый проект нужен, когда причина зависит от модулей или настроек сборки.

OVPS CLOUD MAC

Перенесите следующую сборку на выделенный физический узел

Выберите одну из трёх конфигураций Apple Silicon и шести доступных узлов. Фактический статус доступности отображается в консоли в реальном времени.

Выбрать конфигурацию и оформить заказ