Spec-Zone.ru › Kotlin 1.8

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

Дата выпуска: 4 апреля 2022 года

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

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

Язык

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

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

  • Определённо не-nullable типы

Прототип контекстных получателей для 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 задаче. Если у вас возникнут проблемы, пожалуйста, создайте новую задачу.

Определённо не-nullable типы

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

Для лучшей взаимозаменяемости при расширении дженериков Java-классов и интерфейсов, Kotlin 1.6.20 позволяет пометить дженерический параметр типа как определённо не-nullable на месте использования с новым синтаксисом T & Any. Синтаксическая форма происходит от записи пересекающихся типов и сейчас ограничена параметром типа с nullable верхними границами слева от & и не-nullable 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'
        }
    }
}

Узнайте больше об определённо не-nullable типах в 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 Void вместо KotlinUnit

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

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

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

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

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

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

Новый менеджер памяти Kotlin/Native находится в стадии Alpha. Он может изменяться несовместимо и в будущем потребовать ручную миграцию. Мы будем рады вашим отзывам на 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: возврат 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.

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

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

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

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

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

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

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

iOS hierarchy example

Цепочка инструментов Kotlin обеспечивает правильные зависимости по умолчанию, такие как Kotlin/Native stdlib или native библиотеки. Кроме того, инструментарий 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'
    }
}

См. полную справочную информацию по DSL для плагина Kotlin CocoaPods Gradle.

Kotlin/JS

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

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

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

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

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

  • Улучшения экспорта (как для IR, так и для старых бэкэндов)

  • @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-классам). Это ускоряет операции с символами в коде 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 и его содержимое в систему управления версиями. Добавление lock-файлов в систему управления версиями — рекомендуемая практика, так как это гарантирует, что ваше приложение будет собираться с одним и тем же деревом зависимостей на всех машинах, независимо от того, являются ли это рабочие среды на других машинах или службы CI/CD. Lock-файлы также предотвращают неявное обновление зависимостей npm при выгрузке проекта на новую машину, что является проблемой безопасности.

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

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

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'
}

Изменение имени 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 Plugin:

  • Новые свойства 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, и свойство задачи compile 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 plugin.

Последнее изменение: 10 января 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