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