12.2.3 kapt 编译器插件

原文链接: https://kotlinlang.org/docs/kapt.html

12.2.3 kapt 编译器插件

  • 如果你符合以下情况,请使用 kapt:* 你有一个 Maven 项目。* 你有一个 Gradle 项目,但所需的 Java 注解处理器尚不支持 KSP。查看受支持的库列表。* 如果你符合以下情况,请使用 KSP:* 你有一个 Gradle 项目,且所需的 Java 注解处理器支持 KSP。* 你想创建自己的注解处理器。

kapt 编译器插件让你可以在 Kotlin 中使用现有的 Java 注解处理器,并同时支持 Maven 和 Gradle。它会根据 Kotlin 源码生成桩文件,然后在这些桩文件上运行 Java 注解处理器。

这使你可以在 Kotlin 项目中对 MapStruct 和 Data Binding 等库使用基于 Java 的注解处理。

警告: IntelliJ 构建系统不支持 kapt。要在 IntelliJ IDEA 中重新运行注解处理,请从 Maven 工具窗口启动构建。

设置插件

你可以为 Gradle、Maven 配置 kapt 插件,也可以从命令行使用它。

Gradle

要在 Gradle 中使用 kapt,请按以下步骤操作:

  1. 在构建脚本文件 build.gradle(.kts) 中应用 kapt Gradle 插件:

Kotlin

1
2
3
   plugins {
       kotlin("kapt") version "2.4.20"
   }

Groovy

1
2
3
   plugins {
       id "org.jetbrains.kotlin.kapt" version "2.4.20"
   }
  1. 在 dependencies {} 块中使用 kapt 配置添加相应的依赖:

Kotlin

1
2
3
   dependencies {
       kapt("groupId:artifactId:version")
   }

Groovy

1
2
3
   dependencies {
       kapt 'groupId:artifactId:version'
   }
  1. 如果你之前使用 Android 的注解处理器支持,请把 annotationProcessor 配置的用法替换为 kapt。如果你的项目包含 Java 类,kapt 插件也会处理它们。

如果你为 androidTest 或 test 源码使用注解处理器,相应的 kapt 配置名为 kaptAndroidTest 和 kaptTest。请注意,kaptAndroidTest 和 kaptTest 都扩展自 kapt,因此你只需提供 kapt 依赖,它就会同时用于生产源码和测试。

Maven

你可以使用 <extensions> 选项来简化 kapt 的设置,也可以手动设置,以完全掌控 kapt 的执行。

自动配置

你可以通过为 Kotlin Maven 插件启用 <extensions> 选项来简化 kapt 配置。在这种情况下,你无需手动设置包含目标或源目录的 kapt <execution> 部分。

要自动配置 kapt,请在 pom.xml 构建文件中把 kotlin-maven-plugin 的 <extensions> 选项设置为 true:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<plugin>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-maven-plugin</artifactId>
    <version>${kotlin.version}</version>
    <extensions>true</extensions>
    <configuration>
        <annotationProcessorPaths>

            <annotationProcessorPath>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>1.6.3</version>
            </annotationProcessorPath>
        </annotationProcessorPaths>
    </configuration>
</plugin>

关于 <extensions> 选项的更多信息,请参阅自动配置。

手动配置

要在 Kotlin Maven 项目中手动设置 kapt,请在 compile 执行之前添加 kotlin-maven-plugin 的 kapt 目标执行:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<execution>
    <id>kapt</id>
    <goals>
        <goal>kapt</goal>
    </goals>
    <configuration>
        <sourceDirs>
            <sourceDir>src/main/kotlin</sourceDir>
            <sourceDir>src/main/java</sourceDir>
        </sourceDirs>
        <annotationProcessorPaths>

            <annotationProcessorPath>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>1.6.3</version>
            </annotationProcessorPath>
        </annotationProcessorPaths>
    </configuration>
</execution>

要配置注解处理模式,请在 <configuration> 块中设置 aptMode 选项。例如:

