Add AP option providers of Android Gradle plugin as @Nested to Kapt

Also remove these option providers from the javac task compiler argument
providers so as to avoid output intersections of javac vs kapt.

The options generated by the providers are added to the Kapt subplugin
options as well in order to be passed to Kapt. Since these options
are a list, not a map, pass them without name (Kapt should decode them
into plain arguments). Remove the '-A' prefix as it is there for
command line annotation processor options, but we don't need it, it
is added by Kapt internally.

Issue #KT-23964 Fixed
Issue #KT-24420 Fixed
This commit is contained in:
Sergey Igushkin
2018-05-15 18:40:47 +03:00
parent 77429b1ca5
commit 571905824d
4 changed files with 111 additions and 10 deletions
@@ -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<String> 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")
@@ -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<Any>()
}
}
//TODO A public API is expected for this purpose. Once it is available, use the public API
@@ -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<T>(val variantData: T) {
project: Project,
kaptTask: KaptTask,
javaTask: AbstractCompile)
open val annotationProcessorOptionProviders: List<*>
get() = emptyList<Any>()
}
// Subplugin for the Kotlin Gradle plugin
@@ -226,9 +231,20 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
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<Pair<String, String>> =
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<KotlinCompile> {
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<KotlinCompile> {
newCompilerArgs.add("-proc:none")
@Suppress("UNCHECKED_CAST")
options.compilerArgs = newCompilerArgs as List<String>
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)
}
}
}
}
@@ -56,6 +56,9 @@ open class KaptTask : ConventionTask(), CompilerArgumentAwareWithInput<K2JVMComp
@get:OutputDirectory
lateinit var kotlinSourcesDestinationDir: File
@get:Nested
internal val annotationProcessorOptionProviders: MutableList<Any> = mutableListOf()
override fun createCompilerArgs(): K2JVMCompilerArguments = K2JVMCompilerArguments()
override fun setupCompilerArgs(args: K2JVMCompilerArguments, defaultsOnly: Boolean) {