Spec-Zone.ru › Kotlin 2

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

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

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

ksp.incremental=false

Изменённые файлы

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

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

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

Корневой исходный файл — это файл, символы которого непосредственно получены с помощью любого из следующих методов:

  • Resolver.getAllFiles()

  • Resolver.getSymbolsWithAnnotation()

  • Resolver.getClassDeclarationByName()

  • Resolver.getDeclarationsFromPackage()

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

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

Используйте интерфейс CodeGenerator для создания выходных файлов и связывания входных данных с выходными. Дополнительные сведения см. в разделе CodeGenerator.kt в исходном коде.

Агрегирующие и изолирующие выходные данные

KSP делит сгенерированные выходные данные на два типа: агрегирующие и изолирующие.

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

Агрегирующие

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

Любое изменение входных данных приводит к пересборке всех агрегирующих выходных данных и повторной обработке всех соответствующих зарегистрированных, новых или изменённых исходных файлов.

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

Изолирующие

Изолирующие выходные данные зависят только от указанных для них исходных файлов.

Изменения в других исходных файлах не влияют на эти выходные данные. С одним выходным файлом можно связать несколько исходных файлов.

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

Распространение изменений

KSP распространяет изменения следующими способами:

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

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

Правила (1) и (2) могут многократно запускать друг друга. Например, правило (1) может запустить правило (2), которое затем снова запустит правило (1).

Реализация

Зависимости определяются отношением «многие ко многим» между входными и выходными файлами.

Вот как KSP определяет, какие файлы нужно обработать повторно:

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

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

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

    Почему? Выходной файл создаётся на основе набора входных файлов. Процессорам могут понадобиться все входные файлы, чтобы создать выходной файл заново.

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

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

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

.
├── src
│   ├── sourceA.kt
│   └── sourceB.kt
└── generated
   ├── outputA
   └── outputB

Процессор:

  1. читает sourceA.

  2. создаёт outputA.

  3. читает sourceB.

  4. создаёт outputB.

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

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

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

Если добавляется sourceC:

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

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

Если удаляется sourceA или sourceB, KSP не нужно повторно обрабатывать какие-либо файлы.

Пример процессора

В следующем проекте содержатся классы A и B, где A расширяет B:

// 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()
   }
   // ...
}

Чтобы создать outputForA, процессор:

  1. получает A, вызывая Resolver.getSymbolsWithAnnotation.

  2. получает B, вызывая KSClassDeclaration.superTypes для A.

KSP отслеживает эту связь с помощью отслеживания разрешения типов и автоматически регистрирует B как зависимость A. Поэтому вам не нужно явно объявлять B.kt зависимостью outputForA.

Сообщение об ошибках

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

  1. Включите ведение журналов инкрементальной обработки, добавив следующую строку в gradle.properties:

    ksp.incremental.log=true
    
  2. Выполните чистую сборку, которая завершится успешно.

  3. Сохраните созданные файлы журналов, скопировав их в другое место:

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

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

  4. Измените исходный файл, который вызывает проблему, и снова запустите сборку.

  5. Приложите к задаче на GitHub файлы журналов как успешной сборки, так и сборки, при которой возникает проблема.

Визуализация графа зависимостей символов

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

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

ksp.incremental.log=true
ksp.incremental.log.graph.origin=<fully-qualified-name>

Если вы используете KSP из командной строки, добавьте следующие параметры:

-incremental-log=true -incremental-log.graph.origin=<fully-qualified-name>

Файл DOT создаётся в каталоге logs.

21 июля 2026
Справочник по переходу с обработки аннотаций Java на KSPМногоэтапная обработка

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