Spec-Zone.ru › Kotlin 1.9

Что нового в Kotlin 1.6.20

Выпущено: 4 апреля 2022 года

Kotlin 1.6.20 демонстрирует предварительные версии будущих функций языка, устанавливает иерархическую структуру в качестве значения по умолчанию для многоплатформенных проектов и предлагает эволюционные улучшения других компонентов.

Также вы можете ознакомиться с кратким обзором изменений в этом видео:

Язык

В Kotlin 1.6.20 вы можете опробовать две новые функции языка:

  • Прототип контекстных получателей для Kotlin/JVM

  • Определенно неимеющие значение null типы

Прототип контекстных получателей для Kotlin/JVM

Функция является прототипом, доступным только для Kotlin/JVM. При включенном -Xcontext-receivers, компилятор будет генерировать предварительные бинарные файлы, которые нельзя использовать в производственном коде. Используйте контекстные получатели только в своих тестовых проектах. Мы ценим ваши отзывы на YouTrack.

В Kotlin 1.6.20 вы больше не ограничены одним получателем. Если вам нужно больше, вы можете сделать функции, свойства и классы контекстно-зависимыми (или контекстными), добавив контекстные получатели в их объявление. Контекстное объявление выполняет следующие действия:

  • Оно требует, чтобы все объявленные контекстные получатели присутствовали в области вызывающего кода в качестве неявных получателей.

  • Оно вводит объявленные контекстные получатели в область тела как неявные получатели.

interface LoggingContext {
    val log: Logger // This context provides a reference to a logger 
}

context(LoggingContext)
fun startBusinessOperation() {
    // You can access the log property since LoggingContext is an implicit receiver
    log.info("Operation has started")
}

fun test(loggingContext: LoggingContext) {
    with(loggingContext) {
        // You need to have LoggingContext in a scope as an implicit receiver
        // to call startBusinessOperation()
        startBusinessOperation()
    }
}

Чтобы включить контекстные получатели в свой проект, используйте опцию компилятора -Xcontext-receivers. Подробное описание функции и её синтаксиса можно найти в KEEP.

Обратите внимание, что реализация является прототипом:

  • При включенном -Xcontext-receivers, компилятор будет генерировать предварительные бинарные файлы, которые нельзя использовать в производственном коде

  • Поддержка контекстных получателей в IDE пока минимальна

Опробуйте функцию в своих тестовых проектах и поделитесь своими мыслями и опытом с нами в этом задании YouTrack. Если у вас возникнут проблемы, пожалуйста, создайте новое задание.

Определенно неимеющие значение null типы

Определенно неимеющие значение null типы находятся в Бета-версии. Они почти стабильны, но в будущем могут потребоваться шаги миграции. Мы сделаем всё возможное, чтобы свести к минимуму необходимые изменения.

Для лучшей межплатформенной совместимости при расширении обобщённых Java-классов и интерфейсов, Kotlin 1.6.20 позволяет помечать обобщённый параметр типа как определённо неимеющий значение null в месте использования с новым синтаксисом T & Any. Синтаксическая форма заимствована из обозначения пересечения типов и сейчас ограничена параметром типа с верхними границами имеющими значение null слева от & и неимеющим значение null Any справа:

fun <T> elvisLike(x: T, y: T & Any): T & Any = x ?: y

fun main() {
    // OK
    elvisLike<String>("", "").length
    // Error: 'null' cannot be a value of a non-null type
    elvisLike<String>("", null).length

    // OK
    elvisLike<String?>(null, "").length
    // Error: 'null' cannot be a value of a non-null type
    elvisLike<String?>(null, null).length
}

Установите версию языка на 1.7 для включения этой функции:

kotlin {
    sourceSets.all {
        languageSettings.apply {
            languageVersion = "1.7"
        }
    }
}
kotlin {
    sourceSets.all {
        languageSettings {
            languageVersion = '1.7'
        }
    }
}

Узнайте больше об определённо неимеющих значение null типах в KEEP.

Kotlin/JVM

Kotlin 1.6.20 добавляет:

  • Улучшения совместимости методов по умолчанию в интерфейсах JVM: новое @JvmDefaultWithCompatibility аннотацию для интерфейсов и изменения совместимости в режимах -Xjvm-default

  • Поддержка параллельной компиляции одного модуля в бэкенде JVM

  • Поддержка вызываемых ссылок на конструкторы функциональных интерфейсов

Новая аннотация @JvmDefaultWithCompatibility для интерфейсов

Kotlin 1.6.20 добавляет новую аннотацию @JvmDefaultWithCompatibility: используйте ее вместе с опцией компилятора -Xjvm-default=all для создания метода по умолчанию в интерфейсе JVM для любого неабстрактного члена в любом Kotlin-интерфейсе.

