From f15f90a7199422e3c3898cb3fd4b1999fa0384da Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Tue, 4 Oct 2016 20:20:01 +0300 Subject: [PATCH] Kapt: Support apt options (KT-13984) (cherry picked from commit b566a37) --- .../org/jetbrains/kotlin/gradle/Kapt2IT.kt | 16 +++++++- .../testProject/kapt2/arguments/build.gradle | 27 ++++++++++++++ .../kapt2/arguments/gradle.properties | 1 + .../kapt2/arguments/src/main/java/test.kt | 12 ++++++ .../internal/AnnotationProcessingManager.kt | 2 +- .../internal/Kapt2KotlinGradleSubplugin.kt | 9 +++++ .../kotlin/gradle/plugin/KaptExtension.kt | 33 +++++++++++------ .../AnnotationProcessingExtension.kt | 7 ++-- .../processing/AnnotationProcessingPlugin.kt | 37 ++++++++++++++----- .../test/processor/AbstractProcessorTest.kt | 5 ++- 10 files changed, 123 insertions(+), 26 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/arguments/build.gradle create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/arguments/gradle.properties create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/arguments/src/main/java/test.kt diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt2IT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt2IT.kt index 7f51f0c1285..ec411e6063a 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt2IT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt2IT.kt @@ -25,6 +25,8 @@ class Kapt2IT: BaseGradleIT() { private const val GRADLE_VERSION = "2.10" private const val GRADLE_2_14_VERSION = "2.14.1" 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() = @@ -35,8 +37,9 @@ class Kapt2IT: BaseGradleIT() { override fun defaultBuildOptions(): BuildOptions = super.defaultBuildOptions().copy(withDaemon = true) + private fun CompiledProject.assertKaptSuccessful() { - assertContains("Kapt: Annotation processing complete, 0 errors, 0 warnings") + KAPT_SUCCESSFUL_REGEX.findAll(this.output).single() } @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 fun testInheritedAnnotations() { Project("inheritedAnnotations", GRADLE_VERSION, directoryPrefix = "kapt2").build("build") { diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/arguments/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/arguments/build.gradle new file mode 100644 index 00000000000..48783e7f5a9 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/arguments/build.gradle @@ -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") + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/arguments/gradle.properties b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/arguments/gradle.properties new file mode 100644 index 00000000000..dc3793168f1 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/arguments/gradle.properties @@ -0,0 +1 @@ +kapt.verbose=true \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/arguments/src/main/java/test.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/arguments/src/main/java/test.kt new file mode 100644 index 00000000000..3a0a9b24793 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kapt2/arguments/src/main/java/test.kt @@ -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 + +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AnnotationProcessingManager.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AnnotationProcessingManager.kt index 608155fb439..0c2863f4def 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AnnotationProcessingManager.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AnnotationProcessingManager.kt @@ -201,7 +201,7 @@ class AnnotationProcessingManager( private fun appendAdditionalComplerArgs() { 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 javaTask.modifyCompilerArguments { list -> diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/Kapt2KotlinGradleSubplugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/Kapt2KotlinGradleSubplugin.kt index 874784bc2af..e536bd6feb8 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/Kapt2KotlinGradleSubplugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/Kapt2KotlinGradleSubplugin.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.gradle.internal +import com.android.build.gradle.BaseExtension import com.android.build.gradle.api.AndroidSourceSet import com.android.build.gradle.internal.variant.BaseVariantData import com.android.builder.model.SourceProvider @@ -124,6 +125,14 @@ class Kapt2KotlinGradleSubplugin : KotlinGradleSubplugin { 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") kotlinCompile.sourceAnnotationsRegistry = SourceAnnotationsRegistry(annotationsFile) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KaptExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KaptExtension.kt index 79a7aaefebb..0eec4bf54ea 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KaptExtension.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KaptExtension.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.gradle.plugin import groovy.lang.Closure import org.gradle.api.Project +import java.util.* open class KaptExtension { @@ -31,26 +32,36 @@ open class KaptExtension { this.closure = closure } - fun getAdditionalArguments(project: Project, variant: Any?, android: Any?): List { - val closureToExecute = closure ?: return emptyList() + fun getAdditionalArguments(project: Project, variantData: Any?, androidExtension: Any?): Map { + val closureToExecute = closure ?: return emptyMap() - val executor = KaptAdditionalArgumentsDelegate(project, variant, android) + val executor = KaptAdditionalArgumentsDelegate(project, variantData, androidExtension) executor.execute(closureToExecute) - return executor.additionalCompilerArgs + return executor.args + } + + fun getAdditionalArgumentsForJavac(project: Project, variantData: Any?, androidExtension: Any?): List { + val javacArgs = mutableListOf() + 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 val project: Project, - open val variant: Any?, - open val android: Any? + @Suppress("unused") open val project: Project, + @Suppress("unused") open val variant: Any?, + @Suppress("unused") open val android: Any? ) { + internal val args = LinkedHashMap() - val additionalCompilerArgs = arrayListOf() - + @Suppress("unused") open fun arg(name: Any, vararg values: Any) { - val valuesString = if (values.isNotEmpty()) values.joinToString(" ", prefix = "=") else "" - additionalCompilerArgs.add("-A$name$valuesString") + args.put(name.toString(), values.joinToString(" ")) } fun execute(closure: Closure<*>) { diff --git a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingExtension.kt b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingExtension.kt index 55d203a2fd1..2e52cbda7b4 100755 --- a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingExtension.kt +++ b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingExtension.kt @@ -45,10 +45,10 @@ import java.net.URLClassLoader import java.util.* import javax.annotation.processing.Processor import javax.tools.Diagnostic -import kotlin.system.measureTimeMillis class ClasspathBasedAnnotationProcessingExtension( val annotationProcessingClasspath: List, + override val options: Map, generatedSourcesOutputDir: File, classesOutputDir: File, javaSourceRoots: List, @@ -75,7 +75,7 @@ abstract class AbstractAnnotationProcessingExtension( private companion object { val LINE_SEPARATOR = System.getProperty("line.separator") ?: "\n" } - + private var annotationProcessingComplete = false private val messager = KotlinMessager() @@ -121,7 +121,7 @@ abstract class AbstractAnnotationProcessingExtension( val javaPsiFacade = JavaPsiFacade.getInstance(project) val projectScope = GlobalSearchScope.projectScope(project) - val options = emptyMap() + val options = this.options log { "Options: $options" } val filer = KotlinFiler(generatedSourcesOutputDir, classesOutputDir) @@ -324,6 +324,7 @@ abstract class AbstractAnnotationProcessingExtension( } protected abstract fun loadAnnotationProcessors(): List + protected abstract val options: Map } private class ProcessingResult(val errorCount: Int, val warningCount: Int, val wasAnythingGenerated: Boolean) diff --git a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingPlugin.kt b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingPlugin.kt index ebc1b716ede..20b3e65d7d7 100755 --- a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingPlugin.kt +++ b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingPlugin.kt @@ -35,6 +35,9 @@ import org.jetbrains.kotlin.java.model.internal.JeElementRegistry import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension 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 { val GENERATED_OUTPUT_DIR: CompilerConfigurationKey = CompilerConfigurationKey.create("generated files output directory") @@ -45,6 +48,9 @@ object AnnotationProcessingConfigurationKeys { val ANNOTATION_PROCESSOR_CLASSPATH: CompilerConfigurationKey> = CompilerConfigurationKey.create>("annotation processor classpath") + val APT_OPTIONS: CompilerConfigurationKey> = + CompilerConfigurationKey.create>("annotation processing options") + val INCREMENTAL_DATA_FILE: CompilerConfigurationKey = CompilerConfigurationKey.create("data file for incremental compilation support") @@ -66,6 +72,10 @@ class AnnotationProcessingCommandLineProcessor : CommandLineProcessor { CliOption("apclasspath", "", "Annotation processor classpath", required = false, allowMultipleOccurrences = true) + val APT_OPTIONS_OPTION: CliOption = + CliOption("apoption", ":", "Annotation processor option", + required = false, allowMultipleOccurrences = true) + val INCREMENTAL_DATA_FILE_OPTION: CliOption = CliOption("incrementalData", "", "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 pluginOptions: Collection = - 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) + private fun CompilerConfiguration.appendList(option: CompilerConfigurationKey>, value: T) { + val paths = getList(option).toMutableList() + paths.add(value) + put(option, paths) + } + override fun processOption(option: CliOption, value: String, configuration: CompilerConfiguration) { when (option) { - ANNOTATION_PROCESSOR_CLASSPATH_OPTION -> { - val paths = configuration.getList(AnnotationProcessingConfigurationKeys.ANNOTATION_PROCESSOR_CLASSPATH).toMutableList() - paths.add(value) - configuration.put(AnnotationProcessingConfigurationKeys.ANNOTATION_PROCESSOR_CLASSPATH, paths) - } + ANNOTATION_PROCESSOR_CLASSPATH_OPTION -> configuration.appendList(ANNOTATION_PROCESSOR_CLASSPATH, value) + APT_OPTIONS_OPTION -> configuration.appendList(APT_OPTIONS, 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) INCREMENTAL_DATA_FILE_OPTION -> configuration.put(AnnotationProcessingConfigurationKeys.INCREMENTAL_DATA_FILE, value) @@ -98,8 +111,14 @@ class AnnotationProcessingCommandLineProcessor : CommandLineProcessor { class AnnotationProcessingComponentRegistrar : ComponentRegistrar { override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) { 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 generatedOutputDirFile = File(generatedOutputDir) @@ -126,7 +145,7 @@ class AnnotationProcessingComponentRegistrar : ComponentRegistrar { val sourceRetentionAnnotationHandler = configuration[JVMConfigurationKeys.SOURCE_RETENTION_ANNOTATION_HANDLER] val annotationProcessingExtension = ClasspathBasedAnnotationProcessingExtension( - classpath, generatedOutputDirFile, classesOutputDir, javaRoots, verboseOutput, + classpath, apOptions, generatedOutputDirFile, classesOutputDir, javaRoots, verboseOutput, incrementalDataFile, sourceRetentionAnnotationHandler) project.registerService(JeElementRegistry::class.java, JeElementRegistry()) diff --git a/plugins/plugins-tests/tests/org/jetbrains/kotlin/annotation/processing/test/processor/AbstractProcessorTest.kt b/plugins/plugins-tests/tests/org/jetbrains/kotlin/annotation/processing/test/processor/AbstractProcessorTest.kt index 182ebe303cf..389b4a69928 100755 --- a/plugins/plugins-tests/tests/org/jetbrains/kotlin/annotation/processing/test/processor/AbstractProcessorTest.kt +++ b/plugins/plugins-tests/tests/org/jetbrains/kotlin/annotation/processing/test/processor/AbstractProcessorTest.kt @@ -46,7 +46,10 @@ class AnnotationProcessingExtensionForTests( ) : AbstractAnnotationProcessingExtension(createTempDir(), createTempDir(), listOf(), true, createIncrementalDataFile(), SourceRetentionAnnotationHandlerImpl()) { override fun loadAnnotationProcessors() = processors - + + override val options: Map + get() = emptyMap() + private companion object { fun createTempDir(): File = Files.createTempDirectory("ap-test").toFile().apply { deleteOnExit()