1
2
3
4
<configuration>
   ...
   <aptMode>stubs</aptMode>
</configuration>

CLI

kapt 作为独立的 CLI 工具包含在 Kotlin 编译器的二进制发行版中。

要从命令行运行 kapt,请使用:

1
kapt <options> <source files>

例如:

1
2
3
4
5
6
7
kapt -Kapt-mode=stubsAndApt \
  -Kapt-sources=build/kapt/sources \
  -Kapt-classes=build/kapt/classes \
  -Kapt-stubs=build/kapt/stubs \
  -Kapt-classpath=lib/ap.jar \
  -Kapt-classpath=lib/anotherAp.jar \
  src/main/kotlin

配置注解处理器

kapt 提供了若干选项来控制注解处理器如何被发现、组织和执行,包括管理处理器 classpath、从共享配置继承处理器,以及保持 javac 专用处理器处于活动状态。

关于更多配置选项,例如向注解处理器和 javac 传递选项,请参阅注解处理器配置。

配置处理器 classpath 与发现

你可以禁止发现未包含在 kapt 处理器路径中的注解处理器。这样就能把不必要的注解处理器从编译 classpath 中排除。

Gradle

Gradle 使用跳过编译在项目重新构建时跳过注解处理,从而改善 kapt 的增量构建时间。具体来说,以下情况会跳过注解处理:

  • 项目的源文件没有变化。
  • 依赖中的变化是 ABI 兼容的。例如,只有函数体发生变化。

然而,对于在编译 classpath 上发现的注解处理器,无法使用跳过编译,因为它们内部实现的变化需要运行注解处理任务,即使这些处理器的 ABI 没有变化。

因此我们不建议使用来自编译 classpath 的注解处理器。要把这些处理器从 kapt 处理中排除,请在 gradle.properties 文件中添加 kapt.include.compile.classpath 属性:

# gradle.properties
kapt.include.compile.classpath=false

该选项设置为 false 后,未包含在处理器路径(kapt* 配置)中的注解处理器依赖会被排除在 kapt 处理之外。

Maven

要排除未包含在 kapt 处理器路径中的注解处理器,请在 kapt 插件的 <execution> 部分中把 includeCompileClasspath 选项设置为 false:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<execution>
    <id>kapt</id>
    <goals>
        <goal>kapt</goal>
    </goals>
    <configuration>
        <includeCompileClasspath>false</includeCompileClasspath>
        <sourceDirs>...</sourceDirs>
        <annotationProcessorPaths>...</annotationProcessorPaths>
    </configuration>
</execution>

或者,你也可以在 pom.xml 的 <properties> 部分使用 kapt.include.compile.classpath 属性:

1
2
3
<properties>
    <kapt.include.compile.classpath>false</kapt.include.compile.classpath>
</properties>

该选项设置为 false 后,未包含在 <annotationProcessorPaths> 部分中的注解处理器会被排除在 kapt 处理之外。

如果没有设置 includeCompileClasspath 选项,而 kapt 在编译 classpath 上检测到未在处理器路径中显式定义的注解处理器,你会看到一条弃用警告:

[WARNING] Annotation processors discovery from compile classpath is deprecated.
Set 'kapt.include.compile.classpath=false' to disable discovery.

提示: 要查看未包含在 kapt classpath 中的注解处理器列表,请使用 --info 日志级别选项运行构建。

从父配置继承注解处理器

你可以在一个单独的 Gradle 配置中定义一整套公共注解处理器作为父配置,并在各子项目中专用于 kapt 的配置里进一步扩展它。

例如,对于使用 MapStruct 的子项目,在你的 build.gradle(.kts) 文件中使用以下配置:

1
2
3
4
5
6
7
val commonAnnotationProcessors by configurations.creating
configurations.named("kapt") { extendsFrom(commonAnnotationProcessors) }

dependencies {
    implementation("org.mapstruct:mapstruct:1.6.3")
    commonAnnotationProcessors("org.mapstruct:mapstruct-processor:1.6.3")
}