Если у вас есть клиенты, которые используют ваши Kotlin-интерфейсы, скомпилированные без опции -Xjvm-default=all, они могут быть бинарно несовместимы с кодом, скомпилированным с этой опцией. До Kotlin 1.6.20, чтобы избежать этой проблемы совместимости, рекомендуемый подход заключался в использовании режима -Xjvm-default=all-compatibility и также аннотации @JvmDefaultWithoutCompatibility для интерфейсов, которым не нужна такая совместимость.

Этот подход имел некоторые недостатки:

  • Вы легко могли забыть добавить аннотацию при добавлении нового интерфейса.

  • Обычно в непубличных частях кода больше интерфейсов, чем в публичном API, поэтому вам приходится использовать эту аннотацию во многих местах в коде.

Теперь вы можете использовать режим -Xjvm-default=all и помечать интерфейсы аннотацией @JvmDefaultWithCompatibility. Это позволяет добавить эту аннотацию ко всем интерфейсам в публичном API один раз, и вам не нужно использовать какие-либо аннотации для нового непубличного кода.

Оставьте свой отзыв об этой новой аннотации на этом билете YouTrack.

Изменения совместимости в режимах -Xjvm-default

Kotlin 1.6.20 добавляет возможность компилировать модули в режиме по умолчанию (опция компилятора -Xjvm-default=disable) по отношению к модулям, скомпилированным с режимами -Xjvm-default=all или -Xjvm-default=all-compatibility. Как и раньше, компиляция также будет успешной, если все модули имеют режимы -Xjvm-default=all или -Xjvm-default=all-compatibility. Вы можете оставить свой отзыв на этом билете YouTrack.

Kotlin 1.6.20 устаревает режимы compatibility и enable опции компилятора -Xjvm-default. В описаниях других режимов есть изменения, касающиеся совместимости, но общая логика остаётся прежней. Вы можете ознакомиться с обновлёнными описаниями.

Для получения дополнительной информации о методах по умолчанию в Java-интероперации см. документацию по интероперации и эту статью блога.

Поддержка параллельной компиляции одного модуля в бэкенде JVM

Поддержка параллельной компиляции одного модуля в бэкенде JVM — экспериментальная. Она может быть удалена или изменена в любое время. Требуется включение (см. подробности ниже), и вы должны использовать её только в оценочных целях. Мы будем признательны за ваши отзывы на YouTrack.

Мы продолжаем работу над улучшением времени компиляции нового бэкенда JVM IR. В Kotlin 1.6.20 мы добавили экспериментальный режим бэкенда JVM IR для компиляции всех файлов в модуле параллельно. Параллельная компиляция может сократить общее время компиляции до 15%.

Включите экспериментальный параллельный режим бэкенда с помощью опции компилятора опции компилятора -Xbackend-threads. Используйте следующие аргументы для этой опции:

  • N — количество потоков, которые вы хотите использовать. Оно не должно быть больше числа ваших ядер процессора; в противном случае параллелизация перестанет быть эффективной из-за переключения контекста между потоками

  • 0 — использовать отдельный поток для каждого ядра процессора

Gradle может запускать задачи параллельно, но этот тип параллелизации не сильно помогает, когда проект (или большая его часть) — просто одна большая задача с точки зрения Gradle. Если у вас очень большой монолитный модуль, используйте параллельную компиляцию для более быстрого компилирования. Если ваш проект состоит из множества небольших модулей и имеет параллелизацию сборки, реализованную Gradle, добавление дополнительного уровня параллелизации может навредить производительности из-за переключения контекста.

Параллельная компиляция имеет некоторые ограничения:

  • Она не работает с kapt, потому что kapt отключает бэкенд IR

  • По своему дизайну она требует больше памяти JVM. Объем памяти пропорционален количеству потоков

Поддержка вызываемых ссылок на конструкторы функциональных интерфейсов

Поддержка вызываемых ссылок на конструкторы функциональных интерфейсов — экспериментальная. Она может быть удалена или изменена в любое время. Требуется включение (см. подробности ниже), и вы должны использовать её только в оценочных целях. Мы будем признательны за ваши отзывы на YouTrack.

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

Рассмотрим следующий код:

interface Printer {
    fun print()
}

fun Printer(block: () -> Unit): Printer = object : Printer { override fun print() = block() }

С поддержкой вызываемых ссылок на конструкторы функциональных интерфейсов этот код может быть заменен просто на объявление функционального интерфейса:

fun interface Printer {
    fun print()
}

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

documentsStorage.addPrinter(::Printer)

Сохраните бинарную совместимость, пометив устаревшую функцию Printer аннотацией @Deprecated с DeprecationLevel.HIDDEN.

@Deprecated(message = "Your message about the deprecation", level = DeprecationLevel.HIDDEN)
fun Printer(...) {...}

Используйте опцию компилятора -XXLanguage:+KotlinFunInterfaceConstructorReference для включения этой функции.

Kotlin/Native

