Kapt: Support apt options (KT-13984)

(cherry picked from commit b566a37)
This commit is contained in:
Yan Zhulanow
2016-10-04 20:20:01 +03:00
committed by Yan Zhulanow
parent 0ae9a7a9d1
commit f15f90a719
10 changed files with 123 additions and 26 deletions
@@ -25,6 +25,8 @@ class Kapt2IT: BaseGradleIT() {
private const val GRADLE_VERSION = "2.10" private const val GRADLE_VERSION = "2.10"
private const val GRADLE_2_14_VERSION = "2.14.1" private const val GRADLE_2_14_VERSION = "2.14.1"
private const val ANDROID_GRADLE_PLUGIN_VERSION = "1.5.+" private const val ANDROID_GRADLE_PLUGIN_VERSION = "1.5.+"
private val KAPT_SUCCESSFUL_REGEX = "Annotation processing complete in [0-9]+ ms, 0 errors, 0 warnings".toRegex()
} }
private fun androidBuildOptions() = private fun androidBuildOptions() =
@@ -35,8 +37,9 @@ class Kapt2IT: BaseGradleIT() {
override fun defaultBuildOptions(): BuildOptions = override fun defaultBuildOptions(): BuildOptions =
super.defaultBuildOptions().copy(withDaemon = true) super.defaultBuildOptions().copy(withDaemon = true)
private fun CompiledProject.assertKaptSuccessful() { private fun CompiledProject.assertKaptSuccessful() {
assertContains("Kapt: Annotation processing complete, 0 errors, 0 warnings") KAPT_SUCCESSFUL_REGEX.findAll(this.output).single()
} }
@Test @Test
@@ -98,6 +101,17 @@ class Kapt2IT: BaseGradleIT() {
} }
} }
@Test
fun testArguments() {
Project("arguments", GRADLE_VERSION, directoryPrefix = "kapt2").build("build") {
assertSuccessful()
assertKaptSuccessful()
assertFileExists("build/generated/source/kapt2/main/example/TestClassCustomized.java")
assertFileExists("build/classes/main/example/TestClass.class")
assertFileExists("build/classes/main/example/TestClassCustomized.class")
}
}
@Test @Test
fun testInheritedAnnotations() { fun testInheritedAnnotations() {
Project("inheritedAnnotations", GRADLE_VERSION, directoryPrefix = "kapt2").build("build") { Project("inheritedAnnotations", GRADLE_VERSION, directoryPrefix = "kapt2").build("build") {
@@ -0,0 +1,27 @@
buildscript {
repositories {
mavenLocal()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: "kotlin"
apply plugin: "kotlin-kapt"
repositories {
mavenLocal()
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "org.jetbrains.kotlin:annotation-processor-example:$kotlin_version"
kapt "org.jetbrains.kotlin:annotation-processor-example:$kotlin_version"
}
kapt {
arguments {
arg("suffix", "Customized")
}
}
@@ -0,0 +1,12 @@
package example
@example.ExampleAnnotation
public class TestClass {
@example.ExampleAnnotation
public val testVal: String = "text"
@example.ExampleAnnotation
public fun testFunction(): Class<*> = TestClassCustomized::class.java
}
@@ -201,7 +201,7 @@ class AnnotationProcessingManager(
private fun appendAdditionalComplerArgs() { private fun appendAdditionalComplerArgs() {
val kaptExtension = project.extensions.getByType(KaptExtension::class.java) val kaptExtension = project.extensions.getByType(KaptExtension::class.java)
val args = kaptExtension.getAdditionalArguments(project, androidVariant, getAndroidExtension()) val args = kaptExtension.getAdditionalArgumentsForJavac(project, androidVariant, getAndroidExtension())
if (args.isEmpty()) return if (args.isEmpty()) return
javaTask.modifyCompilerArguments { list -> javaTask.modifyCompilerArguments { list ->
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.gradle.internal package org.jetbrains.kotlin.gradle.internal
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.api.AndroidSourceSet import com.android.build.gradle.api.AndroidSourceSet
import com.android.build.gradle.internal.variant.BaseVariantData import com.android.build.gradle.internal.variant.BaseVariantData
import com.android.builder.model.SourceProvider import com.android.builder.model.SourceProvider
@@ -124,6 +125,14 @@ class Kapt2KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
pluginOptions += SubpluginOption("verbose", "true") pluginOptions += SubpluginOption("verbose", "true")
} }
val androidPlugin = variantData?.let {
project.extensions.findByName("android") as? BaseExtension
}
for ((key, value) in kaptExtension.getAdditionalArguments(project, variantData, androidPlugin)) {
pluginOptions += SubpluginOption("apoption", "$key:$value")
}
val annotationsFile = File(kotlinCompile.taskBuildDirectory, "source-annotations.txt") val annotationsFile = File(kotlinCompile.taskBuildDirectory, "source-annotations.txt")
kotlinCompile.sourceAnnotationsRegistry = SourceAnnotationsRegistry(annotationsFile) kotlinCompile.sourceAnnotationsRegistry = SourceAnnotationsRegistry(annotationsFile)
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.gradle.plugin
import groovy.lang.Closure import groovy.lang.Closure
import org.gradle.api.Project import org.gradle.api.Project
import java.util.*
open class KaptExtension { open class KaptExtension {
@@ -31,26 +32,36 @@ open class KaptExtension {
this.closure = closure this.closure = closure
} }
fun getAdditionalArguments(project: Project, variant: Any?, android: Any?): List<String> { fun getAdditionalArguments(project: Project, variantData: Any?, androidExtension: Any?): Map<String, String> {
val closureToExecute = closure ?: return emptyList() val closureToExecute = closure ?: return emptyMap()
val executor = KaptAdditionalArgumentsDelegate(project, variant, android) val executor = KaptAdditionalArgumentsDelegate(project, variantData, androidExtension)
executor.execute(closureToExecute) executor.execute(closureToExecute)
return executor.additionalCompilerArgs return executor.args
}
fun getAdditionalArgumentsForJavac(project: Project, variantData: Any?, androidExtension: Any?): List<String> {
val javacArgs = mutableListOf<String>()
for ((key, value) in getAdditionalArguments(project, variantData, androidExtension)) {
javacArgs += "-A" + key + (if (value.isNotEmpty()) "=$value" else "")
}
return javacArgs
} }
} }
/**
* [project], [variant] and [android] properties are intended to be used inside the closure.
*/
open class KaptAdditionalArgumentsDelegate( open class KaptAdditionalArgumentsDelegate(
open val project: Project, @Suppress("unused") open val project: Project,
open val variant: Any?, @Suppress("unused") open val variant: Any?,
open val android: Any? @Suppress("unused") open val android: Any?
) { ) {
internal val args = LinkedHashMap<String, String>()
val additionalCompilerArgs = arrayListOf<String>() @Suppress("unused")
open fun arg(name: Any, vararg values: Any) { open fun arg(name: Any, vararg values: Any) {
val valuesString = if (values.isNotEmpty()) values.joinToString(" ", prefix = "=") else "" args.put(name.toString(), values.joinToString(" "))
additionalCompilerArgs.add("-A$name$valuesString")
} }
fun execute(closure: Closure<*>) { fun execute(closure: Closure<*>) {
@@ -45,10 +45,10 @@ import java.net.URLClassLoader
import java.util.* import java.util.*
import javax.annotation.processing.Processor import javax.annotation.processing.Processor
import javax.tools.Diagnostic import javax.tools.Diagnostic
import kotlin.system.measureTimeMillis
class ClasspathBasedAnnotationProcessingExtension( class ClasspathBasedAnnotationProcessingExtension(
val annotationProcessingClasspath: List<File>, val annotationProcessingClasspath: List<File>,
override val options: Map<String, String>,
generatedSourcesOutputDir: File, generatedSourcesOutputDir: File,
classesOutputDir: File, classesOutputDir: File,
javaSourceRoots: List<File>, javaSourceRoots: List<File>,
@@ -75,7 +75,7 @@ abstract class AbstractAnnotationProcessingExtension(
private companion object { private companion object {
val LINE_SEPARATOR = System.getProperty("line.separator") ?: "\n" val LINE_SEPARATOR = System.getProperty("line.separator") ?: "\n"
} }
private var annotationProcessingComplete = false private var annotationProcessingComplete = false
private val messager = KotlinMessager() private val messager = KotlinMessager()
@@ -121,7 +121,7 @@ abstract class AbstractAnnotationProcessingExtension(
val javaPsiFacade = JavaPsiFacade.getInstance(project) val javaPsiFacade = JavaPsiFacade.getInstance(project)
val projectScope = GlobalSearchScope.projectScope(project) val projectScope = GlobalSearchScope.projectScope(project)
val options = emptyMap<String, String>() val options = this.options
log { "Options: $options" } log { "Options: $options" }
val filer = KotlinFiler(generatedSourcesOutputDir, classesOutputDir) val filer = KotlinFiler(generatedSourcesOutputDir, classesOutputDir)
@@ -324,6 +324,7 @@ abstract class AbstractAnnotationProcessingExtension(
} }
protected abstract fun loadAnnotationProcessors(): List<Processor> protected abstract fun loadAnnotationProcessors(): List<Processor>
protected abstract val options: Map<String, String>
} }
private class ProcessingResult(val errorCount: Int, val warningCount: Int, val wasAnythingGenerated: Boolean) private class ProcessingResult(val errorCount: Int, val warningCount: Int, val wasAnythingGenerated: Boolean)
@@ -35,6 +35,9 @@ import org.jetbrains.kotlin.java.model.internal.JeElementRegistry
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension
import java.io.File import java.io.File
import org.jetbrains.kotlin.annotation.processing.AnnotationProcessingConfigurationKeys.ANNOTATION_PROCESSOR_CLASSPATH
import org.jetbrains.kotlin.annotation.processing.AnnotationProcessingConfigurationKeys.APT_OPTIONS
object AnnotationProcessingConfigurationKeys { object AnnotationProcessingConfigurationKeys {
val GENERATED_OUTPUT_DIR: CompilerConfigurationKey<String> = val GENERATED_OUTPUT_DIR: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>("generated files output directory") CompilerConfigurationKey.create<String>("generated files output directory")
@@ -45,6 +48,9 @@ object AnnotationProcessingConfigurationKeys {
val ANNOTATION_PROCESSOR_CLASSPATH: CompilerConfigurationKey<List<String>> = val ANNOTATION_PROCESSOR_CLASSPATH: CompilerConfigurationKey<List<String>> =
CompilerConfigurationKey.create<List<String>>("annotation processor classpath") CompilerConfigurationKey.create<List<String>>("annotation processor classpath")
val APT_OPTIONS: CompilerConfigurationKey<List<String>> =
CompilerConfigurationKey.create<List<String>>("annotation processing options")
val INCREMENTAL_DATA_FILE: CompilerConfigurationKey<String> = val INCREMENTAL_DATA_FILE: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>("data file for incremental compilation support") CompilerConfigurationKey.create<String>("data file for incremental compilation support")
@@ -66,6 +72,10 @@ class AnnotationProcessingCommandLineProcessor : CommandLineProcessor {
CliOption("apclasspath", "<classpath>", "Annotation processor classpath", CliOption("apclasspath", "<classpath>", "Annotation processor classpath",
required = false, allowMultipleOccurrences = true) required = false, allowMultipleOccurrences = true)
val APT_OPTIONS_OPTION: CliOption =
CliOption("apoption", "<key>:<value>", "Annotation processor option",
required = false, allowMultipleOccurrences = true)
val INCREMENTAL_DATA_FILE_OPTION: CliOption = val INCREMENTAL_DATA_FILE_OPTION: CliOption =
CliOption("incrementalData", "<path>", "Location of the incremental data file", required = false) CliOption("incrementalData", "<path>", "Location of the incremental data file", required = false)
@@ -76,16 +86,19 @@ class AnnotationProcessingCommandLineProcessor : CommandLineProcessor {
override val pluginId: String = ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID override val pluginId: String = ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID
override val pluginOptions: Collection<CliOption> = override val pluginOptions: Collection<CliOption> =
listOf(GENERATED_OUTPUT_DIR_OPTION, ANNOTATION_PROCESSOR_CLASSPATH_OPTION, listOf(GENERATED_OUTPUT_DIR_OPTION, ANNOTATION_PROCESSOR_CLASSPATH_OPTION, APT_OPTIONS_OPTION,
CLASS_FILES_OUTPUT_DIR_OPTION, INCREMENTAL_DATA_FILE_OPTION, VERBOSE_MODE_OPTION) CLASS_FILES_OUTPUT_DIR_OPTION, INCREMENTAL_DATA_FILE_OPTION, VERBOSE_MODE_OPTION)
private fun <T> CompilerConfiguration.appendList(option: CompilerConfigurationKey<List<T>>, value: T) {
val paths = getList(option).toMutableList()
paths.add(value)
put(option, paths)
}
override fun processOption(option: CliOption, value: String, configuration: CompilerConfiguration) { override fun processOption(option: CliOption, value: String, configuration: CompilerConfiguration) {
when (option) { when (option) {
ANNOTATION_PROCESSOR_CLASSPATH_OPTION -> { ANNOTATION_PROCESSOR_CLASSPATH_OPTION -> configuration.appendList(ANNOTATION_PROCESSOR_CLASSPATH, value)
val paths = configuration.getList(AnnotationProcessingConfigurationKeys.ANNOTATION_PROCESSOR_CLASSPATH).toMutableList() APT_OPTIONS_OPTION -> configuration.appendList(APT_OPTIONS, value)
paths.add(value)
configuration.put(AnnotationProcessingConfigurationKeys.ANNOTATION_PROCESSOR_CLASSPATH, paths)
}
GENERATED_OUTPUT_DIR_OPTION -> configuration.put(AnnotationProcessingConfigurationKeys.GENERATED_OUTPUT_DIR, value) GENERATED_OUTPUT_DIR_OPTION -> configuration.put(AnnotationProcessingConfigurationKeys.GENERATED_OUTPUT_DIR, value)
CLASS_FILES_OUTPUT_DIR_OPTION -> configuration.put(AnnotationProcessingConfigurationKeys.CLASS_FILES_OUTPUT_DIR, value) CLASS_FILES_OUTPUT_DIR_OPTION -> configuration.put(AnnotationProcessingConfigurationKeys.CLASS_FILES_OUTPUT_DIR, value)
INCREMENTAL_DATA_FILE_OPTION -> configuration.put(AnnotationProcessingConfigurationKeys.INCREMENTAL_DATA_FILE, value) INCREMENTAL_DATA_FILE_OPTION -> configuration.put(AnnotationProcessingConfigurationKeys.INCREMENTAL_DATA_FILE, value)
@@ -98,8 +111,14 @@ class AnnotationProcessingCommandLineProcessor : CommandLineProcessor {
class AnnotationProcessingComponentRegistrar : ComponentRegistrar { class AnnotationProcessingComponentRegistrar : ComponentRegistrar {
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) { override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
val generatedOutputDir = configuration.get(AnnotationProcessingConfigurationKeys.GENERATED_OUTPUT_DIR) ?: return val generatedOutputDir = configuration.get(AnnotationProcessingConfigurationKeys.GENERATED_OUTPUT_DIR) ?: return
val apClasspath = configuration.get(AnnotationProcessingConfigurationKeys.ANNOTATION_PROCESSOR_CLASSPATH)?.map(::File) ?: return val apClasspath = configuration.get(ANNOTATION_PROCESSOR_CLASSPATH)?.map(::File) ?: return
val apOptions = (configuration.get(APT_OPTIONS) ?: listOf())
.map { it.split(':') }
.filter { it.size == 2 }
.map { it[0] to it[1] }
.toMap()
val incrementalDataFile = configuration.get(AnnotationProcessingConfigurationKeys.INCREMENTAL_DATA_FILE)?.let(::File) val incrementalDataFile = configuration.get(AnnotationProcessingConfigurationKeys.INCREMENTAL_DATA_FILE)?.let(::File)
val generatedOutputDirFile = File(generatedOutputDir) val generatedOutputDirFile = File(generatedOutputDir)
@@ -126,7 +145,7 @@ class AnnotationProcessingComponentRegistrar : ComponentRegistrar {
val sourceRetentionAnnotationHandler = configuration[JVMConfigurationKeys.SOURCE_RETENTION_ANNOTATION_HANDLER] val sourceRetentionAnnotationHandler = configuration[JVMConfigurationKeys.SOURCE_RETENTION_ANNOTATION_HANDLER]
val annotationProcessingExtension = ClasspathBasedAnnotationProcessingExtension( val annotationProcessingExtension = ClasspathBasedAnnotationProcessingExtension(
classpath, generatedOutputDirFile, classesOutputDir, javaRoots, verboseOutput, classpath, apOptions, generatedOutputDirFile, classesOutputDir, javaRoots, verboseOutput,
incrementalDataFile, sourceRetentionAnnotationHandler) incrementalDataFile, sourceRetentionAnnotationHandler)
project.registerService(JeElementRegistry::class.java, JeElementRegistry()) project.registerService(JeElementRegistry::class.java, JeElementRegistry())
@@ -46,7 +46,10 @@ class AnnotationProcessingExtensionForTests(
) : AbstractAnnotationProcessingExtension(createTempDir(), createTempDir(), listOf(), true, ) : AbstractAnnotationProcessingExtension(createTempDir(), createTempDir(), listOf(), true,
createIncrementalDataFile(), SourceRetentionAnnotationHandlerImpl()) { createIncrementalDataFile(), SourceRetentionAnnotationHandlerImpl()) {
override fun loadAnnotationProcessors() = processors override fun loadAnnotationProcessors() = processors
override val options: Map<String, String>
get() = emptyMap()
private companion object { private companion object {
fun createTempDir(): File = Files.createTempDirectory("ap-test").toFile().apply { fun createTempDir(): File = Files.createTempDirectory("ap-test").toFile().apply {
deleteOnExit() deleteOnExit()