Annotation collector compiler plugin

This commit is contained in:
Yan Zhulanow
2015-04-30 21:37:03 +03:00
parent c8aa6defb6
commit b2220ca98a
18 changed files with 455 additions and 7 deletions
+1
View File
@@ -42,6 +42,7 @@
<element id="module-output" name="serialization" />
<element id="module-output" name="idea-completion" />
<element id="module-output" name="idea-core" />
<element id="module-output" name="annotation-collector" />
</element>
<element id="library" level="project" name="javax.inject" />
<element id="directory" name="jps">
+1
View File
@@ -8,6 +8,7 @@
<module fileurl="file://$PROJECT_DIR$/plugins/android-jps-plugin/android-jps-plugin.iml" filepath="$PROJECT_DIR$/plugins/android-jps-plugin/android-jps-plugin.iml" group="plugins" />
<module fileurl="file://$PROJECT_DIR$/android-studio/android-studio.iml" filepath="$PROJECT_DIR$/android-studio/android-studio.iml" group="ide" />
<module fileurl="file://$PROJECT_DIR$/compiler/android-tests/android-tests.iml" filepath="$PROJECT_DIR$/compiler/android-tests/android-tests.iml" group="compiler" />
<module fileurl="file://$PROJECT_DIR$/plugins/annotation-collector/annotation-collector.iml" filepath="$PROJECT_DIR$/plugins/annotation-collector/annotation-collector.iml" group="plugins" />
<module fileurl="file://$PROJECT_DIR$/ant/ant.iml" filepath="$PROJECT_DIR$/ant/ant.iml" />
<module fileurl="file://$PROJECT_DIR$/compiler/backend/backend.iml" filepath="$PROJECT_DIR$/compiler/backend/backend.iml" group="compiler/java" />
<module fileurl="file://$PROJECT_DIR$/compiler/backend-common/backend-common.iml" filepath="$PROJECT_DIR$/compiler/backend-common/backend-common.iml" group="compiler" />
+3
View File
@@ -90,6 +90,7 @@
<include name="compiler/frontend.java/src"/>
<include name="compiler/light-classes/src"/>
<include name="compiler/plugin-api/src"/>
<include name="plugins/annotation-collector/src"/>
<include name="compiler/serialization/src"/>
<include name="compiler/util/src"/>
<include name="js/js.dart-ast/src"/>
@@ -116,6 +117,7 @@
<include name="util.runtime/**"/>
<include name="light-classes/**"/>
<include name="plugin-api/**"/>
<include name="annotation-collector/**"/>
<include name="builtins-serializer/**"/>
<include name="js.dart-ast/**"/>
<include name="js.translator/**"/>
@@ -200,6 +202,7 @@
<fileset dir="compiler/frontend.java/src"/>
<fileset dir="compiler/light-classes/src"/>
<fileset dir="compiler/plugin-api/src"/>
<fileset dir="plugins/annotation-collector/src"/>
<fileset dir="compiler/serialization/src"/>
<fileset dir="compiler/util/src"/>
<fileset dir="js/js.dart-ast/src"/>
@@ -49,6 +49,11 @@ public class ClassBuilderFactories {
public byte[] asBytes(ClassBuilder builder) {
throw new IllegalStateException();
}
@Override
public void close() {
throw new IllegalStateException();
}
};
@NotNull
@@ -79,6 +84,11 @@ public class ClassBuilderFactories {
public byte[] asBytes(ClassBuilder builder) {
return ((TraceBuilder) builder).binary.toByteArray();
}
@Override
public void close() {
}
};
@NotNull
@@ -105,6 +115,11 @@ public class ClassBuilderFactories {
ClassWriter visitor = (ClassWriter) builder.getVisitor();
return visitor.toByteArray();
}
@Override
public void close() {
}
};
private ClassBuilderFactories() {
@@ -29,4 +29,6 @@ public interface ClassBuilderFactory {
String asText(ClassBuilder builder);
byte[] asBytes(ClassBuilder builder);
void close();
}
@@ -47,6 +47,10 @@ public abstract class SignatureCollectingClassBuilderFactory(
return delegate.asText((builder as SignatureCollectingClassBuilder)._delegate)
}
public override fun close() {
delegate.close()
}
private inner class SignatureCollectingClassBuilder(
private val classCreatedFor: JvmDeclarationOrigin,
internal val _delegate: ClassBuilder
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.codegen.extensions
import org.jetbrains.kotlin.codegen.ClassBuilderFactory
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
import org.jetbrains.kotlin.resolve.BindingContext
public trait ClassBuilderInterceptorExtension {
companion object : ProjectExtensionDescriptor<ClassBuilderInterceptorExtension>(
"org.jetbrains.kotlin.classBuilderFactoryInterceptorExtension", javaClass<ClassBuilderInterceptorExtension>())
public fun interceptClassBuilderFactory(
interceptedFactory: ClassBuilderFactory,
bindingContext: BindingContext,
diagnostics: DiagnosticSink
): ClassBuilderFactory
}
@@ -50,4 +50,9 @@ public class OptimizationClassBuilderFactory implements ClassBuilderFactory {
public byte[] asBytes(ClassBuilder builder) {
return delegate.asBytes(((OptimizationClassBuilder) builder).getDelegate());
}
@Override
public void close() {
delegate.close();
}
}
@@ -21,6 +21,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.codegen.*;
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension;
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods;
import org.jetbrains.kotlin.codegen.optimization.OptimizationClassBuilderFactory;
import org.jetbrains.kotlin.codegen.when.MappingsClassesForWhenByEnum;
@@ -138,6 +139,9 @@ public class GenerationState {
@Nullable
private final File outDirectory; // TODO: temporary hack, see JetTypeMapperWithOutDirectory state for details
@NotNull
private final ClassBuilderFactory interceptedBuilderFactory;
public GenerationState(
@NotNull Project project,
@NotNull ClassBuilderFactory builderFactory,
@@ -187,9 +191,20 @@ public class GenerationState {
builderFactory = new OptimizationClassBuilderFactory(builderFactory);
}
ClassBuilderFactory interceptedBuilderFactory = new BuilderFactoryForDuplicateSignatureDiagnostics(
builderFactory, this.bindingContext, diagnostics);
Collection<ClassBuilderInterceptorExtension> interceptExtensions =
ClassBuilderInterceptorExtension.Companion.getInstances(project);
for (ClassBuilderInterceptorExtension extension : interceptExtensions) {
interceptedBuilderFactory = extension.interceptClassBuilderFactory(interceptedBuilderFactory, bindingContext, diagnostics);
}
this.interceptedBuilderFactory = interceptedBuilderFactory;
this.diagnostics = diagnostics;
this.classFileFactory = new ClassFileFactory(this, new BuilderFactoryForDuplicateSignatureDiagnostics(
builderFactory, this.bindingContext, diagnostics));
this.classFileFactory = new ClassFileFactory(this, interceptedBuilderFactory);
this.disableCallAssertions = disableCallAssertions;
this.disableParamAssertions = disableParamAssertions;
@@ -306,6 +321,7 @@ public class GenerationState {
}
public void destroy() {
interceptedBuilderFactory.close();
}
@Nullable
+1
View File
@@ -19,5 +19,6 @@
<orderEntry type="module" module-name="serialization" />
<orderEntry type="module" module-name="js.serializer" />
<orderEntry type="module" module-name="util" />
<orderEntry type="module" module-name="annotation-collector" />
</component>
</module>
@@ -21,6 +21,8 @@ import java.util.jar.JarFile
import java.util.jar.Attributes
import java.util.regex.Pattern
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.annotation.AnnotationCollectorCommandLineProcessor
import org.jetbrains.kotlin.annotation.AnnotationCollectorComponentRegistrar
import java.net.URLClassLoader
import java.net.URL
import java.io.File
@@ -45,10 +47,9 @@ public object PluginCliParser {
javaClass.getClassLoader()
)
configuration.addAll(
ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS,
ServiceLoader.load(javaClass<ComponentRegistrar>(), classLoader).toList()
)
val componentRegistrars = ServiceLoader.load(javaClass<ComponentRegistrar>(), classLoader).toArrayList()
componentRegistrars.add(AnnotationCollectorComponentRegistrar())
configuration.addAll(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, componentRegistrars)
processPluginOptions(arguments, configuration, classLoader)
}
@@ -63,7 +64,8 @@ public object PluginCliParser {
it.pluginId
} ?: mapOf()
val commandLineProcessors = ServiceLoader.load(javaClass<CommandLineProcessor>(), classLoader).toList()
val commandLineProcessors = ServiceLoader.load(javaClass<CommandLineProcessor>(), classLoader).toArrayList()
commandLineProcessors.add(AnnotationCollectorCommandLineProcessor())
for (processor in commandLineProcessors) {
val declaredOptions = processor.pluginOptions.valuesToMap { it.name }
@@ -63,6 +63,7 @@ import org.jetbrains.kotlin.cli.jvm.config.JVMConfigurationKeys
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
import org.jetbrains.kotlin.cli.jvm.config.JvmContentRoot
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.config.CommonConfigurationKeys
@@ -139,6 +140,7 @@ public class KotlinCoreEnvironment private(
ExternalDeclarationsProvider.registerExtensionPoint(project)
ExpressionCodegenExtension.registerExtensionPoint(project)
ClassBuilderInterceptorExtension.registerExtensionPoint(project)
for (registrar in configuration.getList(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS)) {
registrar.registerProjectComponents(project, configuration)
@@ -52,4 +52,9 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
public byte[] asBytes(ClassBuilder builder) {
throw new UnsupportedOperationException("asBytes is not implemented"); // TODO
}
@Override
public void close() {
}
}
+3
View File
@@ -20,5 +20,8 @@
<extensionPoint name="simpleNameReferenceExtension"
interface="org.jetbrains.kotlin.plugin.references.SimpleNameReferenceExtension"
area="IDEA_PROJECT"/>
<extensionPoint name="classBuilderInterceptorExtension"
interface="org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension"
area="IDEA_PROJECT"/>
</extensionPoints>
</idea-plugin>
@@ -5,5 +5,7 @@
<defaultErrorMessages implementation="org.jetbrains.kotlin.resolve.jvm.diagnostics.DefaultErrorMessagesJvm"/>
<suppressStringProvider implementation="org.jetbrains.kotlin.load.kotlin.nativeDeclarations.SuppressNoBodyErrorsForNativeDeclarations"/>
<classBuilderInterceptorExtension implementation="org.jetbrains.kotlin.annotation.AnnotationCollectorExtension"/>
</extensions>
</idea-plugin>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="plugin-api" />
<orderEntry type="module" module-name="backend" />
<orderEntry type="module" module-name="frontend" />
<orderEntry type="library" name="kotlin-runtime" level="project" />
<orderEntry type="module" module-name="frontend.java" />
</component>
</module>
@@ -0,0 +1,245 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.annotation
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.codegen.ClassBuilder
import org.jetbrains.kotlin.codegen.ClassBuilderFactory
import org.jetbrains.kotlin.codegen.DelegatingClassBuilder
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.org.objectweb.asm.*
import java.io.File
import java.io.IOException
import java.io.StringWriter
import java.io.Writer
import java.util.regex.Pattern
import java.util.regex.PatternSyntaxException
import kotlin.properties.Delegates
public abstract class AnnotationCollectorExtensionBase() : ClassBuilderInterceptorExtension {
private object RecordTypes {
val ANNOTATED_CLASS = "c"
val ANNOTATED_METHOD = "m"
val ANNOTATED_FIELD = "f"
val SHORTENED_ANNOTATION = "a"
val SHORTENED_PACKAGE_NAME = "p"
}
protected abstract val annotationFilterList: List<String>?
private val classBuilderFactories = arrayListOf<AnnotationCollectorClassBuilderFactory>()
private val shortenedAnnotationCache = ShortenedNameCache(RecordTypes.SHORTENED_ANNOTATION)
private val shortenedPackageNameCache = ShortenedNameCache(RecordTypes.SHORTENED_PACKAGE_NAME)
override fun interceptClassBuilderFactory(
interceptedFactory: ClassBuilderFactory,
bindingContext: BindingContext,
diagnostics: DiagnosticSink
): ClassBuilderFactory {
val factory = AnnotationCollectorClassBuilderFactory(interceptedFactory, getWriter(diagnostics), diagnostics)
classBuilderFactories.add(factory)
return factory
}
protected abstract fun getWriter(diagnostic: DiagnosticSink): Writer
private inner class AnnotationCollectorClassBuilderFactory(
private val delegateFactory: ClassBuilderFactory,
val writer: Writer,
val diagnostics: DiagnosticSink
) : ClassBuilderFactory {
override fun newClassBuilder(origin: JvmDeclarationOrigin): ClassBuilder {
return AnnotationCollectorClassBuilder(delegateFactory.newClassBuilder(origin), writer, diagnostics)
}
override fun getClassBuilderMode() = delegateFactory.getClassBuilderMode()
override fun asText(builder: ClassBuilder?): String? {
return delegateFactory.asText((builder as AnnotationCollectorClassBuilder).delegateClassBuilder)
}
override fun asBytes(builder: ClassBuilder?): ByteArray? {
return delegateFactory.asBytes((builder as AnnotationCollectorClassBuilder).delegateClassBuilder)
}
override fun close() {
for (factory in classBuilderFactories) {
factory.writer.close()
}
classBuilderFactories.clear()
delegateFactory.close()
}
}
private inner class AnnotationCollectorClassBuilder(
internal val delegateClassBuilder: ClassBuilder,
val writer: Writer,
val diagnostics: DiagnosticSink
) : DelegatingClassBuilder() {
private val annotationFilterEnabled: Boolean
private val annotationFilters: List<Pattern>
init {
val nullableAnnotations = annotationFilterList?.map { it.compilePatternOpt() } ?: listOf()
annotationFilterEnabled = nullableAnnotations.isNotEmpty()
annotationFilters = nullableAnnotations.filterNotNull()
}
private val classVisitor: ClassVisitor by Delegates.lazy {
object : ClassVisitor(Opcodes.ASM5, super.getVisitor()) {
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
recordAnnotation(null, RecordTypes.ANNOTATED_CLASS, desc)
return super.visitAnnotation(desc, visible)
}
}
}
private var currentClassSimpleName: String? = null
private var currentPackageName: String? = null
override fun getVisitor() = classVisitor
override fun getDelegate() = delegateClassBuilder
override fun defineClass(
origin: PsiElement?,
version: Int,
access: Int,
name: String,
signature: String?,
superName: String,
interfaces: Array<out String>
) {
currentClassSimpleName = name.substringAfterLast('/')
currentPackageName = name.substringBeforeLast('/', "").replace('/', '.')
super.defineClass(origin, version, access, name, signature, superName, interfaces)
}
override fun newField(
origin: JvmDeclarationOrigin,
access: Int,
name: String,
desc: String,
signature: String?,
value: Any?
): FieldVisitor {
return object : FieldVisitor(Opcodes.ASM5, super.newField(origin, access, name, desc, signature, value)) {
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
recordAnnotation(name, RecordTypes.ANNOTATED_FIELD, desc)
return super.visitAnnotation(desc, visible)
}
}
}
override fun newMethod(
origin: JvmDeclarationOrigin,
access: Int,
name: String,
desc: String,
signature: String?,
exceptions: Array<out String>?
): MethodVisitor {
return object : MethodVisitor(Opcodes.ASM5, super.newMethod(origin, access, name, desc, signature, exceptions)) {
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
recordAnnotation(name, RecordTypes.ANNOTATED_METHOD, desc)
return super.visitAnnotation(desc, visible)
}
}
}
private fun isAnnotationHandled(annotationFqName: String): Boolean {
return if (annotationFilterEnabled)
annotationFilters.any { it.matcher(annotationFqName).matches() }
else !annotationFqName.startsWith("kotlin.jvm.internal.") //apply to all
}
private fun recordAnnotation(name: String?, type: String, annotationDesc: String) {
val annotationFqName = Type.getType(annotationDesc).getClassName()
if (!isAnnotationHandled(annotationFqName)) return
try {
val annotationId = shortenedAnnotationCache.save(annotationFqName, writer)
val packageName = this.currentPackageName!!
val packageNameId = if (!packageName.isEmpty())
shortenedPackageNameCache.save(packageName, writer)
else null
val className = this.currentClassSimpleName!!
val outputClassName = if (packageNameId == null) className else "$packageNameId/$className"
val elementName = if (name != null) " $name" else ""
writer.write("$type $annotationId $outputClassName$elementName\n")
}
catch (e: IOException) {
throw e
}
}
private fun String.compilePatternOpt(): Pattern? {
return try {
Pattern.compile(this)
}
catch (e: PatternSyntaxException) {
null
}
}
}
private class ShortenedNameCache(val type: String) {
private val internalCache = hashMapOf<String, String>()
private var counter: Int = 0
fun save(name: String, writer: Writer): String {
return internalCache.getOrPut(name) {
val resultId = counter.toString()
writer.write("$type $name $resultId\n")
counter += 1
resultId
}
}
}
}
public class AnnotationCollectorExtension(
override val annotationFilterList: List<String>? = null,
val outputFilename: String? = null
) : AnnotationCollectorExtensionBase() {
override fun getWriter(diagnostic: DiagnosticSink): Writer {
try {
return with (File(outputFilename)) {
val parent = getParentFile()
if (!parent.exists()) parent.mkdirs()
bufferedWriter()
}
}
catch (e: IOException) {
throw e
}
}
}
@@ -0,0 +1,91 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.annotation
import com.intellij.mock.MockProject
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
import org.jetbrains.kotlin.compiler.plugin.CliOption
import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException
import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.CompilerConfigurationKey
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.MemberDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters1
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.JetCallableDeclaration
import org.jetbrains.kotlin.psi.JetDeclaration
import org.jetbrains.kotlin.resolve.DescriptorUtils
import java.io.BufferedWriter
import java.io.File
import java.io.IOException
import java.io.Writer
import java.util.regex.Pattern
import java.util.regex.PatternSyntaxException
public object AnnotationCollectorConfigurationKeys {
public val ANNOTATION_FILTER_LIST: CompilerConfigurationKey<List<String>> =
CompilerConfigurationKey.create<List<String>>("annotation filter regular expressions")
public val OUTPUT_FILENAME: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>("output file")
}
public class AnnotationCollectorCommandLineProcessor : CommandLineProcessor {
companion object {
public val ANNOTATION_COLLECTOR_COMPILER_PLUGIN_ID: String = "org.jetbrains.kotlin.kapt"
public val ANNOTATION_FILTER_LIST_OPTION: CliOption =
CliOption("annotations", "<path>", "Annotation filter regular expressions, separated by commas", required = false)
public val OUTPUT_FILENAME_OPTION: CliOption =
CliOption("output", "<path>", "File in which annotated declarations will be placed", required = false)
}
override val pluginId: String = ANNOTATION_COLLECTOR_COMPILER_PLUGIN_ID
override val pluginOptions: Collection<CliOption> = listOf(ANNOTATION_FILTER_LIST_OPTION, OUTPUT_FILENAME_OPTION)
override fun processOption(option: CliOption, value: String, configuration: CompilerConfiguration) {
when (option) {
ANNOTATION_FILTER_LIST_OPTION -> {
val annotations = value.split(',').filter { !it.isEmpty() }.toList()
configuration.put(AnnotationCollectorConfigurationKeys.ANNOTATION_FILTER_LIST, annotations)
}
OUTPUT_FILENAME_OPTION -> configuration.put(AnnotationCollectorConfigurationKeys.OUTPUT_FILENAME, value)
else -> throw CliOptionProcessingException("Unknown option: ${option.name}")
}
}
}
public class AnnotationCollectorComponentRegistrar : ComponentRegistrar {
public override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
val annotationFilterList = configuration.get(AnnotationCollectorConfigurationKeys.ANNOTATION_FILTER_LIST)
val outputFilename = configuration.get(AnnotationCollectorConfigurationKeys.OUTPUT_FILENAME)
if (outputFilename != null) {
val collectorExtension = AnnotationCollectorExtension(annotationFilterList, outputFilename)
ClassBuilderInterceptorExtension.registerExtension(project, collectorExtension)
}
}
}