diff --git a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleSubplugin.kt b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleSubplugin.kt index 6147d9f01bd..d077a6a3234 100644 --- a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleSubplugin.kt +++ b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleSubplugin.kt @@ -19,8 +19,24 @@ package org.jetbrains.kotlin.gradle.plugin import org.gradle.api.Project import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.compile.AbstractCompile +import java.io.File -class SubpluginOption(val key: String, val value: String) +open class SubpluginOption(val key: String, open val value: String) + +class FilesSubpluginOption( + key: String, + val kind: FileOptionKind, + val files: List, + value: String = files.joinToString(File.pathSeparator) { it.canonicalPath }) + : SubpluginOption(key, value) + +class WrapperSubpluginOption( + key: String, + value: String, + val originalOptions: List) + : SubpluginOption(key, value) + +enum class FileOptionKind { INPUT_FILES, CLASSPATH_INPUT, OUTPUT_FILES, OUTPUT_DIRS, INTERNAL } interface KotlinGradleSubplugin { fun isApplicable(project: Project, task: AbstractCompile): Boolean diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidSubplugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidSubplugin.kt index c447e664832..4d6139d654e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidSubplugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AndroidSubplugin.kt @@ -116,8 +116,13 @@ class AndroidSubplugin : KotlinGradleSubplugin { pluginOptions += SubpluginOption("package", applicationPackage) fun addVariant(sourceSet: AndroidSourceSet) { - pluginOptions += SubpluginOption("variant", sourceSet.name + ';' + - sourceSet.res.srcDirs.joinToString(";") { it.absolutePath }) + val optionValue = sourceSet.name + ';' + + sourceSet.res.srcDirs.joinToString(";") { it.absolutePath } + pluginOptions += WrapperSubpluginOption("variant", optionValue, listOf( + SubpluginOption("sourceSetName", sourceSet.name), + //use the INTERNAL option kind since the resources are tracked as sources (see below) + FilesSubpluginOption("resDirs", FileOptionKind.INTERNAL, sourceSet.res.srcDirs.toList()) + )) kotlinCompile.source(project.files(getLayoutDirectories(sourceSet.res.srcDirs))) } @@ -160,11 +165,15 @@ class AndroidSubplugin : KotlinGradleSubplugin { pluginOptions += SubpluginOption("package", getApplicationPackage(project, mainSourceSet)) fun addVariant(name: String, resDirectories: List) { - pluginOptions += SubpluginOption("variant", buildString { + val optionValue = buildString { append(name) append(';') resDirectories.joinTo(this, separator = ";") { it.canonicalPath } - }) + } + pluginOptions += WrapperSubpluginOption("variant", optionValue, listOf( + SubpluginOption("variantName", name), + // use INTERNAL option kind since the resources are tracked as sources (see below) + FilesSubpluginOption("resDirs", FileOptionKind.INTERNAL, resDirectories))) kotlinCompile.source(project.files(getLayoutDirectories(resDirectories))) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AnnotationProcessingManager.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AnnotationProcessingManager.kt index 1314b86060e..abb358d407d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AnnotationProcessingManager.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AnnotationProcessingManager.kt @@ -36,6 +36,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider import org.jetbrains.kotlin.gradle.tasks.kapt.generateAnnotationProcessorWrapper import org.jetbrains.kotlin.gradle.tasks.kapt.generateKotlinAptAnnotation +import org.jetbrains.kotlin.gradle.tasks.shouldEnableGradleCache import java.io.File import java.io.IOException import java.util.zip.ZipFile @@ -78,6 +79,11 @@ internal fun Project.initKapt( // javaTask.doLast { // moveGeneratedJavaFilesToCorrespondingDirectories(kaptManager.aptOutputDir) // } + + if (shouldEnableGradleCache()) { + // Since Kapt1 is about to be dropped, disable the cache for it: + kotlinAfterJavaTask.outputs.doNotCacheIf("Caching is not supported with deprecated Kapt1") { true } + } } else { kotlinAfterJavaTask = null kotlinTask.logger.kotlinDebug("kapt: Class file stubs are not used") @@ -103,6 +109,12 @@ internal fun Project.initKapt( } kotlinTask.kaptOptions.annotationsFile = kaptManager.getAnnotationFile() + + if (shouldEnableGradleCache()) { + // Since Kapt1 is about to be dropped, disable the cache for it: + kotlinTask.outputs.doNotCacheIf("Caching is not supported with deprecated Kapt1") { true } + } + return kotlinAfterJavaTask } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/Kapt3KotlinGradleSubplugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/Kapt3KotlinGradleSubplugin.kt index c819fe2558d..5c20765299d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/Kapt3KotlinGradleSubplugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/Kapt3KotlinGradleSubplugin.kt @@ -1,403 +1,413 @@ -/* - * 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 { - 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(val variantData: T) { - abstract val name: String - abstract val sourceProviders: Iterable - abstract fun addJavaSourceFoldersToModel(generatedFilesDir: File) - abstract val annotationProcessorOptions: Map? - abstract fun registerGeneratedJavaSource( - project: Project, - kaptTask: KaptTask, - javaTask: AbstractCompile) -} - -// Subplugin for the Kotlin Gradle plugin -class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { - 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() - - 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) { - 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 { - assert((variantData != null) xor (javaSourceSet != null)) - - val kaptClasspath = arrayListOf() - val buildDependencies = arrayListOf() - val kaptConfigurations = arrayListOf() - - 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 { - 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 { - val pluginOptions = mutableListOf() - - 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 { - 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) { - 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). - // Although it's not our problem, we need to handle this case properly. - val oldCompilerArgs: List = 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 - } - } - - 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 = 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.Task +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 { + 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(val variantData: T) { + abstract val name: String + abstract val sourceProviders: Iterable + abstract fun addJavaSourceFoldersToModel(generatedFilesDir: File) + abstract val annotationProcessorOptions: Map? + abstract fun registerGeneratedJavaSource( + project: Project, + kaptTask: KaptTask, + javaTask: AbstractCompile) +} + +// Subplugin for the Kotlin Gradle plugin +class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { + 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() + + 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) { + 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 { + assert((variantData != null) xor (javaSourceSet != null)) + + val kaptClasspath = arrayListOf() + val buildDependencies = arrayListOf() + val kaptConfigurations = arrayListOf() + + 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 { + 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 { + val pluginOptions = mutableListOf() + + val generatedFilesDir = getKaptGeneratedSourcesDir(project, sourceSetName) + kaptVariantData?.addJavaSourceFoldersToModel(generatedFilesDir) + + pluginOptions += SubpluginOption("aptMode", aptMode) + disableAnnotationProcessingInJavaTask() + + kaptClasspath.forEach { pluginOptions += FilesSubpluginOption("apclasspath", FileOptionKind.INTERNAL, listOf(it)) } + + javaCompile.source(generatedFilesDir) + + pluginOptions += FilesSubpluginOption("sources", FileOptionKind.INTERNAL, listOf(generatedFilesDir)) + pluginOptions += FilesSubpluginOption("classes", FileOptionKind.INTERNAL, listOf(getKaptGeneratedClassesDir(project, sourceSetName))) + + pluginOptions += FilesSubpluginOption("incrementalData", FileOptionKind.INTERNAL, listOf(getKaptIncrementalDataDir())) + + 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) + .map { SubpluginOption(it.key, it.value) } + + FilesSubpluginOption("kapt.kotlin.generated", FileOptionKind.INTERNAL, listOf(kotlinSourcesOutputDir)) + + pluginOptions += WrapperSubpluginOption("apoptions", encodeList(apOptions.associate { it.key to it.value }), apOptions) + + pluginOptions += SubpluginOption("javacArguments", encodeList(kaptExtension.getJavacOptions())) + + addMiscOptions(pluginOptions) + + return pluginOptions + } + + private fun Kapt3SubpluginContext.buildAndAddOptionsTo(task: Task, container: CompilerPluginOptions, aptMode: String) { + val compilerPluginId = getCompilerPluginId() + for (option in wrapPluginOptions(buildOptions(aptMode), "configuration")) { + container.addPluginArgument(compilerPluginId, option) + task.registerSubpluginOptionAsInput(compilerPluginId, option) + } + + // Also register all the subplugin options from the Kotlin task: + project.afterEvaluate { + kotlinCompile.pluginOptions.subpluginOptionsByPluginId.forEach { (pluginId, options) -> + options.forEach { option -> + task.registerSubpluginOptionAsInput("kotlinCompile.$pluginId", option) + } + } + } + } + + private fun encodeList(options: Map): 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) { + 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 += FilesSubpluginOption("stubs", FileOptionKind.INTERNAL, listOf(getKaptStubsDir())) + + 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() + + 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 } + } + + kaptTask.useBuildCache = kaptExtension.useBuildCache + + 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, 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, 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). + // Although it's not our problem, we need to handle this case properly. + val oldCompilerArgs: List = 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 + } + } + + 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 = 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.") + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/subpluginUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/subpluginUtils.kt index 80c80698d37..efae5dc60eb 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/subpluginUtils.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/subpluginUtils.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.gradle.internal import org.jetbrains.kotlin.gradle.plugin.SubpluginOption +import org.jetbrains.kotlin.gradle.plugin.WrapperSubpluginOption import java.io.ByteArrayOutputStream import java.io.ObjectOutputStream import java.util.* @@ -42,6 +43,6 @@ fun encodePluginOptions(options: Map>): String { fun wrapPluginOptions(options: List, newOptionName: String): List { val groupedOptions = options.groupBy { it.key }.mapValues { opt -> opt.value.map { it.value } } val encodedOptions = encodePluginOptions(groupedOptions) - val singleOption = SubpluginOption(newOptionName, encodedOptions) + val singleOption = WrapperSubpluginOption(newOptionName, encodedOptions, options) return listOf(singleOption) } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt index 65bc86a6001..4ec43cfe53d 100755 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt @@ -20,9 +20,7 @@ import org.gradle.api.plugins.InvalidPluginException import org.gradle.api.plugins.JavaBasePlugin import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.JavaPluginConvention -import org.gradle.api.tasks.Delete -import org.gradle.api.tasks.SourceSet -import org.gradle.api.tasks.SourceSetOutput +import org.gradle.api.tasks.* import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.api.tasks.compile.JavaCompile import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments @@ -822,7 +820,9 @@ internal class SubpluginEnvironment( subpluginClasspath.forEach { pluginOptions.addClasspathEntry(it) } for (option in subplugin.apply(project, kotlinTask, javaTask, variantData, androidProjectHandler, javaSourceSet)) { - pluginOptions.addPluginArgument(subplugin.getCompilerPluginId(), option.key, option.value) + val pluginId = subplugin.getCompilerPluginId() + pluginOptions.addPluginArgument(pluginId, option) + kotlinTask.registerSubpluginOptionAsInput(pluginId, option) } } @@ -830,6 +830,43 @@ internal class SubpluginEnvironment( } } +private val fileOptionsPathSensitivity = PathSensitivity.ABSOLUTE + +internal fun Task.registerSubpluginOptionAsInput(subpluginId: String, option: SubpluginOption) { + when (option) { + is WrapperSubpluginOption -> { + val subpluginIdWithWrapperKey = "$subpluginId.${option.key}" + option.originalOptions.forEach { registerSubpluginOptionAsInput(subpluginIdWithWrapperKey, it) } + } + is FilesSubpluginOption -> { + when (option.kind) { + FileOptionKind.INTERNAL -> Unit + FileOptionKind.OUTPUT_FILES -> outputsCompatible.filesCompatible(*option.files.toTypedArray()) + FileOptionKind.OUTPUT_DIRS -> option.files.forEach { outputsCompatible.dirCompatible(it) } + FileOptionKind.INPUT_FILES, FileOptionKind.CLASSPATH_INPUT -> { + if (!shouldEnableGradleCache()) { + // Normalization makes no sense when we don't mean to use the cache, + // and moreover, Gradle lacks some of the APIs in the earlier versions. + inputsCompatible.filesCompatible(option.files) + } + else { + if (option.kind == FileOptionKind.CLASSPATH_INPUT) + inputs.files(option.files).withNormalizer(ClasspathNormalizer::class.java) + else + inputs.files(option.files).withPathSensitivity(fileOptionsPathSensitivity) + } + } + } + } + else -> { + // Since there might be multiple subplugin options with the same key, + // use the properties count to resolve duplication: + val propertyInputsCount = inputsCompatible.properties.size + inputsCompatible.propertyCompatible("$subpluginId." + option.key + ".$propertyInputsCount", option.value) + } + } +} + private fun Project.getAptDirsForSourceSet(sourceSetName: String): Pair { val aptOutputDir = File(buildDir, "generated/source/kapt") val aptOutputDirForVariant = File(aptOutputDir, sourceSetName) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/CompilerPluginOptions.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/CompilerPluginOptions.kt index 395b425cc28..4bc6b660a65 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/CompilerPluginOptions.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/CompilerPluginOptions.kt @@ -16,12 +16,16 @@ package org.jetbrains.kotlin.gradle.tasks +import org.jetbrains.kotlin.gradle.plugin.SubpluginOption import java.io.File internal class CompilerPluginOptions { private val mutableClasspath = arrayListOf() private val mutableArguments = arrayListOf() + internal val subpluginOptionsByPluginId = + mutableMapOf>() + val classpath: List get() = mutableClasspath @@ -38,7 +42,8 @@ internal class CompilerPluginOptions { mutableClasspath.remove(file.canonicalPath) } - fun addPluginArgument(pluginId: String, key: String, value: String) { - mutableArguments.add("plugin:$pluginId:$key=$value") + fun addPluginArgument(pluginId: String, option: SubpluginOption) { + mutableArguments.add("plugin:$pluginId:${option.key}=${option.value}") + subpluginOptionsByPluginId.getOrPut(pluginId) { mutableListOf() }.add(option) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt index ecad7c907da..0c4b7c60a24 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt @@ -38,8 +38,7 @@ import org.jetbrains.kotlin.compilerRunner.* import org.jetbrains.kotlin.gradle.dsl.* import org.jetbrains.kotlin.gradle.internal.CompilerArgumentAware import org.jetbrains.kotlin.gradle.internal.prepareCompilerArguments -import org.jetbrains.kotlin.gradle.plugin.kotlinDebug -import org.jetbrains.kotlin.gradle.plugin.kotlinInfo +import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.utils.ParsedGradleVersion import org.jetbrains.kotlin.incremental.* import org.jetbrains.kotlin.incremental.multiproject.ArtifactDifferenceRegistry @@ -413,15 +412,15 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl kaptAnnotationsFileUpdater = AnnotationFileUpdaterImpl(kaptAnnotationsFile) } - addPluginArgument(ANNOTATIONS_PLUGIN_NAME, "output", kaptAnnotationsFile.canonicalPath) + addPluginArgument(ANNOTATIONS_PLUGIN_NAME, FilesSubpluginOption("output", FileOptionKind.INTERNAL, listOf(kaptAnnotationsFile))) } if (kaptOptions.generateStubs) { - addPluginArgument(ANNOTATIONS_PLUGIN_NAME, "stubs", destinationDir.canonicalPath) + addPluginArgument(ANNOTATIONS_PLUGIN_NAME, FilesSubpluginOption("stubs", FileOptionKind.INTERNAL, listOf(destinationDir))) } if (kaptOptions.supportInheritedAnnotations) { - addPluginArgument(ANNOTATIONS_PLUGIN_NAME, "inherited", true.toString()) + addPluginArgument(ANNOTATIONS_PLUGIN_NAME, SubpluginOption("inherited", true.toString())) } }