在这个例子中,commonAnnotationProcessors Gradle 配置是你希望用于所有项目的注解处理公共父配置。你使用 extendsFrom() 方法把 commonAnnotationProcessors 添加为父配置。kapt 看到 commonAnnotationProcessors Gradle 配置中有对 MapStruct 注解处理器的依赖,因此会把该处理器包含进自己的注解处理配置中。

保留 Java 编译器的注解处理器

默认情况下,kapt 会运行所有注解处理器,并禁用 javac 的注解处理。不过,你可能需要 javac 来运行某些注解处理器,例如 Lombok。

在 Gradle 构建文件中,使用 keepJavacAnnotationProcessors 选项:

1
2
3
kapt {
    keepJavacAnnotationProcessors = true
}

如果你使用 Maven,请显式配置插件。请参阅这个设置 Lombok 编译器插件的示例。

优化 kapt 构建

kapt 提供了若干 Gradle 特有的策略来减少注解处理时间,包括并行运行任务、利用构建缓存、缓存处理器类加载器以及使用增量注解处理。

关于影响构建行为的更多选项,例如错误类型修正、桩元数据剥离以及编译 classpath 扫描,请参阅行为选项。

并行运行 kapt 任务

kapt 使用 Gradle Worker API来运行注解处理任务。使用 Worker API 让 Gradle 可以并行运行单个项目中的独立注解处理任务,在某些情况下能显著缩短执行时间。

如果你在 Kotlin Gradle 插件中设置了自定义 JDK 版本,kapt 任务工作进程只会使用 processIsolation() 模式。

如果你想为 kapt 工作进程提供额外的 JVM 参数,请使用 KaptWithoutKotlincTask 的输入 kaptProcessJvmArgs:

Kotlin

1
2
3
4
tasks.withType<org.jetbrains.kotlin.gradle.internal.KaptWithoutKotlincTask>()
    .configureEach {
        kaptProcessJvmArgs.add("-Xmx512m")
    }

Groovy

1
2
3
4
tasks.withType(org.jetbrains.kotlin.gradle.internal.KaptWithoutKotlincTask.class)
    .configureEach {
        kaptProcessJvmArgs.add('-Xmx512m')
    }

安全地使用 Gradle 构建缓存

Gradle 会默认缓存 kapt 注解处理任务。然而,注解处理器可以运行任意代码。这可能导致把任务输入不必要地转换为输出,或者访问和修改 Gradle 无法跟踪的文件。

当构建中使用的注解处理器无法被正确缓存时,你可以禁用缓存,以避免 kapt 任务出现误命中。为此,请在构建脚本中使用 useBuildCache 属性:

1
2
3
kapt {
    useBuildCache = false
}

缓存注解处理器的类加载器

实验性 - 通用

如果你连续运行许多 Gradle 任务,缓存注解处理器的类加载器可以帮助 kapt 运行得更快。

要启用该特性,请在 gradle.properties 文件中使用以下属性:

# gradle.properties
#
# Any positive value enables caching
# Use the same value as the number of modules that use kapt
kapt.classloaders.cache.size=5

# Disable for caching to work
kapt.include.compile.classpath=false

如果你在注解处理器缓存方面遇到任何问题,请为它们禁用缓存:

# Specify annotation processors' full names to disable caching for them
kapt.classloaders.cache.disableForProcessors=[annotation processors full names]

注意: 如果你在使用该特性时遇到任何问题,我们非常欢迎你在 YouTrack 上提供反馈。

使用增量注解处理

在 Gradle 中,kapt 默认支持增量注解处理,因此只会重新处理发生变化的文件。

目前,增量注解处理仅在以下情况有效:

  • 增量编译已启用。
  • 构建中的所有注解处理器都是增量的。

要禁用增量注解处理,请在 gradle.properties 文件中添加以下行:

kapt.incremental.apt=false

注意: 目前,kapt 的增量注解处理在 Maven 或 CLI 中不受支持。

分析性能

