Spec-Zone.ru › Kotlin 1.7

Что нового в 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. Синтаксическая форма происходит от записи пересечения типов и теперь ограничена параметром типа с 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'
        }
    }
}

Узнайте больше об однозначно не-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 interop, см. документацию по взаимосвязи взаимодействия и эту статью блога.

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

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

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

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

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

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

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: возврат 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: возврат 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: в 0 example.kexe 0x227190 kfun:kotlin.Throwable#<init>(kotlin.String?){} + 96 в 1 example.kexe 0x221e4c kfun:kotlin.Exception#<init>(kotlin.String?){} + 92 в 2 example.kexe 0x221f4c kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 92 в 3 example.kexe 0x22234c kfun:kotlin.IllegalStateException#<init>(kotlin.String?){} + 92 в 4 example.kexe 0x25d708 kfun:#bar(){} + 104 в 5 example.kexe 0x25d68c kfun:#main(){} + 12
  • 1.6.20 с libbacktrace:

Неперехваченное исключение Kotlin: kotlin.IllegalStateException: в 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) в 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) в 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) в 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) в 4 example.kexe 0x25fac8 kfun:#bar(){} + 104 [inlined] (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/libraries/stdlib/src/kotlin/util/Preconditions.kt:143:56) в 5 example.kexe 0x25fac8 kfun:#bar(){} + 104 [inlined] (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:4:5) в 6 example.kexe 0x25fac8 kfun:#bar(){} + 104 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:2:13) в 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: в 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) в 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) в 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) в 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) в 4 example.kexe 0x10a8489a5 kfun:#bar(){} + 117 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:2:1) в 5 example.kexe 0x10a84891c kfun:#main(){} + 12 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:1:14) ...
  • 1.6.20 с libbacktrace:

Неперехваченное исключение Kotlin: kotlin.IllegalStateException: в 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) в 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) в 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) в 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) в 4 example.kexe 0x106689d35 kfun:#bar(){} + 117 [inlined] (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/libraries/stdlib/src/kotlin/util/Preconditions.kt:143:56) >> в 5 example.kexe 0x106689d35 kfun:#bar(){} + 117 [inlined] (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:4:5) в 6 example.kexe 0x106689d35 kfun:#bar(){} + 117 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:2:13) в 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 в 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 и доступ к специфичным для iOS зависимостям, например Foundation.

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

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

iOS hierarchy example

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

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

При публикации многоплатформенной библиотеки 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 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.tasks.KotlinCompile::class).configureEach {
    // $base is a base path of source files
    kotlinOptions.freeCompilerArgs += "-Xklib-relative-path-base=$base"
}
tasks.withType(org.jetbrains.kotlin.gradle.tasks.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, для определения стратегии выполнения компилятора Kotlin использовалась системная переменная -Dkotlin.compiler.execution.strategy. Эта переменная могла быть неудобной в некоторых случаях. 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's) являются:

  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 buildscript:

import org.jetbrains.kotlin.gradle.tasks.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 плагине.

Последнее изменение: 17 июня 2022
Что нового в Kotlin 1.7.0 Что нового в Kotlin 1.6.0

© 2010–2022 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