Spec-Zone.ru › Kotlin 1.9

Инкрементальная обработка

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

Для определения, какие исходные данные являются изменёнными (те, которые нужно переобрабатывать), KSP нуждается в помощи процессоров для определения, какие входные исходные данные соответствуют каким сгенерированным выходам. Чтобы помочь с этим часто сложным и подверженным ошибкам процессом, KSP разработан таким образом, чтобы требовать только минимальный набор корневых исходных данных, которые процессоры используют в качестве стартовой точки для навигации по структуре кода. Другими словами, процессор должен связать выход с источниками соответствующего KSNode, если KSNode получен из любого из следующих:

  • Resolver.getAllFiles

  • Resolver.getSymbolsWithAnnotation

  • Resolver.getClassDeclarationByName

  • Resolver.getDeclarationsFromPackage

Инкрементальная обработка в настоящее время включена по умолчанию. Чтобы отключить её, установите свойство Gradle ksp.incremental=false. Чтобы включить логи, которые выводят набор изменённых данных в соответствии с зависимостями и выходами, используйте ksp.incremental.log=true. Вы можете найти эти файлы логов в выходном каталоге build с расширением файла .log.

В JVM изменения classpath, а также изменения Kotlin и Java исходных кодов отслеживаются по умолчанию. Чтобы отслеживать только изменения Kotlin и Java исходных кодов, отключите отслеживание classpath, установив свойство Gradle ksp.incremental.intermodule=false.

Агрегирование против изоляции

Аналогично концепциям в обработке аннотаций Gradle, KSP поддерживает режимы агрегирования и изоляции. Обратите внимание, что в отличие от обработки аннотаций Gradle, KSP классифицирует каждый вывод как агрегирующий или изолирующий, а не весь процессор.

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

Например, вывод, который собирает все символы с определённой аннотацией, считается агрегирующим выводом.

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

Например, сгенерированный класс, посвящённый интерфейсу, который он реализует, считается изолирующим.

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

Вот краткое описание для читателей, знакомых с обработкой аннотаций Java:

  • В изолирующем процессоре аннотаций Java все выходы изолированы в KSP.

  • В агрегирующем процессоре аннотаций Java некоторые выходы могут быть изолирующими, а некоторые — агрегирующими в KSP.

Как это реализовано

Зависимости рассчитываются на основе соответствия файлов входных и выходных данных, а не аннотаций. Это отношение «многие ко многим».

Правила распространения изменения состояния из-за соответствия входных и выходных данных:

  1. Если входной файл изменён, он всегда будет переобработан.

  2. Если входной файл изменён и он связан с выходом, то все другие входные файлы, связанные с тем же выходом, также будут переобработан. Это транзитивно, то есть недействительность происходит многократно до тех пор, пока не появятся новые файлы, требующие изменения.

  3. Все входные файлы, связанные с одним или несколькими агрегирующими выходами, будут переобработаны. Другими словами, если входной файл не связан ни с одним агрегирующим выходом, он не будет переобработан (если только он не соответствует 1. или 2. выше).

Причины:

  1. Если вход изменён, может быть введена новая информация, и поэтому процессорам необходимо снова запустить с этим входом.

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

  3. aggregating=true означает, что вывод потенциально может зависеть от новой информации, которая может поступать как от новых файлов, так и от изменённых существующих файлов. aggregating=false означает, что процессор уверен, что информация поступает только из определённых входных файлов и никогда из других или новых файлов.

Пример 1

Процессор генерирует outputForA после чтения класса A в A.kt и класса B в B.kt, где A расширяет B. Процессор получил A с помощью Resolver.getSymbolsWithAnnotation и затем получил B с помощью KSClassDeclaration.superTypes из A. Поскольку включение B обусловлено A, B.kt не нужно указывать в dependencies для outputForA. Вы по-прежнему можете указать B.kt в этом случае, но это не обязательно.

// A.kt
@Interesting
class A : B()

// B.kt
open class B

// Example1Processor.kt
class Example1Processor : SymbolProcessor {
    override fun process(resolver: Resolver) {
        val declA = resolver.getSymbolsWithAnnotation("Interesting").first() as KSClassDeclaration
        val declB = declA.superTypes.first().resolve().declaration
        // B.kt isn't required, because it can be deduced as a dependency by KSP
        val dependencies = Dependencies(aggregating = true, declA.containingFile!!)
        // outputForA.kt
        val outputName = "outputFor${declA.simpleName.asString()}"
        // outputForA depends on A.kt and B.kt
        val output = codeGenerator.createNewFile(dependencies, "com.example", outputName, "kt")
        output.write("// $declA : $declB\n".toByteArray())
        output.close()
    }
    // ...
}

Пример 2

Предположим, что процессор генерирует outputA после чтения sourceA и outputB после чтения sourceB.

Когда sourceA изменится:

  • Если outputB агрегируется, sourceA и sourceB переобрабатываются.

  • Если outputB изолируется, перерабатывается только sourceA.

Когда sourceC добавлено:

  • Если outputB агрегируется, sourceC и sourceB переобрабатываются.

  • Если outputB изолируется, перерабатывается только sourceC.

Когда sourceA удалено, ничего не нужно перерабатывать.

Когда sourceB удалено, ничего не нужно перерабатывать.

Как определяется изменение состояния файлов

Изменённый файл либо напрямую изменён пользователем, либо косвенно затронут другими изменёнными файлами. KSP распространяет изменение состояния в двух этапах:

  • Распространение с помощью отслеживания разрешения: Разрешение ссылки на тип (неявное или явное) — единственный способ перехода от одного файла к другому. Когда процессор разрешает ссылку на тип, изменённый или затронутый файл, содержащий изменение, которое потенциально может повлиять на результат разрешения, повлияет на файл, содержащий эту ссылку.

  • Распространение с помощью соответствия входных и выходных данных: Если исходный файл изменён или затронут, все другие исходные файлы, имеющие общие выходы с этим файлом, также затронуты.

Обратите внимание, что оба они транзитивны, а второй формирует классы эквивалентности.

Отчёт об ошибках

Для отчёта об ошибке установите свойства Gradle ksp.incremental=true и ksp.incremental.log=true, и выполните чистую сборку. Эта сборка генерирует два файла логов:

  • build/kspCaches/<source set>/logs/kspDirtySet.log

  • build/kspCaches/<source set>/logs/kspSourceToOutputs.log

Затем вы можете выполнить последовательные инкрементальные сборки, которые сгенерируют два дополнительных файла логов:

  • build/kspCaches/<source set>/logs/kspDirtySetByDeps.log

  • build/kspCaches/<source set>/logs/kspDirtySetByOutputs.log

Эти логи содержат имена файлов источников и выходов, а также отметки времени сборок.

Последнее изменение: 12 июля 2023 г.
Ссылка на обработку аннотаций Java в KSP Обработка за несколько раундов

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

Spec-Zone.ru

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