kapt 提供了内置的诊断功能,帮助你了解注解处理的性能,包括每个处理器的执行时间报告和生成文件数量,以便识别未使用的处理器。

关于更多诊断选项,例如用于调试增量处理的文件读取历史和内存泄漏检测,请参阅诊断与统计选项。

测量注解处理器的性能

要获取注解处理器执行的性能统计信息,请使用 showProcessorStats 选项。示例输出如下:

1
2
3
Kapt Annotation Processing performance report:
com.example.processor.TestingProcessor: total: 133 ms, init: 36 ms, 2 round(s): 97 ms, 0 ms
com.example.processor.AnotherProcessor: total: 100 ms, init: 6 ms, 1 round(s): 93 ms

你可以使用 dumpProcessorStats 选项把该报告导出到文件。例如,下面的 CLI 命令运行 kapt 并把统计信息导出到 ap-perf-report.file 文件:

1
2
3
4
kapt -Kapt-mode=stubsAndApt \
  -Kapt-classpath=processor/build/libs/processor.jar \
  -Kapt-dump-processor-stats=ap-perf-report.file \
  sample/src/main/

跟踪生成文件的数量

kapt 插件可以报告每个注解处理器生成文件数量的统计信息。

这有助于跟踪构建中是否包含未使用的注解处理器。你可以利用生成的报告找出触发不必要注解处理器的模块,并更新这些模块以避免出现这种情况。

要启用统计信息报告:

  1. 在你的 Gradle 构建文件中,把 showProcessorStats 选项设置为 true:
1
2
3
4
   // build.gradle(.kts)
   kapt {
       showProcessorStats = true
   }
  1. 在你的 gradle.properties 文件中,把 verbose 编译器选项设置为 true:
   # gradle.properties
   kapt.verbose=true

统计信息会以 info 级别出现在日志中。你可以看到 Annotation processor stats: 行,其后是每个注解处理器执行时间的统计信息。在这些行之后是 Generated files report: 行,其后是每个注解处理器生成文件数量的统计信息。例如:

1
2
3
4
[INFO] Annotation processor stats:
[INFO] org.mapstruct.ap.MappingProcessor: total: 290 ms, init: 1 ms, 3 round(s): 289 ms, 0 ms, 0 ms
[INFO] Generated files report:
[INFO] org.mapstruct.ap.MappingProcessor: total sources: 2, sources per round: 2, 0, 0

注意: 目前,使用 showProcessorStats 和 verbose 编译器选项跟踪生成文件数量在 Maven 或 CLI 中不受支持。

生成 Kotlin 源码

kapt 可以生成 Kotlin 源码。为此,请使用 processingEnv.options["kapt.kotlin.generated"] 把生成的 Kotlin 源文件写入指定目录。随后这些 Kotlin 源文件会与主源码一起编译。

注意: kapt 不支持对生成的 Kotlin 文件进行多轮注解处理。

编译器选项

注解处理器配置

选项说明如何设置
aptMode控制 kapt 工作流各阶段的执行: stubsAndApt 生成桩并运行注解处理(默认) stubs 只从 Kotlin 生成 Java 桩 apt 只运行注解处理器(假定桩已存在)Gradle: 不能直接使用;Gradle 把生成桩和 apt 作为单独任务运行 Maven: stubsAndApt ]]> CLI: -Kapt-mode=stubsAndApt
classpath用于发现注解处理器的 classpath 条目。Gradle: dependencies { kapt(“com.example:processor:1.0”) } Maven: ... ]]> CLI: -Kapt-classpath=lib/my-processor.jar
processors要运行的处理器完全限定类名,以逗号分隔,绕过自动发现。Gradle: kapt { annotationProcessor(“com.example.MyProcessor”) } Maven: com.example.MyProcessor ]]> CLI: -Kapt-processors=com.example.MyProcessor
apOption传递给注解处理器的键值选项。Gradle: kapt { arguments { arg(“room.schemaLocation”, “$projectDir/schemas”) } } Maven: room.schemaLocation=/schemas ]]> CLI: -Kapt-options:room.schemaLocation=/schemas
javacOption传递给 Java 编译器的键值选项。Gradle: kapt { javacOptions { option("-source", “11”) } } Maven: -source=11 ]]> CLI: -Kapt-javac-option:-source=11
processIncrementally启用增量注解处理;只会重新处理受变更影响的文件。Gradle: # gradle.properties kapt.incremental.apt=true Maven: 目前不支持 CLI: 目前不支持