Kotlin/Native 1.6.20 продолжает развитие новых компонентов. Мы сделали ещё один шаг к согласованному опыту работы с Kotlin на других платформах:

  • Обновление нового менеджера памяти

  • Конкурентная реализация фазы сбора мусора в новом менеджере памяти

  • Инициализация классов аннотаций

  • Взаимодействие с Swift async/await: возврат Swift's Void вместо KotlinUnit

  • Улучшенные трассировки стека с libbacktrace

  • Поддержка автономных исполняемых файлов Android

  • Улучшения производительности

  • Улучшенная обработка ошибок при импорте модулей cinterop

  • Поддержка библиотек Xcode 13

Обновление нового менеджера памяти

Новый менеджер памяти Kotlin/Native находится в стадии Альфа. Он может изменяться несовместимо и в будущем потребовать ручную миграцию. Мы ценим ваши отзывы на YouTrack.

С Kotlin 1.6.20 вы можете попробовать альфа-версию нового менеджера памяти Kotlin/Native. Она устраняет различия между JVM и Native платформами, обеспечивая согласованный опыт разработчика в многоплатформенных проектах. Например, вам будет намного проще создавать новые кроссплатформенные мобильные приложения, работающие как на Android, так и на iOS.

Новый менеджер памяти Kotlin/Native снимает ограничения на совместное использование объектов между потоками. Он также предоставляет потокобезопасные примитивы для работы с памятью без утечек, которые не требуют специального управления или аннотаций.

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

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

Конкурентная реализация фазы сбора мусора в новом менеджере памяти

Если вы уже перешли на новый менеджер памяти, о котором было объявлено в Kotlin 1.6, вы можете заметить значительное улучшение времени выполнения: наши бенчмарки показывают среднее улучшение на 35%. Начиная с 1.6.20, для нового менеджера памяти также доступна конкурентная реализация фазы сбора мусора. Это должно улучшить производительность и сократить время пауз сборщика мусора.

Чтобы включить эту функцию для нового менеджера памяти Kotlin/Native, используйте следующую опцию компилятора:

-Xgc=cms 

Пожалуйста, поделитесь своими отзывами о производительности нового менеджера памяти в YouTrack.

Инициализация классов аннотаций

В Kotlin 1.6.0 инициализация классов аннотаций стала стабильной для Kotlin/JVM и Kotlin/JS. Версия 1.6.20 предоставляет поддержку для Kotlin/Native.

Узнайте больше об инициализации классов аннотаций.

Взаимодействие с Swift async/await: возврат Swift's Void вместо KotlinUnit

Взаимодействие с конкурентностью Swift async/await является экспериментальным. Оно может быть удалено или изменено в любое время. Используйте его только в оценочных целях. Мы ценим ваши отзывы на YouTrack.

Мы продолжаем работу над экспериментальным взаимодействием с Swift async/await (доступно начиная со Swift 5.5). Kotlin 1.6.20 отличается от предыдущих версий способом работы с suspend функциями с типом возврата Unit.

Ранее такие функции в Swift представлялись как async функции, возвращающие KotlinUnit. Однако правильный тип возврата для них — Void, аналогично не-подвешенным функциям.

Чтобы избежать разрыва существующего кода, мы вводим свойство Gradle, которое заставляет компилятор переводить Unit-возвращающие приостановленные функции в async Swift с типом возврата Void.

# gradle.properties
kotlin.native.binary.unitSuspendFunctionObjCExport=proper

Мы планируем сделать это поведение по умолчанию в будущих выпусках Kotlin.

Улучшенные трассировки стека с помощью libbacktrace

Использование libbacktrace для определения местоположений исходного кода — экспериментальное свойство. Его могут удалить или изменить в любое время. Используйте его только для оценочных целей. Мы будем признательны за ваш отзыв по этому вопросу в YouTrack.

Kotlin/Native теперь может генерировать подробные трассировки стека с указанием файлов и номеров строк для лучшей отладки linux* (за исключением linuxMips32 и linuxMipsel32) и androidNative* целей.

Эта функция использует библиотеку libbacktrace в качестве подсистемы. Посмотрите следующий код, чтобы увидеть пример различий:

fun main() = bar()
fun bar() = baz()
inline fun baz() {
    error("")
}
  • До версии 1.6.20:

Неперехваченное исключение Kotlin: kotlin.IllegalStateException: at 0 example.kexe 0x227190 kfun:kotlin.Throwable#<init>(kotlin.String?){} + 96 at 1 example.kexe 0x221e4c kfun:kotlin.Exception#<init>(kotlin.String?){} + 92 at 2 example.kexe 0x221f4c kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 92 at 3 example.kexe 0x22234c kfun:kotlin.IllegalStateException#<init>(kotlin.String?){} + 92 at 4 example.kexe 0x25d708 kfun:#bar(){} + 104 at 5 example.kexe 0x25d68c kfun:#main(){} + 12
  • Версия 1.6.20 с libbacktrace:

