🔨 Xcodebuild, Swift Build и Swift-Build: как устроен конвейер сборки в Swift.
Когда вы запускаете сборку проекта, под капотом запускается не один инструмент, а целый конвейер. Многие разработчики воспринимают сборку как нечто монолитное, но в современной экосистеме Swift она разделена на четкие слои абстракции. Понимание того, как xcodebuild, swift build и swift-build взаимодействуют - это ключ к эффективной отладке сложных проблем со сборкой, созданию кастомных инструментов и глубокому пониманию того, как ваши проекты превращаются в исполняемый код.
Абстрактный сердцевинный движок: swift-build как единый планировщик задач:
В основе всего лежит swift-build - низкоуровневый движок, построенный на llbuild. Его задача - не понимать Swift, Xcode-проекты или манифесты пакетов. Его задача - эффективно планировать и исполнять граф зависимостей задач. Он работает с абстрактным промежуточным представлением проекта - PIF (Project Intermediate Format). PIF - это, по сути, универсальный язык, на котором можно описать любую задачу сборки: компиляцию .swift файла, линковку библиотеки, копирование ресурсов. swift-build получает этот граф задач и решает, что, когда и как выполнять, оптимизируя параллелизацию и инкрементальные сборки.
Трансляторы - как swift build и xcodebuild говорят с движком:
Здесь начинается специализация. Ни swift build (система сборки Swift Package Manager), ни xcodebuild не компилируют код сами. Они - трансляторы или фронтенды.
🔵swift build (SwiftPM): его мир - это Package.swift. Он парсит манимуфест, анализирует зависимости, разрешает версии и строит граф таргетов специфичный для пакетов. Затем он транслирует этот граф в универсальный PIF и передает его на выполнение движку swift-build с флагом --build-system=swiftbuild.
🔵xcodebuild: его вселенная - это .xcodeproj с кучей настроек (Build Settings), схем (Schemes) и конфигураций. Он считывает этот комплексный проект, разрешает все переменные, обрабатывает зависимости (включая те же Swift-пакеты через XCFrameworks) и транслирует всю эту информацию в тот же самый PIF. Затем он передает PIF тому же самому движку swift-build.
Ключевое преимущество - разделение ответственности и совместимость:
Польза от такого гениального решения:
🔵Единый кэш и инкрементальность: независимо от того, собираете ли вы пакет через SwiftPM или проект через Xcode, движок swift-build один. Это значит, что кэш объектов (DerivedData) и механизм инкрементальной сборки унифицированы и работают согласованно.
🔵Снижение сложности: движку сборки не нужно знать сотни Build Settings Xcode. Ему говорят: «скомпилируй этот файл с этими флагами». А xcodebuild уже сам разбирается, как из настроек SWIFT_OPTIMIZATION_LEVEL и OTHER_SWIFT_FLAGS сформировать итоговый вызов компилятора.
🔵Возможность для сторонних инструментов: такие проекты, как Tuist или XcodeGen, используют эту же схему. Они не изобретают свой компилятор. Они генерируют .xcodeproj (или даже сразу PIF), а затем используют стандартный xcodebuild или напрямую swift-build для сборки. Это обеспечивает стабильность и совместимость.
Где прячется swiftc? Роль компилятора в этой цепочке:
Компилятор swiftc - это просто еще одна задача в графе, который строит движок swift-build. Когда движок видит в PIF задачу типа «скомпилировать Swift-файл», он вызывает swiftc с конкретным набором аргументов, которые были подготовлены для него фронтендом (xcodebuild или swift build). swiftc не имеет состояния между вызовами и ничего не знает о проекте в целом, он просто компилирует то, что ему передали.
💡 Вывод:
Сборка в экосистеме Swift - это не монолит, а пайплайн с четкими интерфейсами: фронтенд (xcodebuild/swift build) отвечает за понимание формата проекта и генерацию абстрактного плана, а бэкенд (swift-build) - за оптимальное исполнение этого плана через вызов низкоуровневых инструментов вроде swiftc.
Такое разделение позволяет Apple независимо развивать удобные инструменты для разработчиков (Xcode, SwiftPM) и высокопроизводительный движок сборки.
➡️ Подписаться на канал
Мобильный трудоголик
Post #302
1.2K
- ❤ 10
- 👍 4
- 🔥 1
- 🙏 1