输出目录选项

选项说明如何设置
sources注解处理器生成 .java 源文件的目录。Gradle: 自动设置为 build/generated/source/kapt/main Maven: 自动设置为 target/generated-sources/kapt/ CLI: -Kapt-sources=build/kapt/sources
classes由生成的源文件编译出的 .class 文件的目录。Gradle: 自动管理 Maven: 自动管理 CLI: -Kapt-classes=build/kapt/classes
stubs由 Kotlin 源码生成的 Java 桩文件目录,用作注解处理器的输入。Gradle: 自动管理 Maven: 自动管理 CLI: -Kapt-stubs=build/kapt/stubs
incrementalData存储增量构建的状态。Gradle: 自动管理 Maven: 目前不支持 CLI: 目前不支持

行为选项

选项说明如何设置
correctErrorTypes默认情况下,kapt 会把每个未知类型(包括生成的类的类型)替换为 NonExistentClass。 你可以启用桩中的错误类型介入,把无法解析的错误类型替换为来自生成源的类型。 默认为 falseGradle: kapt { correctErrorTypes = true } Maven: true ]]> CLI: -Kapt-correct-error-types=true
dumpDefaultParameterValues在生成的桩中把默认参数初始化器作为字段值包含进来。 默认为 falseGradle: kapt { dumpDefaultParameterValues = true } Maven: 不可用 CLI: -Kapt-dump-default-parameter-values=true
mapDiagnosticLocations把桩文件中的错误消息映射回它们原本的 Kotlin 源码位置。 默认为 falseGradle: kapt { mapDiagnosticLocations = true } Maven: true ]]> CLI: -Kapt-map-diagnostic-locations=true
strict把桩生成中的不兼容情况从警告变为错误。 默认为 falseGradle: kapt { strictMode = true } Maven: 不可用 CLI: -Kapt-strict=true
stripMetadata从生成的桩中移除 @kotlin.Metadata 注解,从而减小桩体积,并向处理器隐藏 Kotlin 特有的信息。 默认为 falseGradle: kapt { stripMetadata = true } Maven: 不可用 CLI: -Kapt-strip-metadata=true
verbose启用 kapt 的详细日志。 默认为 falseGradle: # gradle.properties kapt.verbose=true Maven: 目前不支持 CLI: 目前不支持
infoAsWarnings把 info 级别的 kapt 消息提升为警告。 默认为 falseGradle: 不能直接使用 Maven: 目前不支持 CLI: 目前不支持
includeCompileClasspath在编译 classpath 中扫描注解处理器。为实现可复现性请设置为 false。 默认为 trueGradle: kapt { includeCompileClasspath = false } Maven: false ]]> CLI: 目前不支持

诊断与统计选项

选项说明如何设置
showProcessorStats把每个处理器的执行时间打印到标准输出。Gradle: kapt { showProcessorStats = true } Maven: 不可用 CLI: -Kapt-show-processor-stats=true
dumpProcessorStats把处理器耗时统计写入文件。Gradle: 不可用 Maven: 不可用 CLI: -Kapt-dump-processor-stats=build/kapt-stats.txt
dumpFileReadHistory把处理器读取过的文件列表写入文件,便于调试增量注解处理器。Gradle: 不可用 Maven: 不可用 CLI: -Kapt-dump-file-read-history=build/kapt-reads.txt
detectMemoryLeaks内存泄漏检测模式:none、default 或 paranoid。Gradle: kapt { detectMemoryLeaks = “paranoid” } Maven: 目前不支持 CLI: 目前不支持

接下来做什么?