Неперехваченное исключение Kotlin: kotlin.IllegalStateException: at 0 example.kexe 0x229550 kfun:kotlin.Throwable#<init>(kotlin.String?){} + 96 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Throwable.kt:24:37) at 1 example.kexe 0x22420c kfun:kotlin.Exception#<init>(kotlin.String?){} + 92 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:23:44) at 2 example.kexe 0x22430c kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 92 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:34:44) at 3 example.kexe 0x22470c kfun:kotlin.IllegalStateException#<init>(kotlin.String?){} + 92 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:70:44) at 4 example.kexe 0x25fac8 kfun:#bar(){} + 104 [inlined] (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/libraries/stdlib/src/kotlin/util/Preconditions.kt:143:56) at 5 example.kexe 0x25fac8 kfun:#bar(){} + 104 [inlined] (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:4:5) at 6 example.kexe 0x25fac8 kfun:#bar(){} + 104 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:2:13) at 7 example.kexe 0x25fa4c kfun:#main(){} + 12 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:1:14)

На целях Apple, которые уже имели местоположения файлов и номера строк в трассировках стека, libbacktrace предоставляет больше деталей для вызовов вложенных функций:

  • До версии 1.6.20:

Неперехваченное исключение Kotlin: kotlin.IllegalStateException: at 0 example.kexe 0x10a85a8f8 kfun:kotlin.Throwable#<init>(kotlin.String?){} + 88 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Throwable.kt:24:37) at 1 example.kexe 0x10a855846 kfun:kotlin.Exception#<init>(kotlin.String?){} + 86 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:23:44) at 2 example.kexe 0x10a855936 kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 86 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:34:44) at 3 example.kexe 0x10a855c86 kfun:kotlin.IllegalStateException#<init>(kotlin.String?){} + 86 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:70:44) at 4 example.kexe 0x10a8489a5 kfun:#bar(){} + 117 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:2:1) at 5 example.kexe 0x10a84891c kfun:#main(){} + 12 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:1:14) ...
  • Версия 1.6.20 с libbacktrace:

Неперехваченное исключение Kotlin: kotlin.IllegalStateException: at 0 example.kexe 0x10669bc88 kfun:kotlin.Throwable#<init>(kotlin.String?){} + 88 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Throwable.kt:24:37) at 1 example.kexe 0x106696bd6 kfun:kotlin.Exception#<init>(kotlin.String?){} + 86 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:23:44) at 2 example.kexe 0x106696cc6 kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 86 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:34:44) at 3 example.kexe 0x106697016 kfun:kotlin.IllegalStateException#<init>(kotlin.String?){} + 86 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:70:44) at 4 example.kexe 0x106689d35 kfun:#bar(){} + 117 [inlined] (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/libraries/stdlib/src/kotlin/util/Preconditions.kt:143:56) >> at 5 example.kexe 0x106689d35 kfun:#bar(){} + 117 [inlined] (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:4:5) at 6 example.kexe 0x106689d35 kfun:#bar(){} + 117 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:2:13) at 7 example.kexe 0x106689cac kfun:#main(){} + 12 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:1:14) ...

Чтобы получить улучшенные трассировки стека с libbacktrace, добавьте следующую строку в gradle.properties.

# gradle.properties
kotlin.native.binary.sourceInfoType=libbacktrace

Пожалуйста, расскажите нам, как работает отладка Kotlin/Native с libbacktrace в этом вопросе YouTrack.

Поддержка автономных исполняемых файлов Android

Ранее исполняемые файлы Android Native в Kotlin/Native фактически не были исполняемыми файлами, а были библиотеками, которые можно было использовать как NativeActivity. Теперь есть возможность генерировать стандартные исполняемые файлы для целей Android Native.

Для этого в части build.gradle(.kts) вашего проекта настройте блок исполняемого файла для вашей androidNative цели. Добавьте следующий параметр двоичного файла:

kotlin {
    androidNativeX64("android") {
        binaries {
            executable {
                binaryOptions["androidProgramType"] = "standalone"
            }
        }
    }
}

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

binaryOptions["androidProgramType"] = "nativeActivity"

Спасибо Mattia Iavarone за реализацию!

Улучшения производительности

Мы усердно работаем над Kotlin/Native, чтобы ускорить процесс компиляции и улучшить ваш опыт разработки.

Kotlin 1.6.20 включает некоторые улучшения производительности и исправления ошибок, которые влияют на LLVM IR, который генерирует Kotlin. Согласно бенчмаркам на наших внутренних проектах, мы получили следующие средние приросты производительности:

  • 15% уменьшение времени выполнения

  • 20% уменьшение размера кода как для релизных, так и для отладочных библиотек

  • 26% уменьшение времени компиляции релизных библиотек

Эти изменения также обеспечили 10% уменьшение времени компиляции отладочной библиотеки для крупного внутреннего проекта.

Для достижения этого мы реализовали статическую инициализацию для некоторых сгенерированных компилятором синтетических объектов, улучшили структуру LLVM IR для каждой функции и оптимизировали кэши компилятора.

Улучшенная обработка ошибок при импорте модулей cinterop

