Сталкивались с багом IDE, когда подсвечивает вызов в
commonMain красным, а сборка проходит. Это не баг IDE, так устроена текущая схема компиляции KMP.Когда собирается, например, JVM-таргет, код из
commonMain компилируется вместе с jvmMain против платформенных артефактов зависимостей, то есть против JAR. Поэтому common код может незаметно зарезолвиться в платформенную декларацию библиотеки. IDE же анализирует commonMain только по metadata KLIB, где есть лишь common-декларации.Из-за этого возникают три класса проблем.
❌ Красный код в IDE компилируется
// lib/jvmMain
class Foo
// app/commonMain
Foo() // IDE: Unresolved reference, сборка: ок
То же самое с
commonTest: тесты могут вызывать код из jvmMain вместо commonMain.❌ Выбирается другая перегрузка
// lib/commonMain
fun foo(x: Any) = "common"
// lib/jvmMain
fun foo(x: String) = "platform"
// app/commonMain
println(foo("")) // IDE ведёт в common, в рантайме печатается "platform"
❌ Вывод типов ломается только на одной платформе
Если
actual-классы на JVM добавляют лишний супертип (например, Serializable), то if (...) ClickEvent() else ScrollEvent() на JVM выводится в Any вместо Event. В итоге .name не резолвится, хотя IDE ошибок не видит.Что меняется с раздельной компиляцией в Kotlin 2.5.0
✔️ 1. Common-код ведёт себя одинаково на всех платформах Сейчас один и тот же вызов из
commonMain может выбрать разные перегрузки на разных таргетах:// lib/commonMain
fun foo(x: Any) = "common"
// lib/jvmMain
fun foo(x: String) = "platform"
// app/commonMain
println(foo("")) // JVM: "platform", остальные таргеты: "common"
С тем же связан вывод типов. Если
actual-класс на JVM добавляет супертип (например, Serializable), то if (...) ClickEvent() else ScrollEvent() на JVM выводится в Any, и код не собирается только там. В новом режиме common-код резолвится один раз и одинаково для всех.✔️ 2. Common-код по-настоящему common Сейчас
commonMain может случайно вызвать платформенный класс зависимости, и всё соберётся. Проблема всплывёт, когда добавите iOS или Wasm, и чинить придётся задним числом. Новый режим ловит это сразу, а исправление явное: через expect/actual.✔️ 3. Тесты проверяют то, что заявлено
commonTest сейчас может вызвать код из jvmMain вместо commonMain. Тест зелёный на JVM, хотя common-контракт он не проверяет. В новом режиме тесты видят только common API.✔️ 4. Задел для ускорения сборки Если common source sets компилируются отдельно, их можно будет собирать инкрементально. JetBrains прямо называет это следующим шагом. Для больших KMP-модулей это потенциально заметный выигрыш.
✔️ 5. Согласованность IDE и компилятор Красный код в IDE больше не будет собираться, а Go to declaration будет вести туда, что реально вызовется.
✔️ Как включить (experimental, по умолчанию выключено):
# gradle.properties
kotlin.kmp.separateCompilation=true
Перед включением важно учесть:
👉 Авторам библиотек нужно публиковать metadata KLIB. KMP Gradle plugin делает это по умолчанию, но теперь без них работать не будет
👉 С cinterop пока есть проблемы коммонизации (KT-88178, KT-41509), так что включайте осторожно
👉 Модули с единственным таргетом пока не затронуты
👉 После включения часть старого кода может перестать собираться. Это места, где common-код случайно опирался на платформенные API
Совет: включите флаг на ветке и посмотрите, что упадёт. Каждая такая ошибка показывает место, где common-код на самом деле не был common.
🔗 Подробнее в блоге Kotlin
#Kotlin #KMP #IDEA
