Обработка аннотаций с Kotlin

Обработчики аннотаций (см. JSR 269) поддерживаются в Kotlin с помощью плагина компилятора kapt.

Если коротко, то в проектах Kotlin можно использовать такие библиотеки, как Dagger или Data Binding .

Ниже демонстрируется применение плагина kapt к сборкам Gradle/Maven.

Использование в Gradle

Применим плагин kotlin-kapt в Gradle:

apply plugin: 'kotlin-kapt'

Или с помощью плагинов предметно-ориентированного языка:

plugins {
    id "org.jetbrains.kotlin.kapt" version "{{ site.data.releases.latest.version }}"
}

Затем добавим соответствующие зависимости, используя конфигурацию kapt в нашем блоке dependencies:

dependencies {
    kapt 'groupId:artifactId:version'
}

Если для обработчиков аннотаций ранее использовался Android support, то необходимо заменить annotationProcessor на kapt. Если наш проект содержит классы Java, kapt также позаботится и о них.

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

Аргументы обработчика аннотации

Используем блок arguments {} для передачи аргументов обработчикам аннотаций:

kapt {
    arguments {
        arg("key", "value")
    }
}

Параметры компилятора Java

Kapt использует компилятор Java для запуска обработчиков аннотаций. Так можно передавать произвольные параметры в javac:

kapt {
    javacOptions {
        // Increase the max count of errors from annotation processors.
        // Default is 100.
        option("-Xmaxerrs", 500)
    }
}

Коррекция несуществующего типа

Некоторые обработчики аннотаций (например, AutoFactory) полагаются на известные типы в объявлениях сигнатур. По умолчанию Kapt заменяет каждый неизвестный тип (включая типы для сгенерированных классов) на NonExistentClass, но это поведение можно изменить. Добавим в build.gradle дополнительный флаг для включения данного типа ошибки в заглушки:

kapt {
    correctErrorTypes = true
}

Использование в Maven

Добавим выполнение цели kapt от плагина kotlin-maven до compile:

<execution>
    <id>kapt</id>
    <goals>
        <goal>kapt</goal>
    </goals>
    <configuration>
        <sourceDirs>
            <sourceDir>src/main/kotlin</sourceDir>
            <sourceDir>src/main/java</sourceDir>
        </sourceDirs>
        <annotationProcessorPaths>
            <!-- Specify your annotation processors here. -->
            <annotationProcessorPath>
                <groupId>com.google.dagger</groupId>
                <artifactId>dagger-compiler</artifactId>
                <version>2.9</version>
            </annotationProcessorPath>
        </annotationProcessorPaths>
    </configuration>
</execution>

Полный образец проекта, показывающий использование Kotlin, Maven и Dagger, можно найти в репозитории примеров Kotlin.

Обратите внимание на то, что kapt по-прежнему не поддерживается собственной системой сборки IntelliJ IDEA. Запустите сборку с панели инструментов Maven Projects, если требуется повторный запуск обработки аннотаций.

Использование в командной строке

Плагин компилятора Kapt доступен в бинарном дистрибутиве компилятора Kotlin.

Плагин можно подключить, указав его путь к JAR-файлу с помощью параметра Xplugin kotlinc:

-Xplugin=$KOTLIN_HOME/lib/kotlin-annotation-processing.jar

Список доступных параметров:

  • sources (обязательно): Путь вывода для сгенерированных файлов.
  • classes (обязательно): Путь вывода для сгенерированных файлов и ресурсов.
  • stubs (обязательно): Путь вывода для файлов-заглушек. Другими словами, какая-то временная директория.
  • incrementalData: Путь вывода для бинарных заглушек.
  • apclasspath (повторяется): путь к обработчику JAR для аннотации. Передайте столько вариантов apclasspath, сколько у вас имеется JARов.
  • apoptions: Кодированный в base64 список параметров обработчика аннотаций. Для получения дополнительной информации см. Кодировка параметров AP / javac .
  • javacArguments: Кодированный в base64 список параметров, переданных в javac. Для получения дополнительной информации см. Кодировка параметров AP / javac .
  • processors: Список идущих через запятую имен классов обработчиков аннотаций. Если данный список определен, то kapt не будет пытаться искать обработчики аннотаций в apclasspath.
  • verbose: Включить подробный вывод.
  • aptMode (обязательно)
    • stubs – генерировать только заглушки, необходимые для обработки аннотаций;
    • apt – только обрабатывать аннотации;
    • stubsAndApt – генерировать заглушки и обрабатывать аннотации;
  • correctErrorTypes: Смотрите здесь. По умолчанию отключено.

Формат параметров плагина: -P plugin:<plugin id>:<key>=<value>. Параметры могут повторяться.

Пример:

-P plugin:org.jetbrains.kotlin.kapt3:sources=build/kapt/sources
-P plugin:org.jetbrains.kotlin.kapt3:classes=build/kapt/classes
-P plugin:org.jetbrains.kotlin.kapt3:stubs=build/kapt/stubs

-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=lib/ap.jar
-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=lib/anotherAp.jar

-P plugin:org.jetbrains.kotlin.kapt3:correctErrorTypes=true

Создание исходного кода Kotlin

Kapt может генерировать исходный код Kotlin. Просто поместим сгенерированные исходные файлы Kotlin в указанную директорию processingEnv.options["kapt.kotlin.generated"], и эти файлы будут скомпилированы вместе с основными исходниками.

Полный образец можно найти в kotlin-examples репозитория Github.

Обратите внимание на то, что Kapt не поддерживает множественные циклы для сгенерированных файлов Kotlin.

Кодировка параметров AP / javac

Параметры интерфейса командной строки apoptions и javacArguments принимают кодированные пары “ключ-значение” параметров.
Так можно кодировать параметры самостоятельно:

fun encodeList(options: Map<String, String>): String {
    val os = ByteArrayOutputStream()
    val oos = ObjectOutputStream(os)

    oos.writeInt(options.size)
    for ((key, value) in options.entries) {
        oos.writeUTF(key)
        oos.writeUTF(value)
    }

    oos.flush()
    return Base64.getEncoder().encodeToString(os.toByteArray())
}