В этом релизе представлена улучшенная обработка ошибок в случаях импорта модуля Objective-C с помощью инструмента cinterop (как это обычно делается для CocoaPods). Ранее, если при работе с модулем Objective-C возникала ошибка (например, при возникновении ошибки компиляции в заголовке), вы получали неинформативное сообщение об ошибке, например, fatal error: could not build module $name. Мы расширили эту часть инструмента cinterop, поэтому вы получите сообщение об ошибке с расширенным описанием.

Поддержка библиотек Xcode 13

Библиотеки, поставляемые с Xcode 13, полностью поддерживаются в этом релизе. Вы можете использовать их в любом месте вашего кода Kotlin.

Kotlin Multiplatform

1.6.20 принесла следующие заметные обновления для Kotlin Multiplatform:

  • Поддержка иерархической структуры теперь по умолчанию для всех новых проектов multiplatform

  • Плагин Kotlin CocoaPods Gradle получил несколько полезных функций для интеграции с CocoaPods

Поддержка иерархической структуры для проектов multiplatform

Kotlin 1.6.20 поставляется с поддержкой иерархической структуры, включенной по умолчанию. С ее введением в Kotlin 1.4.0 мы существенно улучшили фронтенд и сделали стабильнее импорт в IDE.

Ранее существовало два способа добавления кода в проект multiplatform. Первый — вставить его в платформенно-специфический набор исходных файлов, ограниченный одной платформой и не подлежащий повторному использованию другими. Второй — использовать общий набор исходных файлов, общий для всех платформ, которые в данный момент поддерживаются Kotlin.

Теперь вы можете обмениваться исходным кодом между несколькими похожими целевыми платформами нативных приложений, которые повторно используют значительную часть общей логики и API сторонних библиотек. Данная технология обеспечит правильные зависимости по умолчанию и найдет доступный API в общем коде. Это устраняет сложную настройку сборки и необходимость использования обходных путей для получения поддержки IDE при совместном использовании наборов исходных файлов между целевыми платформами нативных приложений. Это также поможет предотвратить небезопасное использование API, предназначенного для другой платформы.

Эта технология будет полезна и разработчикам библиотек, так как иерархическая структура проекта позволяет им публиковать и использовать библиотеки с общим API для подмножества платформ.

По умолчанию библиотеки, опубликованные с иерархической структурой проекта, совместимы только с проектами, имеющими иерархическую структуру.

Улучшение совместного использования кода в вашем проекте

Без поддержки иерархической структуры нет прямого способа обмениваться кодом между некоторыми, но не всеми целевыми платформами Kotlin. Один популярный пример — обмен кодом между всеми целевыми платформами iOS и доступ к платформенно-специфическим зависимостям, например, Foundation.

Благодаря поддержке иерархической структуры проекта, теперь вы можете добиться этого «из коробки». В новой структуре наборы исходных файлов образуют иерархию. Вы можете использовать платформенно-специфические языковые функции и зависимости, доступные для каждой платформы, к которой компилируется данный набор исходных файлов.

Например, рассмотрим типичный проект multiplatform с двумя целевыми платформами — iosArm64 и iosX64 для устройств и симуляторов iOS. Kotlin-инструменты понимают, что у обеих платформ одинаковая функция, и позволяют вам получить доступ к этой функции из промежуточного набора исходных файлов, iosMain.

iOS hierarchy example

Цепочка инструментов Kotlin предоставляет правильные зависимости по умолчанию, такие как Kotlin/Native stdlib или нативные библиотеки. Кроме того, инструменты Kotlin постараются найти именно ту часть API, которая доступна в общем коде. Это предотвращает такие случаи, как, например, использование функции, специфичной для macOS, в коде, общем для Windows.

Больше возможностей для авторов библиотек

Когда multiplatform-библиотека публикуется, API ее промежуточных наборов исходных файлов теперь также публикуется вместе с ней, делая его доступным для потребителей. Снова, цепочка инструментов Kotlin автоматически определит доступный API в наборе исходных файлов потребителя, внимательно следя за небезопасными использованиями, такими как использование API, предназначенного для JVM, в коде JS. Узнайте больше о разделении кода в библиотеках.

Настройка и настройка

Начиная с Kotlin 1.6.20, все ваши новые проекты multiplatform будут иметь иерархическую структуру проекта. Дополнительная настройка не требуется.

  • Если вы уже включили ее вручную, вы можете удалить устаревшие опции из gradle.properties:

    # gradle.properties
    kotlin.mpp.enableGranularSourceSetsMetadata=true
    kotlin.native.enableDependencyPropagation=false // or 'true', depending on your previous setup
    
  • Для Kotlin 1.6.20 рекомендуется использовать Android Studio 2021.1.1 (Bumblebee) или более позднюю версию для наилучшего опыта.

  • Вы также можете отказаться от использования. Чтобы отключить поддержку иерархической структуры, установите следующие опции в gradle.properties:

    # gradle.properties
    kotlin.mpp.hierarchicalStructureSupport=false
    

Оставьте свой отзыв

