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 9132e85870b..d44cd82d9e2 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 @@ -45,6 +45,9 @@ enum class FilesOptionKind { // such as output directories, inputs or classpath options. } +/** Defines a subplugin option that should be excluded from Gradle input/output checks */ +open class InternalSubpluginOption(key: String, value: String) : SubpluginOption(key, value) + interface KotlinGradleSubplugin { fun isApplicable(project: Project, task: AbstractCompile): Boolean diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt index c919612c152..8453de33a66 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/AbstractKotlinAndroidGradleTests.kt @@ -26,17 +26,20 @@ class KotlinAndroid32GradleIT : KotlinAndroid3GradleIT(GradleVersionRequired.AtL apply plugin: 'kotlin-kapt' class MyNested implements org.gradle.process.CommandLineArgumentProvider { - String value = "" @InputFile File inputFile = null @Override - Iterable asArguments() { return [value] } + @Internal + Iterable asArguments() { + // Read the arguments from a file, because changing them in a build script is treated as an + // implementation change by Gradle: + return [new File('args.txt').text] + } } def nested = new MyNested() - nested.value = '123' nested.inputFile = file("${'$'}projectDir/in.txt") android.applicationVariants.all { @@ -46,6 +49,7 @@ class KotlinAndroid32GradleIT : KotlinAndroid3GradleIT(GradleVersionRequired.AtL ) File(projectDir, "Android/in.txt").appendText("1234") + File(projectDir, "args.txt").appendText("1234") val kaptTasks = listOf(":Android:kaptFlavor1DebugKotlin") val javacTasks = listOf(":Android:compileFlavor1DebugJavaWithJavac") @@ -65,14 +69,13 @@ class KotlinAndroid32GradleIT : KotlinAndroid3GradleIT(GradleVersionRequired.AtL assertTasksUpToDate(javacTasks) } - gradleBuildScript(subproject = "Android").modify { - it.replace("nested.value = '123'", "nested.value = '456'") - } + // Changing only the annotation provider arguments should not trigger the tasks to run, as the arguments may be outputs, + // internals or neither: + File(projectDir, "args.txt").appendText("5678") build(*buildTasks) { assertSuccessful() - assertTasksExecuted(kaptTasks) - assertTasksUpToDate(javacTasks) + assertTasksUpToDate(javacTasks + kaptTasks) } } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt3AndroidIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt3AndroidIT.kt index a75e2283d2e..2b1b9e242ec 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt3AndroidIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt3AndroidIT.kt @@ -1,18 +1,18 @@ package org.jetbrains.kotlin.gradle import org.jetbrains.kotlin.gradle.util.isLegacyAndroidGradleVersion +import org.jetbrains.kotlin.gradle.util.modify import org.jetbrains.kotlin.test.KotlinTestUtils import org.junit.Test -import java.io.File class Kapt3Android30IT : Kapt3AndroidIT() { override val androidGradlePluginVersion: String - get() = "3.0.0-beta1" + get() = "3.2.0-alpha18" } open class Kapt3AndroidIT : Kapt3BaseIT() { companion object { - private val GRADLE_VERSION = GradleVersionRequired.AtLeast("4.1") + private val GRADLE_VERSION = GradleVersionRequired.AtLeast("4.6") } protected open val androidGradlePluginVersion: String @@ -37,8 +37,17 @@ open class Kapt3AndroidIT : Kapt3BaseIT() { assertSuccessful() assertKaptSuccessful() assertFileExists("app/build/generated/source/kapt/debug/org/example/kotlin/butterknife/SimpleActivity\$\$ViewBinder.java") - assertFileExists("app/build/intermediates/classes/debug/org/example/kotlin/butterknife/SimpleActivity\$\$ViewBinder.class") + + val butterknifeJavaClassesDir = + if (isLegacyAndroidGradleVersion(androidGradlePluginVersion)) + "app/build/intermediates/classes/debug/org/example/kotlin/butterknife/" + else + "app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/org/example/kotlin/butterknife/" + + assertFileExists(butterknifeJavaClassesDir + "SimpleActivity\$\$ViewBinder.class") + assertFileExists("app/build/tmp/kotlin-classes/debug/org/example/kotlin/butterknife/SimpleAdapter\$ViewHolder.class") + if (isLegacyAndroidGradleVersion(androidGradlePluginVersion)) { // we don't copy classes with new AGP assertFileExists("app/build/intermediates/classes/debug/org/example/kotlin/butterknife/SimpleAdapter\$ViewHolder.class") @@ -61,7 +70,15 @@ open class Kapt3AndroidIT : Kapt3BaseIT() { assertKaptSuccessful() assertFileExists("app/build/generated/source/kapt/debug/com/example/dagger/kotlin/DaggerApplicationComponent.java") assertFileExists("app/build/generated/source/kapt/debug/com/example/dagger/kotlin/ui/HomeActivity_MembersInjector.java") - assertFileExists("app/build/intermediates/classes/debug/com/example/dagger/kotlin/DaggerApplicationComponent.class") + + val daggerJavaClassesDir = + if (isLegacyAndroidGradleVersion(androidGradlePluginVersion)) + "app/build/intermediates/classes/debug/com/example/dagger/kotlin/" + else + "app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/example/dagger/kotlin/" + + assertFileExists(daggerJavaClassesDir + "DaggerApplicationComponent.class") + assertFileExists("app/build/tmp/kotlin-classes/debug/com/example/dagger/kotlin/AndroidModule.class") if (isLegacyAndroidGradleVersion(androidGradlePluginVersion)) { // we don't copy classes with new AGP @@ -116,11 +133,40 @@ open class Kapt3AndroidIT : Kapt3BaseIT() { val project = Project("android-databinding", GRADLE_VERSION, directoryPrefix = "kapt2") val options = androidBuildOptions() + + if (!isLegacyAndroidGradleVersion(androidGradlePluginVersion)) { + project.setupWorkingDir() + + // With new AGP, there's no need in the Databinding kapt dependency: + project.gradleBuildScript("app").modify { + it.lines().filterNot { + it.contains("kapt \"com.android.databinding:compiler") + }.joinToString("\n") + } + + // Workaround for KT-24915 + project.gradleBuildScript("app").appendText( + "\n" + """ + afterEvaluate { + kaptDebugKotlin.dependsOn dataBindingExportFeaturePackageIdsDebug + } + """.trimIndent() + ) + } + project.build("assembleDebug", options = options) { assertSuccessful() assertKaptSuccessful() assertFileExists("app/build/generated/source/kapt/debug/com/example/databinding/BR.java") - assertFileExists("app/build/generated/source/kapt/debug/com/example/databinding/databinding/ActivityTestBinding.java") + + if (isLegacyAndroidGradleVersion(androidGradlePluginVersion)) { + assertFileExists("app/build/generated/source/kapt/debug/com/example/databinding/databinding/ActivityTestBinding.java") + } else { + assertFileExists("app/build/generated/source/kapt/debug/com/example/databinding/databinding/ActivityTestBindingImpl.java") + } + + // KT-23866 + assertNotContains("The following options were not recognized by any processor") } } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt index cf1fe9af46b..8cc8e63cb64 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt @@ -21,10 +21,10 @@ import org.gradle.process.CommandLineArgumentProvider 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.getKaptGeneratedSourcesDir -import org.jetbrains.kotlin.gradle.tasks.isWorkerAPISupported import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.jetbrains.kotlin.gradle.tasks.isWorkerAPISupported import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast import java.io.ByteArrayOutputStream import java.io.File @@ -261,18 +261,26 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { val apOptionsFromProviders = if (isGradleVersionAtLeast(4, 6)) kaptVariantData?.annotationProcessorOptionProviders - ?.flatMap { (it as CommandLineArgumentProvider).asArguments().map { "" to it.removePrefix("-A") } } + ?.flatMap { (it as CommandLineArgumentProvider).asArguments() } .orEmpty() else emptyList() + val subluginOptionsFromProvidedApOptions = apOptionsFromProviders.map { + // Use the internal subplugin option type to exclude them from Gradle input/output checks, as their providers are already + // properly registered as a nested input: + + // Pass options as they are in the key-only form (key = 'a=b'), kapt will deal with them: + InternalSubpluginOption(key = it.removePrefix("-A"), value = "") + } + val apOptionsPairsList: List> = kaptExtension.getAdditionalArguments(project, kaptVariantData?.variantData, androidPlugin).toList() + - androidOptions.toList() + - apOptionsFromProviders + androidOptions.toList() return apOptionsPairsList.map { SubpluginOption(it.first, it.second) } + - FilesSubpluginOption(KAPT_KOTLIN_GENERATED, listOf(kotlinSourcesOutputDir)) + FilesSubpluginOption(KAPT_KOTLIN_GENERATED, listOf(kotlinSourcesOutputDir)) + + subluginOptionsFromProvidedApOptions } private fun Kapt3SubpluginContext.buildAndAddOptionsTo(task: Task, container: CompilerPluginOptions, aptMode: String) { @@ -350,16 +358,16 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { kaptTask.kaptClasspathConfigurations = kaptClasspathConfigurations - if (kaptTask is KaptWithKotlincTask) { - kaptVariantData?.annotationProcessorOptionProviders?.let { - kaptTask.annotationProcessorOptionProviders.add(it) - } + kaptVariantData?.annotationProcessorOptionProviders?.let { + kaptTask.annotationProcessorOptionProviders.add(it) + } + if (kaptTask is KaptWithKotlincTask) { buildAndAddOptionsTo(kaptTask, kaptTask.pluginOptions, aptMode = "apt") } if (kaptTask is KaptWithoutKotlincTask) { - project.configurations.create(KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME).apply { + project.configurations.maybeCreate(KAPT_WORKER_DEPENDENCIES_CONFIGURATION_NAME).apply { project.getKotlinPluginVersion()?.let { kotlinPluginVersion -> val kaptDependency = getPluginArtifact().run { "$groupId:$artifactId:$kotlinPluginVersion" } dependencies.add(project.dependencies.create(kaptDependency)) 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 64353edba9c..8a4e8640d0c 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 @@ -16,17 +16,22 @@ 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.* +import org.gradle.api.tasks.CompileClasspathNormalizer +import org.gradle.api.tasks.Delete +import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.SourceSetOutput import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.api.tasks.compile.JavaCompile import org.gradle.jvm.tasks.Jar import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsImpl -import org.jetbrains.kotlin.gradle.internal.* import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin.Companion.getKaptGeneratedClassesDir +import org.jetbrains.kotlin.gradle.internal.Kapt3KotlinGradleSubplugin +import org.jetbrains.kotlin.gradle.internal.KaptVariantData +import org.jetbrains.kotlin.gradle.internal.checkAndroidAnnotationProcessorDependencyUsage +import org.jetbrains.kotlin.gradle.scripting.internal.ScriptingGradleSubplugin import org.jetbrains.kotlin.gradle.tasks.* import org.jetbrains.kotlin.gradle.utils.* -import org.jetbrains.kotlin.gradle.scripting.internal.ScriptingGradleSubplugin import java.io.File import java.net.URL import java.util.* @@ -722,6 +727,8 @@ internal fun Task.registerSubpluginOptionsAsInputs(subpluginId: String, subplugi optionsGroup.forEachIndexed { index, option -> val indexSuffix = if (optionsGroup.size > 1) ".$index" else "" when (option) { + is InternalSubpluginOption -> Unit + is CompositeSubpluginOption -> { val subpluginIdWithWrapperKey = "$subpluginId.${optionKey}$indexSuffix" registerSubpluginOptionsAsInputs(subpluginIdWithWrapperKey, option.originalOptions)