Explicit opt-in switch for Kapt caching.

This commit is contained in:
Sergey Igushkin
2017-11-30 20:50:44 +03:00
parent 7ee3b81939
commit 57f710c931
3 changed files with 531 additions and 511 deletions
@@ -1,394 +1,403 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * Copyright 2010-2016 JetBrains s.r.o.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.gradle.internal package org.jetbrains.kotlin.gradle.internal
import com.android.build.gradle.BaseExtension import com.android.build.gradle.BaseExtension
import com.android.build.gradle.api.AndroidSourceSet import com.android.build.gradle.api.AndroidSourceSet
import com.android.builder.model.SourceProvider import com.android.builder.model.SourceProvider
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.Dependency
import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.TaskDependency import org.gradle.api.tasks.TaskDependency
import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.tasks.compile.JavaCompile import org.gradle.api.tasks.compile.JavaCompile
import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import java.io.File import java.io.File
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.getKaptGeneratedSourcesDir import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.getKaptGeneratedSourcesDir
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.getKaptGeneratedClassesDir import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.getKaptGeneratedClassesDir
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.getKaptGeneratedKotlinSourcesDir import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.getKaptGeneratedKotlinSourcesDir
import org.jetbrains.kotlin.gradle.tasks.useBuildCacheIfSupported import org.jetbrains.kotlin.gradle.tasks.shouldEnableGradleCache
import java.io.ByteArrayOutputStream import org.jetbrains.kotlin.gradle.tasks.useBuildCacheIfSupported
import java.io.ObjectOutputStream import java.io.ByteArrayOutputStream
import java.util.* import java.io.ObjectOutputStream
import java.util.*
// apply plugin: 'kotlin-kapt'
class Kapt3GradleSubplugin : Plugin<Project> { // apply plugin: 'kotlin-kapt'
companion object { class Kapt3GradleSubplugin : Plugin<Project> {
fun isEnabled(project: Project) = project.plugins.findPlugin(Kapt3GradleSubplugin::class.java) != null companion object {
fun isEnabled(project: Project) = project.plugins.findPlugin(Kapt3GradleSubplugin::class.java) != null
@JvmStatic
fun getKaptGeneratedClassesDir(project: Project, sourceSetName: String) = @JvmStatic
File(project.project.buildDir, "tmp/kapt3/classes/$sourceSetName") fun getKaptGeneratedClassesDir(project: Project, sourceSetName: String) =
File(project.project.buildDir, "tmp/kapt3/classes/$sourceSetName")
@JvmStatic
fun getKaptGeneratedSourcesDir(project: Project, sourceSetName: String) = @JvmStatic
File(project.project.buildDir, "generated/source/kapt/$sourceSetName") fun getKaptGeneratedSourcesDir(project: Project, sourceSetName: String) =
File(project.project.buildDir, "generated/source/kapt/$sourceSetName")
@JvmStatic
fun getKaptGeneratedKotlinSourcesDir(project: Project, sourceSetName: String) = @JvmStatic
File(project.project.buildDir, "generated/source/kaptKotlin/$sourceSetName") fun getKaptGeneratedKotlinSourcesDir(project: Project, sourceSetName: String) =
} File(project.project.buildDir, "generated/source/kaptKotlin/$sourceSetName")
}
override fun apply(project: Project) {}
} override fun apply(project: Project) {}
}
abstract class KaptVariantData<T>(val variantData: T) {
abstract val name: String abstract class KaptVariantData<T>(val variantData: T) {
abstract val sourceProviders: Iterable<SourceProvider> abstract val name: String
abstract fun addJavaSourceFoldersToModel(generatedFilesDir: File) abstract val sourceProviders: Iterable<SourceProvider>
abstract val annotationProcessorOptions: Map<String, String>? abstract fun addJavaSourceFoldersToModel(generatedFilesDir: File)
abstract fun registerGeneratedJavaSource( abstract val annotationProcessorOptions: Map<String, String>?
project: Project, abstract fun registerGeneratedJavaSource(
kaptTask: KaptTask, project: Project,
javaTask: AbstractCompile) kaptTask: KaptTask,
} javaTask: AbstractCompile)
}
// Subplugin for the Kotlin Gradle plugin
class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> { // Subplugin for the Kotlin Gradle plugin
companion object { class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
private val VERBOSE_OPTION_NAME = "kapt.verbose" companion object {
private val VERBOSE_OPTION_NAME = "kapt.verbose"
val MAIN_KAPT_CONFIGURATION_NAME = "kapt"
val MAIN_KAPT_CONFIGURATION_NAME = "kapt"
val KAPT_GROUP_NAME = "org.jetbrains.kotlin"
val KAPT_ARTIFACT_NAME = "kotlin-annotation-processing-gradle" val KAPT_GROUP_NAME = "org.jetbrains.kotlin"
val KAPT_ARTIFACT_NAME = "kotlin-annotation-processing-gradle"
fun getKaptConfigurationName(sourceSetName: String): String {
return if (sourceSetName != "main") fun getKaptConfigurationName(sourceSetName: String): String {
"$MAIN_KAPT_CONFIGURATION_NAME${sourceSetName.capitalize()}" return if (sourceSetName != "main")
else "$MAIN_KAPT_CONFIGURATION_NAME${sourceSetName.capitalize()}"
MAIN_KAPT_CONFIGURATION_NAME else
} MAIN_KAPT_CONFIGURATION_NAME
}
fun Project.findKaptConfiguration(sourceSetName: String): Configuration? {
return project.configurations.findByName(getKaptConfigurationName(sourceSetName)) fun Project.findKaptConfiguration(sourceSetName: String): Configuration? {
} return project.configurations.findByName(getKaptConfigurationName(sourceSetName))
}
fun findMainKaptConfiguration(project: Project) = project.findKaptConfiguration(MAIN_KAPT_CONFIGURATION_NAME)
} fun findMainKaptConfiguration(project: Project) = project.findKaptConfiguration(MAIN_KAPT_CONFIGURATION_NAME)
}
private val kotlinToKaptGenerateStubsTasksMap = mutableMapOf<KotlinCompile, KaptGenerateStubsTask>()
private val kotlinToKaptGenerateStubsTasksMap = mutableMapOf<KotlinCompile, KaptGenerateStubsTask>()
override fun isApplicable(project: Project, task: AbstractCompile) = task is KotlinCompile && Kapt3GradleSubplugin.isEnabled(project)
override fun isApplicable(project: Project, task: AbstractCompile) = task is KotlinCompile && Kapt3GradleSubplugin.isEnabled(project)
private fun Kapt3SubpluginContext.getKaptStubsDir() = createAndReturnTemporaryKaptDirectory("stubs")
private fun Kapt3SubpluginContext.getKaptStubsDir() = createAndReturnTemporaryKaptDirectory("stubs")
private fun Kapt3SubpluginContext.getKaptIncrementalDataDir() = createAndReturnTemporaryKaptDirectory("incrementalData")
private fun Kapt3SubpluginContext.getKaptIncrementalDataDir() = createAndReturnTemporaryKaptDirectory("incrementalData")
private fun Kapt3SubpluginContext.createAndReturnTemporaryKaptDirectory(name: String): File {
val dir = File(project.buildDir, "tmp/kapt3/$name/$sourceSetName") private fun Kapt3SubpluginContext.createAndReturnTemporaryKaptDirectory(name: String): File {
dir.mkdirs() val dir = File(project.buildDir, "tmp/kapt3/$name/$sourceSetName")
return dir dir.mkdirs()
} return dir
}
private inner class Kapt3SubpluginContext(
val project: Project, private inner class Kapt3SubpluginContext(
val kotlinCompile: KotlinCompile, val project: Project,
val javaCompile: AbstractCompile, val kotlinCompile: KotlinCompile,
val kaptVariantData: KaptVariantData<*>?, val javaCompile: AbstractCompile,
val sourceSetName: String, val kaptVariantData: KaptVariantData<*>?,
val javaSourceSet: SourceSet?, val sourceSetName: String,
val kaptExtension: KaptExtension, val javaSourceSet: SourceSet?,
val kaptClasspath: MutableList<File>) { val kaptExtension: KaptExtension,
val sourcesOutputDir = getKaptGeneratedSourcesDir(project, sourceSetName) val kaptClasspath: MutableList<File>) {
val kotlinSourcesOutputDir = getKaptGeneratedKotlinSourcesDir(project, sourceSetName) val sourcesOutputDir = getKaptGeneratedSourcesDir(project, sourceSetName)
val classesOutputDir = getKaptGeneratedClassesDir(project, sourceSetName) val kotlinSourcesOutputDir = getKaptGeneratedKotlinSourcesDir(project, sourceSetName)
val classesOutputDir = getKaptGeneratedClassesDir(project, sourceSetName)
val kaptClasspathArtifacts = project
.resolveSubpluginArtifacts(listOf(this@Kapt3KotlinGradleSubplugin)) val kaptClasspathArtifacts = project
.flatMap { it.value } .resolveSubpluginArtifacts(listOf(this@Kapt3KotlinGradleSubplugin))
} .flatMap { it.value }
}
override fun apply(
project: Project, override fun apply(
kotlinCompile: KotlinCompile, project: Project,
javaCompile: AbstractCompile, kotlinCompile: KotlinCompile,
variantData: Any?, javaCompile: AbstractCompile,
androidProjectHandler: Any?, variantData: Any?,
javaSourceSet: SourceSet? androidProjectHandler: Any?,
): List<SubpluginOption> { javaSourceSet: SourceSet?
assert((variantData != null) xor (javaSourceSet != null)) ): List<SubpluginOption> {
assert((variantData != null) xor (javaSourceSet != null))
val kaptClasspath = arrayListOf<File>()
val buildDependencies = arrayListOf<TaskDependency>() val kaptClasspath = arrayListOf<File>()
val kaptConfigurations = arrayListOf<Configuration>() val buildDependencies = arrayListOf<TaskDependency>()
val kaptConfigurations = arrayListOf<Configuration>()
fun handleSourceSet(sourceSetName: String) {
val kaptConfiguration = project.findKaptConfiguration(sourceSetName) fun handleSourceSet(sourceSetName: String) {
val filteredDependencies = kaptConfiguration?.dependencies?.filter { val kaptConfiguration = project.findKaptConfiguration(sourceSetName)
it.group != getGroupName() || it.name != getArtifactName() val filteredDependencies = kaptConfiguration?.dependencies?.filter {
} ?: emptyList() it.group != getGroupName() || it.name != getArtifactName()
} ?: emptyList()
if (kaptConfiguration != null) {
kaptConfigurations += kaptConfiguration if (kaptConfiguration != null) {
buildDependencies += kaptConfiguration.buildDependencies kaptConfigurations += kaptConfiguration
buildDependencies += kaptConfiguration.buildDependencies
if (filteredDependencies.isNotEmpty()) {
kaptClasspath.addAll(kaptConfiguration.resolve()) if (filteredDependencies.isNotEmpty()) {
} kaptClasspath.addAll(kaptConfiguration.resolve())
} }
} }
}
val kaptVariantData = variantData as? KaptVariantData<*>
val kaptVariantData = variantData as? KaptVariantData<*>
val sourceSetName = if (kaptVariantData != null) {
for (provider in kaptVariantData.sourceProviders) { val sourceSetName = if (kaptVariantData != null) {
handleSourceSet((provider as AndroidSourceSet).name) for (provider in kaptVariantData.sourceProviders) {
} handleSourceSet((provider as AndroidSourceSet).name)
}
kaptVariantData.name
} kaptVariantData.name
else { }
if (javaSourceSet == null) error("Java source set should not be null") else {
if (javaSourceSet == null) error("Java source set should not be null")
handleSourceSet(javaSourceSet.name)
javaSourceSet.name handleSourceSet(javaSourceSet.name)
} javaSourceSet.name
}
val kaptExtension = project.extensions.getByType(KaptExtension::class.java)
val kaptExtension = project.extensions.getByType(KaptExtension::class.java)
val context = Kapt3SubpluginContext(project, kotlinCompile, javaCompile,
kaptVariantData, sourceSetName, javaSourceSet, kaptExtension, kaptClasspath) val context = Kapt3SubpluginContext(project, kotlinCompile, javaCompile,
kaptVariantData, sourceSetName, javaSourceSet, kaptExtension, kaptClasspath)
val kaptGenerateStubsTask = context.createKaptGenerateStubsTask()
val kaptTask = context.createKaptKotlinTask() val kaptGenerateStubsTask = context.createKaptGenerateStubsTask()
val kaptTask = context.createKaptKotlinTask()
kaptGenerateStubsTask.source(*kaptConfigurations.toTypedArray())
kaptGenerateStubsTask.source(*kaptConfigurations.toTypedArray())
kaptGenerateStubsTask.dependsOn(*buildDependencies.toTypedArray())
kaptGenerateStubsTask.dependsOn(*kotlinCompile.dependsOn.toTypedArray()) kaptGenerateStubsTask.dependsOn(*buildDependencies.toTypedArray())
kaptGenerateStubsTask.dependsOn(*kotlinCompile.dependsOn.toTypedArray())
kaptTask.dependsOn(kaptGenerateStubsTask)
kotlinCompile.dependsOn(kaptTask) kaptTask.dependsOn(kaptGenerateStubsTask)
kotlinCompile.dependsOn(kaptTask)
/** Plugin options are applied to kapt*Compile inside [createKaptKotlinTask] */
return emptyList() /** Plugin options are applied to kapt*Compile inside [createKaptKotlinTask] */
} return emptyList()
}
override fun getSubpluginKotlinTasks(project: Project, kotlinCompile: KotlinCompile): List<AbstractCompile> {
val kaptGenerateStubsTask = kotlinToKaptGenerateStubsTasksMap[kotlinCompile] override fun getSubpluginKotlinTasks(project: Project, kotlinCompile: KotlinCompile): List<AbstractCompile> {
return if (kaptGenerateStubsTask == null) emptyList() else listOf(kaptGenerateStubsTask) val kaptGenerateStubsTask = kotlinToKaptGenerateStubsTasksMap[kotlinCompile]
} return if (kaptGenerateStubsTask == null) emptyList() else listOf(kaptGenerateStubsTask)
}
// This method should be called no more than once for each Kapt3SubpluginContext
private fun Kapt3SubpluginContext.buildOptions(aptMode: String): List<SubpluginOption> { // This method should be called no more than once for each Kapt3SubpluginContext
val pluginOptions = mutableListOf<SubpluginOption>() private fun Kapt3SubpluginContext.buildOptions(aptMode: String): List<SubpluginOption> {
val pluginOptions = mutableListOf<SubpluginOption>()
val generatedFilesDir = getKaptGeneratedSourcesDir(project, sourceSetName)
kaptVariantData?.addJavaSourceFoldersToModel(generatedFilesDir) val generatedFilesDir = getKaptGeneratedSourcesDir(project, sourceSetName)
kaptVariantData?.addJavaSourceFoldersToModel(generatedFilesDir)
pluginOptions += SubpluginOption("aptMode", aptMode)
disableAnnotationProcessingInJavaTask() pluginOptions += SubpluginOption("aptMode", aptMode)
disableAnnotationProcessingInJavaTask()
kaptClasspath.forEach { pluginOptions += SubpluginOption("apclasspath", it.absolutePath) }
kaptClasspath.forEach { pluginOptions += SubpluginOption("apclasspath", it.absolutePath) }
javaCompile.source(generatedFilesDir)
javaCompile.source(generatedFilesDir)
pluginOptions += SubpluginOption("sources", generatedFilesDir.canonicalPath)
pluginOptions += SubpluginOption("classes", getKaptGeneratedClassesDir(project, sourceSetName).canonicalPath) pluginOptions += SubpluginOption("sources", generatedFilesDir.canonicalPath)
pluginOptions += SubpluginOption("classes", getKaptGeneratedClassesDir(project, sourceSetName).canonicalPath)
pluginOptions += SubpluginOption("incrementalData", getKaptIncrementalDataDir().canonicalPath)
pluginOptions += SubpluginOption("incrementalData", getKaptIncrementalDataDir().canonicalPath)
val annotationProcessors = kaptExtension.processors
if (annotationProcessors.isNotEmpty()) { val annotationProcessors = kaptExtension.processors
pluginOptions += SubpluginOption("processors", annotationProcessors) if (annotationProcessors.isNotEmpty()) {
} pluginOptions += SubpluginOption("processors", annotationProcessors)
}
val androidPlugin = kaptVariantData?.let {
project.extensions.findByName("android") as? BaseExtension val androidPlugin = kaptVariantData?.let {
} project.extensions.findByName("android") as? BaseExtension
}
val androidOptions = kaptVariantData?.annotationProcessorOptions ?: emptyMap()
val androidOptions = kaptVariantData?.annotationProcessorOptions ?: emptyMap()
kotlinSourcesOutputDir.mkdirs()
kotlinSourcesOutputDir.mkdirs()
val apOptions = kaptExtension.getAdditionalArguments(
project, val apOptions = kaptExtension.getAdditionalArguments(
kaptVariantData?.variantData, project,
androidPlugin kaptVariantData?.variantData,
) + androidOptions + mapOf("kapt.kotlin.generated" to kotlinSourcesOutputDir.absolutePath) androidPlugin
) + androidOptions + mapOf("kapt.kotlin.generated" to kotlinSourcesOutputDir.absolutePath)
pluginOptions += SubpluginOption("apoptions", encodeList(apOptions))
pluginOptions += SubpluginOption("apoptions", encodeList(apOptions))
pluginOptions += SubpluginOption("javacArguments", encodeList(kaptExtension.getJavacOptions()))
pluginOptions += SubpluginOption("javacArguments", encodeList(kaptExtension.getJavacOptions()))
addMiscOptions(pluginOptions)
addMiscOptions(pluginOptions)
return pluginOptions
} return pluginOptions
}
private fun Kapt3SubpluginContext.buildAndAddOptionsTo(container: CompilerPluginOptions, aptMode: String) {
val compilerPluginId = getCompilerPluginId() private fun Kapt3SubpluginContext.buildAndAddOptionsTo(container: CompilerPluginOptions, aptMode: String) {
for (option in wrapPluginOptions(buildOptions(aptMode), "configuration")) { val compilerPluginId = getCompilerPluginId()
container.addPluginArgument(compilerPluginId, option.key, option.value) for (option in wrapPluginOptions(buildOptions(aptMode), "configuration")) {
} container.addPluginArgument(compilerPluginId, option.key, option.value)
} }
}
private fun encodeList(options: Map<String, String>): String {
val os = ByteArrayOutputStream() private fun encodeList(options: Map<String, String>): String {
val oos = ObjectOutputStream(os) val os = ByteArrayOutputStream()
val oos = ObjectOutputStream(os)
oos.writeInt(options.size)
for ((key, value) in options.entries) { oos.writeInt(options.size)
oos.writeUTF(key) for ((key, value) in options.entries) {
oos.writeUTF(value) oos.writeUTF(key)
} oos.writeUTF(value)
}
oos.flush()
return Base64.getEncoder().encodeToString(os.toByteArray()) oos.flush()
} return Base64.getEncoder().encodeToString(os.toByteArray())
}
private fun Kapt3SubpluginContext.addMiscOptions(pluginOptions: MutableList<SubpluginOption>) {
if (kaptExtension.generateStubs) { private fun Kapt3SubpluginContext.addMiscOptions(pluginOptions: MutableList<SubpluginOption>) {
project.logger.warn("'kapt.generateStubs' is not used by the 'kotlin-kapt' plugin") if (kaptExtension.generateStubs) {
} project.logger.warn("'kapt.generateStubs' is not used by the 'kotlin-kapt' plugin")
}
pluginOptions += SubpluginOption("useLightAnalysis", "${kaptExtension.useLightAnalysis}")
pluginOptions += SubpluginOption("correctErrorTypes", "${kaptExtension.correctErrorTypes}") pluginOptions += SubpluginOption("useLightAnalysis", "${kaptExtension.useLightAnalysis}")
pluginOptions += SubpluginOption("stubs", getKaptStubsDir().canonicalPath) pluginOptions += SubpluginOption("correctErrorTypes", "${kaptExtension.correctErrorTypes}")
pluginOptions += SubpluginOption("stubs", getKaptStubsDir().canonicalPath)
if (project.hasProperty(VERBOSE_OPTION_NAME) && project.property(VERBOSE_OPTION_NAME) == "true") {
pluginOptions += SubpluginOption("verbose", "true") if (project.hasProperty(VERBOSE_OPTION_NAME) && project.property(VERBOSE_OPTION_NAME) == "true") {
} pluginOptions += SubpluginOption("verbose", "true")
} }
}
private fun Kapt3SubpluginContext.createKaptKotlinTask(): KaptTask {
val kaptTask = project.tasks.create( private fun Kapt3SubpluginContext.createKaptKotlinTask(): KaptTask {
getKaptTaskName("kapt"), val kaptTask = project.tasks.create(
KaptTask::class.java) getKaptTaskName("kapt"),
KaptTask::class.java)
kaptTask.useBuildCacheIfSupported()
kaptTask.useBuildCacheIfSupported()
kaptTask.kotlinCompileTask = kotlinCompile
kaptTask.kotlinCompileTask = kotlinCompile
kaptClasspathArtifacts.forEach { kaptTask.pluginOptions.addClasspathEntry(it) }
kaptClasspathArtifacts.forEach { kaptTask.pluginOptions.addClasspathEntry(it) }
kaptTask.stubsDir = getKaptStubsDir()
kaptTask.destinationDir = sourcesOutputDir kaptTask.stubsDir = getKaptStubsDir()
kaptTask.kotlinSourcesDestinationDir = kotlinSourcesOutputDir kaptTask.destinationDir = sourcesOutputDir
kaptTask.classesDir = classesOutputDir kaptTask.kotlinSourcesDestinationDir = kotlinSourcesOutputDir
kaptTask.classesDir = classesOutputDir
javaSourceSet?.output?.apply {
if (tryAddClassesDir { project.files(classesOutputDir).builtBy(kaptTask) }) { javaSourceSet?.output?.apply {
kotlinCompile.attachClassesDir { classesOutputDir } if (tryAddClassesDir { project.files(classesOutputDir).builtBy(kaptTask) }) {
} kotlinCompile.attachClassesDir { classesOutputDir }
} }
}
kotlinCompile.source(sourcesOutputDir, kotlinSourcesOutputDir)
kotlinCompile.source(sourcesOutputDir, kotlinSourcesOutputDir)
if (kaptVariantData != null) {
kaptVariantData.registerGeneratedJavaSource(project, kaptTask, javaCompile) if (kaptVariantData != null) {
} kaptVariantData.registerGeneratedJavaSource(project, kaptTask, javaCompile)
else { }
registerGeneratedJavaSource(kaptTask, javaCompile) else {
} registerGeneratedJavaSource(kaptTask, javaCompile)
}
kaptTask.kaptClasspath = kaptClasspath
kaptTask.kaptClasspath = kaptClasspath
buildAndAddOptionsTo(kaptTask.pluginOptions, aptMode = "apt")
kaptTask.useBuildCache = kaptExtension.useBuildCache
return kaptTask
} if (shouldEnableGradleCache()) {
val reason = "Caching is disabled by default for kapt because of arbitrary behavior of external " +
private fun Kapt3SubpluginContext.createKaptGenerateStubsTask(): KaptGenerateStubsTask { "annotation processors. You can enable it by adding 'kapt.useBuildCache = true' to the build script."
val kaptTask = project.tasks.create( kaptTask.outputs.doNotCacheIf(reason) { !kaptTask.useBuildCache }
getKaptTaskName("kaptGenerateStubs"), }
KaptGenerateStubsTask::class.java)
buildAndAddOptionsTo(kaptTask.pluginOptions, aptMode = "apt")
kaptTask.useBuildCacheIfSupported()
return kaptTask
kaptTask.sourceSetName = sourceSetName }
kaptTask.kotlinCompileTask = kotlinCompile
kotlinToKaptGenerateStubsTasksMap[kotlinCompile] = kaptTask private fun Kapt3SubpluginContext.createKaptGenerateStubsTask(): KaptGenerateStubsTask {
val kaptTask = project.tasks.create(
kaptClasspathArtifacts.forEach { kaptTask.pluginOptions.addClasspathEntry(it) } getKaptTaskName("kaptGenerateStubs"),
KaptGenerateStubsTask::class.java)
kaptTask.stubsDir = getKaptStubsDir()
kaptTask.destinationDir = getKaptIncrementalDataDir() kaptTask.useBuildCacheIfSupported()
kaptTask.mapClasspath { kotlinCompile.classpath }
kaptTask.generatedSourcesDir = sourcesOutputDir kaptTask.sourceSetName = sourceSetName
mapKotlinTaskProperties(project, kaptTask) kaptTask.kotlinCompileTask = kotlinCompile
kotlinToKaptGenerateStubsTasksMap[kotlinCompile] = kaptTask
kaptTask.kaptClasspath = kaptClasspath
buildAndAddOptionsTo(kaptTask.pluginOptions, aptMode = "stubs") kaptClasspathArtifacts.forEach { kaptTask.pluginOptions.addClasspathEntry(it) }
return kaptTask kaptTask.stubsDir = getKaptStubsDir()
} kaptTask.destinationDir = getKaptIncrementalDataDir()
kaptTask.mapClasspath { kotlinCompile.classpath }
private fun Kapt3SubpluginContext.getKaptTaskName(prefix: String): String { kaptTask.generatedSourcesDir = sourcesOutputDir
// Replace compile*Kotlin to kapt*Kotlin mapKotlinTaskProperties(project, kaptTask)
val baseName = kotlinCompile.name
assert(baseName.startsWith("compile")) kaptTask.kaptClasspath = kaptClasspath
return baseName.replaceFirst("compile", prefix) buildAndAddOptionsTo(kaptTask.pluginOptions, aptMode = "stubs")
}
return kaptTask
private fun Kapt3SubpluginContext.disableAnnotationProcessingInJavaTask() { }
(javaCompile as? JavaCompile)?.let { javaCompile ->
val options = javaCompile.options private fun Kapt3SubpluginContext.getKaptTaskName(prefix: String): String {
// 'android-apt' (com.neenbedankt) adds a File instance to compilerArgs (List<String>). // Replace compile*Kotlin to kapt*Kotlin
// Although it's not our problem, we need to handle this case properly. val baseName = kotlinCompile.name
val oldCompilerArgs: List<Any> = options.compilerArgs assert(baseName.startsWith("compile"))
val newCompilerArgs = oldCompilerArgs.filterTo(mutableListOf()) { return baseName.replaceFirst("compile", prefix)
it !is CharSequence || !it.toString().startsWith("-proc:") }
}
newCompilerArgs.add("-proc:none") private fun Kapt3SubpluginContext.disableAnnotationProcessingInJavaTask() {
@Suppress("UNCHECKED_CAST") (javaCompile as? JavaCompile)?.let { javaCompile ->
options.compilerArgs = newCompilerArgs as List<String> val options = javaCompile.options
} // 'android-apt' (com.neenbedankt) adds a File instance to compilerArgs (List<String>).
} // Although it's not our problem, we need to handle this case properly.
val oldCompilerArgs: List<Any> = options.compilerArgs
override fun getCompilerPluginId() = "org.jetbrains.kotlin.kapt3" val newCompilerArgs = oldCompilerArgs.filterTo(mutableListOf()) {
override fun getGroupName() = KAPT_GROUP_NAME it !is CharSequence || !it.toString().startsWith("-proc:")
override fun getArtifactName() = KAPT_ARTIFACT_NAME }
} newCompilerArgs.add("-proc:none")
@Suppress("UNCHECKED_CAST")
internal fun registerGeneratedJavaSource(kaptTask: KaptTask, javaTask: AbstractCompile) { options.compilerArgs = newCompilerArgs as List<String>
javaTask.source(kaptTask.destinationDir) }
} }
internal fun Configuration.getNamedDependencies(): List<Dependency> = allDependencies.filter { it.group != null && it.name != null } override fun getCompilerPluginId() = "org.jetbrains.kotlin.kapt3"
override fun getGroupName() = KAPT_GROUP_NAME
internal fun checkAndroidAnnotationProcessorDependencyUsage(project: Project) { override fun getArtifactName() = KAPT_ARTIFACT_NAME
val isKapt3Enabled = Kapt3GradleSubplugin.isEnabled(project) }
val androidAPDependencies = project.configurations internal fun registerGeneratedJavaSource(kaptTask: KaptTask, javaTask: AbstractCompile) {
.filter { it.name == "annotationProcessor" javaTask.source(kaptTask.destinationDir)
|| (it.name.endsWith("AnnotationProcessor") && !it.name.startsWith("_")) } }
.flatMap { it.getNamedDependencies() }
internal fun Configuration.getNamedDependencies(): List<Dependency> = allDependencies.filter { it.group != null && it.name != null }
if (androidAPDependencies.isNotEmpty()) {
val artifactsRendered = androidAPDependencies.joinToString { "'${it.group}:${it.name}:${it.version}'" } internal fun checkAndroidAnnotationProcessorDependencyUsage(project: Project) {
val andApplyKapt = if (isKapt3Enabled) "" else " and apply the kapt plugin: \"apply plugin: 'kotlin-kapt'\"" val isKapt3Enabled = Kapt3GradleSubplugin.isEnabled(project)
project.logger.warn("${project.name}: " +
"'annotationProcessor' dependencies won't be recognized as kapt annotation processors. " + val androidAPDependencies = project.configurations
"Please change the configuration name to 'kapt' for these artifacts: $artifactsRendered$andApplyKapt.") .filter { it.name == "annotationProcessor"
} || (it.name.endsWith("AnnotationProcessor") && !it.name.startsWith("_")) }
} .flatMap { it.getNamedDependencies() }
if (androidAPDependencies.isNotEmpty()) {
val artifactsRendered = androidAPDependencies.joinToString { "'${it.group}:${it.name}:${it.version}'" }
val andApplyKapt = if (isKapt3Enabled) "" else " and apply the kapt plugin: \"apply plugin: 'kotlin-kapt'\""
project.logger.warn("${project.name}: " +
"'annotationProcessor' dependencies won't be recognized as kapt annotation processors. " +
"Please change the configuration name to 'kapt' for these artifacts: $artifactsRendered$andApplyKapt.")
}
}
@@ -1,118 +1,121 @@
package org.jetbrains.kotlin.gradle.internal package org.jetbrains.kotlin.gradle.internal
import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil.compareVersionNumbers import com.intellij.openapi.util.text.StringUtil.compareVersionNumbers
import org.gradle.api.GradleException import org.gradle.api.GradleException
import org.gradle.api.file.FileCollection import org.gradle.api.file.FileCollection
import org.gradle.api.internal.ConventionTask import org.gradle.api.internal.ConventionTask
import org.gradle.api.tasks.* import org.gradle.api.tasks.*
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.compilerRunner.GradleCompilerEnvironment import org.jetbrains.kotlin.compilerRunner.GradleCompilerEnvironment
import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner
import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl
import org.jetbrains.kotlin.gradle.tasks.* import org.jetbrains.kotlin.gradle.tasks.*
import java.io.File import java.io.File
@CacheableTask @CacheableTask
open class KaptTask : ConventionTask(), CompilerArgumentAware<K2JVMCompilerArguments> { open class KaptTask : ConventionTask(), CompilerArgumentAware<K2JVMCompilerArguments> {
@get:Internal @get:Internal
internal val pluginOptions = CompilerPluginOptions() internal val pluginOptions = CompilerPluginOptions()
@get:Internal @get:Internal
internal lateinit var kotlinCompileTask: KotlinCompile internal lateinit var kotlinCompileTask: KotlinCompile
@get:Internal @get:Internal
internal lateinit var stubsDir: File internal lateinit var stubsDir: File
private fun isInsideDestinationDirs(file: File): Boolean { private fun isInsideDestinationDirs(file: File): Boolean {
return FileUtil.isAncestor(destinationDir, file, /* strict = */ false) return FileUtil.isAncestor(destinationDir, file, /* strict = */ false)
|| FileUtil.isAncestor(classesDir, file, /* strict = */ false) || FileUtil.isAncestor(classesDir, file, /* strict = */ false)
} }
@get:Classpath @get:InputFiles @get:Classpath @get:InputFiles
lateinit var kaptClasspath: List<File> lateinit var kaptClasspath: List<File>
@get:OutputDirectory @get:OutputDirectory
internal lateinit var classesDir: File internal lateinit var classesDir: File
@get:OutputDirectory @get:OutputDirectory
lateinit var destinationDir: File lateinit var destinationDir: File
@get:OutputDirectory @get:OutputDirectory
lateinit var kotlinSourcesDestinationDir: File lateinit var kotlinSourcesDestinationDir: File
override fun createCompilerArgs(): K2JVMCompilerArguments = K2JVMCompilerArguments() override fun createCompilerArgs(): K2JVMCompilerArguments = K2JVMCompilerArguments()
override fun setupCompilerArgs(args: K2JVMCompilerArguments, defaultsOnly: Boolean) { override fun setupCompilerArgs(args: K2JVMCompilerArguments, defaultsOnly: Boolean) {
kotlinCompileTask.setupCompilerArgs(args) kotlinCompileTask.setupCompilerArgs(args)
args.pluginClasspaths = (pluginClasspath + args.pluginClasspaths!!).toSet().toTypedArray() args.pluginClasspaths = (pluginClasspath + args.pluginClasspaths!!).toSet().toTypedArray()
args.pluginOptions = (pluginOptions.arguments + args.pluginOptions!!).toTypedArray() args.pluginOptions = (pluginOptions.arguments + args.pluginOptions!!).toTypedArray()
args.verbose = project.hasProperty("kapt.verbose") && project.property("kapt.verbose").toString().toBoolean() == true args.verbose = project.hasProperty("kapt.verbose") && project.property("kapt.verbose").toString().toBoolean() == true
} }
@get:Classpath @get:InputFiles @get:Classpath @get:InputFiles
val classpath: FileCollection val classpath: FileCollection
get() = kotlinCompileTask.classpath get() = kotlinCompileTask.classpath
@get:Classpath @get:InputFiles @Suppress("unused") @get:Internal
internal val kotlinTaskPluginClasspaths get() = kotlinCompileTask.pluginClasspath var useBuildCache: Boolean = false
@get:Classpath @get:InputFiles @get:Classpath @get:InputFiles @Suppress("unused")
internal val pluginClasspath get() = pluginOptions.classpath internal val kotlinTaskPluginClasspaths get() = kotlinCompileTask.pluginClasspath
@get:InputFiles @get:PathSensitive(PathSensitivity.ABSOLUTE) @get:Classpath @get:InputFiles
val source: FileCollection internal val pluginClasspath get() = pluginOptions.classpath
get() {
val sourcesFromKotlinTask = kotlinCompileTask.source @get:InputFiles @get:PathSensitive(PathSensitivity.ABSOLUTE)
.filter { it.extension == "java" && !isInsideDestinationDirs(it) } val source: FileCollection
get() {
val stubSources = project.fileTree(stubsDir) val sourcesFromKotlinTask = kotlinCompileTask.source
return sourcesFromKotlinTask + stubSources .filter { it.extension == "java" && !isInsideDestinationDirs(it) }
}
val stubSources = project.fileTree(stubsDir)
@TaskAction return sourcesFromKotlinTask + stubSources
fun compile() { }
/** Delete everything inside generated sources and classes output directory
* (annotation processing is not incremental) */ @TaskAction
destinationDir.clearDirectory() fun compile() {
classesDir.clearDirectory() /** Delete everything inside generated sources and classes output directory
kotlinSourcesDestinationDir.clearDirectory() * (annotation processing is not incremental) */
destinationDir.clearDirectory()
val sourceRootsFromKotlin = kotlinCompileTask.sourceRootsContainer.sourceRoots classesDir.clearDirectory()
val rawSourceRoots = FilteringSourceRootsContainer(sourceRootsFromKotlin, { !isInsideDestinationDirs(it) }) kotlinSourcesDestinationDir.clearDirectory()
val sourceRoots = SourceRoots.ForJvm.create(kotlinCompileTask.source, rawSourceRoots)
val sourceRootsFromKotlin = kotlinCompileTask.sourceRootsContainer.sourceRoots
val args = prepareCompilerArguments() val rawSourceRoots = FilteringSourceRootsContainer(sourceRootsFromKotlin, { !isInsideDestinationDirs(it) })
val sourceRoots = SourceRoots.ForJvm.create(kotlinCompileTask.source, rawSourceRoots)
val messageCollector = GradleMessageCollector(logger)
val outputItemCollector = OutputItemsCollectorImpl() val args = prepareCompilerArguments()
val environment = GradleCompilerEnvironment(kotlinCompileTask.computedCompilerClasspath, messageCollector, outputItemCollector, args)
if (environment.toolsJar == null && !isAtLeastJava9) { val messageCollector = GradleMessageCollector(logger)
throw GradleException("Could not find tools.jar in system classpath, which is required for kapt to work") val outputItemCollector = OutputItemsCollectorImpl()
} val environment = GradleCompilerEnvironment(kotlinCompileTask.computedCompilerClasspath, messageCollector, outputItemCollector, args)
if (environment.toolsJar == null && !isAtLeastJava9) {
val compilerRunner = GradleCompilerRunner(project) throw GradleException("Could not find tools.jar in system classpath, which is required for kapt to work")
val exitCode = compilerRunner.runJvmCompiler( }
sourceRoots.kotlinSourceFiles,
sourceRoots.javaSourceRoots, val compilerRunner = GradleCompilerRunner(project)
kotlinCompileTask.javaPackagePrefix, val exitCode = compilerRunner.runJvmCompiler(
args, sourceRoots.kotlinSourceFiles,
environment) sourceRoots.javaSourceRoots,
throwGradleExceptionIfError(exitCode) kotlinCompileTask.javaPackagePrefix,
} args,
environment)
private fun File.clearDirectory() { throwGradleExceptionIfError(exitCode)
deleteRecursively() }
mkdirs()
} private fun File.clearDirectory() {
deleteRecursively()
private val isAtLeastJava9: Boolean mkdirs()
get() = compareVersionNumbers(getJavaRuntimeVersion(), "9") >= 0 }
private fun getJavaRuntimeVersion(): String { private val isAtLeastJava9: Boolean
val rtVersion = System.getProperty("java.runtime.version") get() = compareVersionNumbers(getJavaRuntimeVersion(), "9") >= 0
return if (Character.isDigit(rtVersion[0])) rtVersion else System.getProperty("java.version")
} private fun getJavaRuntimeVersion(): String {
val rtVersion = System.getProperty("java.runtime.version")
return if (Character.isDigit(rtVersion[0])) rtVersion else System.getProperty("java.version")
}
} }
@@ -32,12 +32,20 @@ open class KaptExtension {
open var processors: String = "" open var processors: String = ""
/** Explicit opt-in switch for Kapt caching. Should be used when annotation processors used by this project are
* certain NOT to use anything aside from the task inputs in their logic and are guaranteed to produce the same
* output on subsequent runs without input changes. */
var useBuildCache: Boolean = false
private val apOptionsActions = private val apOptionsActions =
mutableListOf<(KaptAnnotationProcessorOptions) -> Unit>() mutableListOf<(KaptAnnotationProcessorOptions) -> Unit>()
private val javacOptionsActions = private val javacOptionsActions =
mutableListOf<(KaptJavacOptionsDelegate) -> Unit>() mutableListOf<(KaptJavacOptionsDelegate) -> Unit>()
private var apOptionsClosure: Closure<*>? = null
private var javacOptionsClosure: Closure<*>? = null
open fun arguments(closure: Closure<*>) { open fun arguments(closure: Closure<*>) {
apOptionsActions += { apOptions -> apOptionsActions += { apOptions ->
apOptions.execute(closure) apOptions.execute(closure)