Это значительное изменение для всей экосистемы. Мы будем рады получить ваши отзывы, чтобы сделать его еще лучше.

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

Плагин Kotlin CocoaPods Gradle

Для упрощения интеграции с CocoaPods Kotlin 1.6.20 предоставляет следующие функции:

  • Плагин CocoaPods теперь имеет задачи, которые собирают XCFrameworks со всеми зарегистрированными целевыми платформами и генерируют файл Podspec. Это может быть полезно, когда вы не хотите интегрироваться непосредственно с Xcode, но хотите собрать артефакты и развернуть их в локальном репозитории CocoaPods.

    Узнайте больше о создании XCFrameworks.

  • Если вы используете интеграцию CocoaPods в своих проектах, вы привыкли указывать требуемую версию Pod для всего проекта Gradle. Теперь у вас больше вариантов:

    • Укажите версию Pod напрямую в блоке cocoapods

    • Продолжайте использовать версию проекта Gradle

    Если ни один из этих свойств не настроен, вы получите ошибку.

  • Теперь вы можете настроить имя CocoaPod в блоке cocoapods вместо изменения имени всего проекта Gradle.

  • Плагин CocoaPods добавляет новое свойство extraSpecAttributes, которое вы можете использовать для настройки свойств в файле Podspec, которые ранее были закодированы жестко, например libraries или vendored_frameworks.

kotlin {
    cocoapods {
        version = "1.0"
        name = "MyCocoaPod"
        extraSpecAttributes["social_media_url"] = 'https://twitter.com/kotlin'
        extraSpecAttributes["vendored_frameworks"] = 'CustomFramework.xcframework'
        extraSpecAttributes["libraries"] = 'xml'
    }
}

См. полную справку по плагину Kotlin CocoaPods Gradle DSL.

Kotlin/JS

Улучшения Kotlin/JS в версии 1.6.20 в основном касаются компилятора IR:

  • Инкрементальная компиляция для библиотек разработки (IR)

  • Ленивая инициализация свойств верхнего уровня по умолчанию (IR)

  • Отдельные файлы JS для модулей проекта по умолчанию (IR)

  • Оптимизация класса char (IR)

  • Улучшения экспорта и генерации деклараций TypeScript (IR и legacy бэкэнды)

  • @AfterTest гарантии для асинхронных тестов

Инкрементальная компиляция для библиотек разработки с использованием компилятора IR

Для повышения эффективности разработки Kotlin/JS с помощью компилятора IR мы ввели новый режим инкрементальной компиляции.

При построении библиотек разработки с помощью задачи compileDevelopmentExecutableKotlinJs Gradle в этом режиме компилятор кеширует результаты предыдущих компиляций на уровне модуля. Он использует кэшированные результаты для неизмененных исходных файлов во время последующих компиляций, ускоряя их выполнение, особенно при небольших изменениях. Обратите внимание, что это улучшение предназначено исключительно для процесса разработки (ускорение цикла редактирование-сборка-отладка) и не влияет на сборку производственных артефактов.

Для включения инкрементальной компиляции для библиотек разработки добавьте следующую строку в файл проекта gradle.properties:

# gradle.properties
kotlin.incremental.js.ir=true // false by default

В наших тестовых проектах новый режим ускорил инкрементальную компиляцию до 30%. Однако, чистая сборка в этом режиме стала медленнее из-за необходимости создания и заполнения кэшей.

Пожалуйста, поделитесь своими впечатлениями об использовании инкрементальной компиляции с вашими проектами Kotlin/JS в этом задании YouTrack.

Ленивая инициализация свойств верхнего уровня по умолчанию с использованием компилятора IR

В Kotlin 1.4.30 мы представили прототип ленивой инициализации свойств верхнего уровня в компиляторе JS IR. Благодаря устранению необходимости инициализации всех свойств при запуске приложения, ленивая инициализация сокращает время запуска. Наши измерения показали ускорение примерно на 10% в реальном приложении Kotlin/JS.

Теперь, после доработки и тщательного тестирования этого механизма, мы делаем ленивую инициализацию свойств верхнего уровня по умолчанию в компиляторе IR.

// lazy initialization
val a = run {
    val result = // intensive computations
        println(result)
    result
} // run is executed upon the first usage of the variable

Если по какой-то причине вам нужно инициализировать свойство немедленно (при запуске приложения), пометьте его аннотацией @EagerInitialization.

Отдельные файлы JS для модулей проекта по умолчанию с использованием компилятора IR

Ранее компилятор JS IR предлагал возможность генерировать отдельные .js файлы для модулей проекта. Это была альтернатива по умолчанию — один .js файл для всего проекта. Этот файл может быть слишком большим и неудобным в использовании, так как при каждом использовании функции из вашего проекта вам нужно включать весь файл JS в качестве зависимости. Наличие нескольких файлов добавляет гибкость и уменьшает размер таких зависимостей. Эта функция была доступна с опцией компилятора -Xir-per-module.

