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 2f90bb404e0..93ee90b557d 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 @@ -16,7 +16,68 @@ class KotlinAndroidGradleIT : class KotlinAndroidWithJackGradleIT : AbstractKotlinAndroidWithJackGradleTests(androidGradlePluginVersion = "2.3.+") // TODO If we there is a way to fetch the latest Android plugin version, test against the latest version -class KotlinAndroid32GradleIT : KotlinAndroid3GradleIT(GradleVersionRequired.AtLeast("4.5"), "3.2.0-alpha06") +class KotlinAndroid32GradleIT : KotlinAndroid3GradleIT(GradleVersionRequired.AtLeast("4.6"), "3.2.0-alpha15") { + + @Test + fun testKaptUsingApOptionProvidersAsNestedInputOutput() = with(Project("AndroidProject", gradleVersion)) { + setupWorkingDir() + + gradleBuildScript(subproject = "Android").appendText( + """ + + apply plugin: 'kotlin-kapt' + + class MyNested implements org.gradle.process.CommandLineArgumentProvider { + String value = "" + + @InputFile + File inputFile = null + + @Override + Iterable asArguments() { return [value] } + } + + def nested = new MyNested() + nested.value = '123' + nested.inputFile = file("${'$'}projectDir/in.txt") + + android.applicationVariants.all { + it.javaCompileOptions.annotationProcessorOptions.compilerArgumentProviders.add(nested) + } + """.trimIndent() + ) + + File(projectDir, "Android/in.txt").appendText("1234") + + val kaptTasks = listOf(":Android:kaptFlavor1DebugKotlin") + val javacTasks = listOf(":Android:compileFlavor1DebugJavaWithJavac") + + val buildTasks = (kaptTasks + javacTasks).toTypedArray() + + build(*buildTasks) { + assertSuccessful() + assertTasksExecuted(kaptTasks + javacTasks) + } + + File(projectDir, "Android/in.txt").appendText("5678") + + build(*buildTasks) { + assertSuccessful() + assertTasksExecuted(kaptTasks) + assertTasksUpToDate(javacTasks) + } + + gradleBuildScript(subproject = "Android").modify { + it.replace("nested.value = '123'", "nested.value = '456'") + } + + build(*buildTasks) { + assertSuccessful() + assertTasksExecuted(kaptTasks) + assertTasksUpToDate(javacTasks) + } + } +} class KotlinAndroid30GradleIT : KotlinAndroid3GradleIT(GradleVersionRequired.AtLeast("4.1"), "3.0.0") diff --git a/libraries/tools/kotlin-gradle-plugin/src/agp25/kotlin/org/jetbrains/kotlin/gradle/plugin/android/Android25ProjectHandler.kt b/libraries/tools/kotlin-gradle-plugin/src/agp25/kotlin/org/jetbrains/kotlin/gradle/plugin/android/Android25ProjectHandler.kt index e17196f9e73..d6af7166641 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/agp25/kotlin/org/jetbrains/kotlin/gradle/plugin/android/Android25ProjectHandler.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/agp25/kotlin/org/jetbrains/kotlin/gradle/plugin/android/Android25ProjectHandler.kt @@ -128,6 +128,15 @@ class Android25ProjectHandler(kotlinConfigurationTools: KotlinConfigurationTools variantData.registerExternalAptJavaOutput(kaptSourceOutput) variantData.dataBindingDependencyArtifactsIfSupported?.let { kaptTask.dependsOn(it) } } + + override val annotationProcessorOptionProviders: List<*> + get() = try { + // Public API added in Android Gradle Plugin 3.2.0-alpha15: + val apOptions = variantData.javaCompileOptions.annotationProcessorOptions + apOptions.javaClass.getMethod("getCompilerArgumentProviders").invoke(apOptions) as List<*> + } catch (e: NoSuchMethodException) { + emptyList() + } } //TODO A public API is expected for this purpose. Once it is available, use the public API 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 46b58d33d06..a35f5d68cea 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 @@ -28,14 +28,16 @@ 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.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.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.utils.isGradleVersionAtLeast import java.io.ByteArrayOutputStream +import java.io.File import java.io.ObjectOutputStream import java.util.* @@ -69,6 +71,9 @@ abstract class KaptVariantData(val variantData: T) { project: Project, kaptTask: KaptTask, javaTask: AbstractCompile) + + open val annotationProcessorOptionProviders: List<*> + get() = emptyList() } // Subplugin for the Kotlin Gradle plugin @@ -226,9 +231,20 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { kotlinSourcesOutputDir.mkdirs() - val apOptions = - (kaptExtension.getAdditionalArguments(project, kaptVariantData?.variantData, androidPlugin) + androidOptions) - .map { SubpluginOption(it.key, it.value) } + + val apOptionsFromProviders = + if (isGradleVersionAtLeast(4, 6)) + kaptVariantData?.annotationProcessorOptionProviders + ?.flatMap { (it as CommandLineArgumentProvider).asArguments().map { "" to it.removePrefix("-A") } } + .orEmpty() + else + emptyList() + + val apOptionsPairsList: List> = + kaptExtension.getAdditionalArguments(project, kaptVariantData?.variantData, androidPlugin).toList() + + androidOptions.toList() + + apOptionsFromProviders + + val apOptions = apOptionsPairsList.map { SubpluginOption(it.first, it.second) } + FilesSubpluginOption("kapt.kotlin.generated", listOf(kotlinSourcesOutputDir)) pluginOptions += CompositeSubpluginOption("apoptions", encodeList(apOptions.associate { it.key to it.value }), apOptions) @@ -312,13 +328,16 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { if (kaptVariantData != null) { kaptVariantData.registerGeneratedJavaSource(project, kaptTask, javaCompile) - } - else { + } else { registerGeneratedJavaSource(kaptTask, javaCompile) } kaptTask.kaptClasspathConfigurations = kaptClasspathConfigurations + kaptVariantData?.annotationProcessorOptionProviders?.let { + kaptTask.annotationProcessorOptionProviders.add(it) + } + buildAndAddOptionsTo(kaptTask, kaptTask.pluginOptions, aptMode = "apt") return kaptTask @@ -366,6 +385,15 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin { newCompilerArgs.add("-proc:none") @Suppress("UNCHECKED_CAST") options.compilerArgs = newCompilerArgs as List + + if (isGradleVersionAtLeast(4, 6)) { + // Filter out the argument providers that are related to annotation processing and therefore already used by Kapt. + // This is done to avoid outputs intersections between Kapt and and javaCompile and make the up-to-date check for + // javaCompile more granular as it does not perform annotation processing: + if (kaptVariantData != null) { + options.compilerArgumentProviders.removeAll(kaptVariantData.annotationProcessorOptionProviders) + } + } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/KaptTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/KaptTask.kt index 7141fbcd4d5..dcf08af9929 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/KaptTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/KaptTask.kt @@ -56,6 +56,9 @@ open class KaptTask : ConventionTask(), CompilerArgumentAwareWithInput = mutableListOf() + override fun createCompilerArgs(): K2JVMCompilerArguments = K2JVMCompilerArguments() override fun setupCompilerArgs(args: K2JVMCompilerArguments, defaultsOnly: Boolean) {