From dbd72ae53b2f234b584ad501c11e4e035d1e1c94 Mon Sep 17 00:00:00 2001 From: Sergey Igushkin Date: Thu, 21 Jun 2018 23:42:51 +0300 Subject: [PATCH] Fix Android ap option providers as Kapt task nested inputs 1) Exclude the providers arguments from the kapt inputs, as the values may contain absolute paths, may be output properties etc. The providers should correctly annotate their inputs. 2) Fix the options passed to kapt as 'value=option' (leading to all the options collapse into one with the null key), use 'key=option' instead to make Kapt pass them as '-Aoption'. Issue #KT-23866 Fixed Issue #KT-25027 Fixed --- .../gradle/plugin/KotlinGradleSubplugin.kt | 3 + .../AbstractKotlinAndroidGradleTests.kt | 19 +++--- .../jetbrains/kotlin/gradle/Kapt3AndroidIT.kt | 58 +++++++++++++++++-- .../kapt/Kapt3KotlinGradleSubplugin.kt | 28 +++++---- .../kotlin/gradle/plugin/KotlinPlugin.kt | 13 ++++- 5 files changed, 94 insertions(+), 27 deletions(-) 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)