Начиная с версии 1.6.20, компилятор JS IR по умолчанию генерирует отдельные .js файлы для модулей проекта.

Компиляция проекта в один .js файл теперь доступна с помощью следующей свойства Gradle:

# gradle.properties
kotlin.js.ir.output.granularity=whole-program // `per-module` is the default

В предыдущих версиях экспериментальный режим по модулям (доступный через флаг -Xir-per-module=true ) вызывал main() функции в каждом модуле. Это несовместимо с обычным режимом одного .js файла. Начиная с версии 1.6.20, функция main() будет вызываться только в главном модуле в обоих случаях. Если вам необходимо выполнить какой-либо код при загрузке модуля, вы можете использовать свойства верхнего уровня, помеченные аннотацией @EagerInitialization. См. Ленивая инициализация свойств верхнего уровня по умолчанию (IR).

Оптимизация класса char

Класс Char теперь обрабатывается компилятором Kotlin/JS без создания обёртки (аналогично inline classes). Это ускоряет операции с символами в коде Kotlin/JS.

Помимо повышения производительности, это меняет способ экспорта Char в JavaScript: теперь он переводится в Number.

Улучшения экспорта и генерации деклараций TypeScript

Kotlin 1.6.20 включает несколько исправлений и улучшений механизма экспорта (аннотация @JsExport), включая генерацию деклараций TypeScript (.d.ts). Мы добавили возможность экспортировать интерфейсы и перечисления, а также исправили поведение экспорта в некоторых особых случаях, о которых нам сообщили ранее. Более подробную информацию см. в списке улучшений экспорта в YouTrack.

Подробнее о использовании кода Kotlin из JavaScript.

@AfterTest гарантии для асинхронных тестов

Kotlin 1.6.20 обеспечивает корректную работу функций @AfterTest с асинхронными тестами в Kotlin/JS. Если тип возврата функции теста статически разрешается как Promise, компилятор теперь планирует выполнение функции @AfterTest в соответствующий обратный вызов then().

Безопасность

Kotlin 1.6.20 добавляет несколько функций для повышения безопасности вашего кода:

  • Использование относительных путей в klibs

  • Сохранение yarn.lock для проектов Kotlin/JS Gradle

  • Установка зависимостей npm со значением --ignore-scripts по умолчанию

Использование относительных путей в klibs

Библиотека в формате klib содержит сериализованное представление IR исходных файлов, которое также включает их пути для генерации правильной отладочной информации. До Kotlin 1.6.20 хранимые пути к файлам были абсолютными. Поскольку авторы библиотек могут не захотеть делиться абсолютными путями, версия 1.6.20 имеет альтернативный вариант.

Если вы публикуете klib и хотите использовать только относительные пути к исходным файлам в артефакте, теперь вы можете передать опцию компилятора -Xklib-relative-path-base с одним или несколькими базовыми путями к исходным файлам:

tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinCompile::class).configureEach {
    // $base is a base path of source files
    kotlinOptions.freeCompilerArgs += "-Xklib-relative-path-base=$base"
}
tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinCompile).configureEach {
    kotlinOptions {
        // $base is a base path of source files
        freeCompilerArgs += "-Xklib-relative-path-base=$base"
    }
}

Сохранение yarn.lock для проектов Kotlin/JS Gradle

Функция была перенесена обратно в Kotlin 1.6.10.

Плагин Kotlin/JS Gradle теперь предоставляет возможность сохранения файла yarn.lock, что позволяет заблокировать версии зависимостей npm для вашего проекта без дополнительной конфигурации Gradle. Эта функция вносит изменения в стандартную структуру проекта, добавив автоматически сгенерированную директорию kotlin-js-store в корень проекта. Внутри неё находится файл yarn.lock.

Мы настоятельно рекомендуем добавить директорию kotlin-js-store и её содержимое в систему управления версиями. Добавление файлов блокировки в систему управления версиями – это рекомендуемая практика, так как она гарантирует, что ваш проект будет собираться с точно таким же деревом зависимостей на всех машинах, независимо от того, являются ли они средами разработки на других машинах или службами CI/CD. Файлы блокировки также предотвращают неявное обновление зависимостей npm при проверке проекта на новой машине, что является проблемой безопасности.

Инструменты, такие как Dependabot, также могут анализировать файлы yarn.lock ваших проектов Kotlin/JS и предупреждать вас, если какой-либо пакет npm, от которого вы зависите, уязвим.

При необходимости вы можете изменить имя директории и файла блокировки в файле сборки:

rootProject.plugins.withType<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin> {
    rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().lockFileDirectory =
        project.rootDir.resolve("my-kotlin-js-store")
    rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().lockFileName = "my-yarn.lock"
}
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin) {
    rootProject.extensions.getByType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension).lockFileDirectory =
        file("my-kotlin-js-store")
    rootProject.extensions.getByType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension).lockFileName = 'my-yarn.lock'
}

Изменение имени файла блокировки может привести к тому, что инструменты анализа зависимостей перестанут его распознавать.

Установка зависимостей npm по умолчанию с параметром --ignore-scripts

Функция была перенесена обратно в Kotlin 1.6.10.

Плагин Kotlin/JS Gradle теперь по умолчанию предотвращает выполнение скриптов жизненного цикла во время установки зависимостей npm. Это изменение направлено на снижение вероятности выполнения вредоносного кода из уязвимых пакетов npm.

Чтобы вернуться к старой конфигурации, вы можете явно включить выполнение скриптов жизненного цикла, добавив следующие строки в build.gradle(.kts):

rootProject.plugins.withType<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin> {
    rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().ignoreScripts = false
}
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin) {
    rootProject.extensions.getByType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension).ignoreScripts = false
}

Дополнительную информацию о зависимостях npm проекта Kotlin/JS Gradle.

Gradle

Kotlin 1.6.20 содержит следующие изменения для плагина Kotlin Gradle:

  • Новые свойства kotlin.compiler.execution.strategy и compilerExecutionStrategy для определения стратегии выполнения компилятора Kotlin

  • Устаревание опций kapt.use.worker.api, kotlin.experimental.coroutines, и kotlin.coroutines

  • Удаление опции сборки kotlin.parallel.tasks.in.project

Свойства для определения стратегии выполнения компилятора Kotlin

До Kotlin 1.6.20, вы использовали системную переменную -Dkotlin.compiler.execution.strategy для определения стратегии выполнения компилятора Kotlin. Эта переменная могла быть неудобной в некоторых случаях. Kotlin 1.6.20 вводит свойство Gradle с тем же именем, kotlin.compiler.execution.strategy, и свойство задачи компиляции compilerExecutionStrategy.

Системная переменная по-прежнему работает, но будет удалена в будущих релизах.

Текущий приоритет свойств следующий:

  • Свойство задачи compilerExecutionStrategy имеет приоритет над системной переменной и свойством Gradle kotlin.compiler.execution.strategy.

  • Свойство Gradle имеет приоритет над системной переменной.

Существует три стратегии выполнения компилятора, которые вы можете назначить этим свойствам:

Стратегия

Где выполняется компилятор Kotlin

Инкрементальная компиляция

Другие характеристики

Демон

В собственном процессе демона

Да

По умолчанию. Может быть использован несколькими демонами Gradle

В процессе

В процессе демона Gradle

Нет

Может использовать кучу вместе с демоном Gradle

Вне процесса

В отдельном процессе для каждого вызова

Нет

—

Соответственно, доступные значения для свойств kotlin.compiler.execution.strategy (как системных, так и Gradle):

  1. daemon (по умолчанию)

  2. in-process

  3. out-of-process

Используйте свойство Gradle kotlin.compiler.execution.strategy в gradle.properties:

# gradle.properties
kotlin.compiler.execution.strategy=out-of-process

Доступные значения для свойства задачи compilerExecutionStrategy:

  1. org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy.DAEMON (по умолчанию)

  2. org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy.IN_PROCESS

  3. org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy.OUT_OF_PROCESS

Используйте свойство задачи compilerExecutionStrategy в скрипте сборки build.gradle.kts:

import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy

// ...

tasks.withType<KotlinCompile>().configureEach {
    compilerExecutionStrategy.set(KotlinCompilerExecutionStrategy.IN_PROCESS)
}

Пожалуйста, оставьте свои отзывы на этой задаче YouTrack.

Устаревание опций сборки для kapt и сопроцедур

В Kotlin 1.6.20, мы изменили уровни устаревания свойств:

  • Мы устарели возможность запуска kapt через демен Kotlin с kapt.use.worker.api – теперь это генерирует предупреждение в выходные данные Gradle. По умолчанию, kapt использует рабочие потоки Gradle с выпуска 1.3.70, и мы рекомендуем придерживаться этого метода.

    Мы собираемся удалить опцию kapt.use.worker.api в будущих выпусках.

  • Мы устарели опцию Gradle DSL kotlin.experimental.coroutines и свойство kotlin.coroutines, используемое в gradle.properties. Просто используйте функции-подвески или добавьте зависимость kotlinx.coroutines в свой файл build.gradle(.kts).

    Узнайте больше о сопроцедурах в руководстве по сопроцедурам.

Удаление опции kotlin.parallel.tasks.in.project сборки

В Kotlin 1.5.20 мы объявили устаревание опции сборки kotlin.parallel.tasks.in.project. Эта опция была удалена в Kotlin 1.6.20.

В зависимости от проекта, параллельная компиляция в демоне Kotlin может потребовать больше памяти. Для уменьшения потребления памяти, увеличьте размер кучи для демона Kotlin.

Узнайте больше о текущих поддерживаемых опциях компилятора в плагине Kotlin Gradle.

Последнее изменение: 25 мая 2023
Что нового в Kotlin 1.7.0 Что нового в Kotlin 1.6.0

© 2010–2023 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/whatsnew1620.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API