diff --git a/plugins/kapt3/kapt3.iml b/plugins/kapt3/kapt3.iml index b87c6efed97..00a9aed3a04 100644 --- a/plugins/kapt3/kapt3.iml +++ b/plugins/kapt3/kapt3.iml @@ -13,5 +13,7 @@ + + \ No newline at end of file diff --git a/plugins/kapt3/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor b/plugins/kapt3/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor new file mode 100644 index 00000000000..501e2a06db1 --- /dev/null +++ b/plugins/kapt3/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor @@ -0,0 +1 @@ +org.jetbrains.kotlin.kapt3.Kapt3CommandLineProcessor \ No newline at end of file diff --git a/plugins/kapt3/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar b/plugins/kapt3/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar new file mode 100644 index 00000000000..3c8e65064a7 --- /dev/null +++ b/plugins/kapt3/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar @@ -0,0 +1 @@ +org.jetbrains.kotlin.kapt3.Kapt3ComponentRegistrar \ No newline at end of file diff --git a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/Kapt3AnalysisCompletedHandlerExtension.kt b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/Kapt3AnalysisCompletedHandlerExtension.kt new file mode 100644 index 00000000000..931d82b6aca --- /dev/null +++ b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/Kapt3AnalysisCompletedHandlerExtension.kt @@ -0,0 +1,123 @@ +/* + * Copyright 2010-2016 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.kapt3 + +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.analyzer.AnalysisResult +import org.jetbrains.kotlin.codegen.* +import org.jetbrains.kotlin.codegen.state.GenerationState +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.kapt3.diagnostic.ErrorsKapt3 +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin +import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension +import org.jetbrains.org.objectweb.asm.FieldVisitor +import org.jetbrains.org.objectweb.asm.MethodVisitor +import org.jetbrains.org.objectweb.asm.tree.ClassNode +import org.jetbrains.org.objectweb.asm.tree.FieldNode +import org.jetbrains.org.objectweb.asm.tree.MethodNode +import java.io.File + +class Kapt3AnalysisCompletedHandlerExtension( + val classpath: List, + val javaSourceRoots: List, + val sourcesOutputDir: File, + val classesOutputDir: File, + val options: Map, + val isVerbose: Boolean +) : AnalysisCompletedHandlerExtension { + override fun analysisCompleted( + project: Project, + module: ModuleDescriptor, + bindingTrace: BindingTrace, + files: Collection + ): AnalysisResult? { + if (files.isEmpty()) { + return AnalysisResult.Companion.success(BindingContext.EMPTY, module, shouldGenerateCode = false) + } + + val builderFactory = Kapt3BuilderFactory() + + val generationState = GenerationState( + project, + builderFactory, + module, + bindingTrace.bindingContext, + files.toList(), + disableCallAssertions = false, + disableParamAssertions = false) + + try { + KotlinCodegenFacade.compileCorrectFiles(generationState, CompilationErrorHandler.THROW_EXCEPTION) + val compiledClasses = builderFactory.compiledClasses + val origins = builderFactory.origins + } catch (thr: Throwable) { + bindingTrace.report(ErrorsKapt3.KAPT3_PROCESSING_ERROR.on(files.first())) + } finally { + generationState.destroy() + } + + return AnalysisResult.success(BindingContext.EMPTY, module, shouldGenerateCode = false) + } +} + +private class Kapt3BuilderFactory : ClassBuilderFactory { + val compiledClasses = mutableListOf() + val origins = mutableMapOf() + + override fun getClassBuilderMode(): ClassBuilderMode = ClassBuilderMode.KAPT3 + + override fun newClassBuilder(origin: JvmDeclarationOrigin): AbstractClassBuilder.Concrete { + val classNode = ClassNode() + compiledClasses += classNode + origins.put(classNode, origin) + + return object : AbstractClassBuilder.Concrete(classNode) { + override fun newField( + origin: JvmDeclarationOrigin, + access: Int, + name: String, + desc: String, + signature: String?, + value: Any? + ): FieldVisitor { + val fieldNode = super.newField(origin, access, name, desc, signature, value) as FieldNode + origins.put(fieldNode, origin) + return fieldNode + } + + override fun newMethod( + origin: JvmDeclarationOrigin, + access: Int, + name: String, + desc: String, + signature: String?, + exceptions: Array? + ): MethodVisitor { + val methodNode = super.newMethod(origin, access, name, desc, signature, exceptions) as MethodNode + origins.put(methodNode, origin) + return methodNode + } + } + } + + override fun asText(builder: ClassBuilder) = throw UnsupportedOperationException() + override fun asBytes(builder: ClassBuilder) = throw UnsupportedOperationException() + override fun close() {} +} \ No newline at end of file diff --git a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/Kapt3Plugin.kt b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/Kapt3Plugin.kt new file mode 100644 index 00000000000..6a75b419af6 --- /dev/null +++ b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/Kapt3Plugin.kt @@ -0,0 +1,132 @@ +/* + * Copyright 2010-2016 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.kapt3 + +import com.intellij.mock.MockProject +import com.intellij.openapi.extensions.Extensions +import org.jetbrains.kotlin.kapt3.Kapt3ConfigurationKeys.ANNOTATION_PROCESSOR_CLASSPATH +import org.jetbrains.kotlin.kapt3.Kapt3ConfigurationKeys.APT_OPTIONS +import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot +import org.jetbrains.kotlin.cli.jvm.config.JvmContentRoot +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.config.JVMConfigurationKeys +import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages +import org.jetbrains.kotlin.kapt3.diagnostic.DefaultErrorMessagesKapt3 +import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension +import java.io.File + +object Kapt3ConfigurationKeys { + val GENERATED_OUTPUT_DIR: CompilerConfigurationKey = + CompilerConfigurationKey.create("generated files output directory") + + val CLASS_FILES_OUTPUT_DIR: CompilerConfigurationKey = + CompilerConfigurationKey.create("class files output directory") + + val ANNOTATION_PROCESSOR_CLASSPATH: CompilerConfigurationKey> = + CompilerConfigurationKey.create>("annotation processor classpath") + + val APT_OPTIONS: CompilerConfigurationKey> = + CompilerConfigurationKey.create>("annotation processing options") + + val VERBOSE_MODE: CompilerConfigurationKey = + CompilerConfigurationKey.create("verbose mode") +} + +class Kapt3CommandLineProcessor : CommandLineProcessor { + companion object { + val ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID: String = "org.jetbrains.kotlin.kapt3" + + val GENERATED_OUTPUT_DIR_OPTION: CliOption = + CliOption("generated", "", "Output path for the generated files", required = false) + + val CLASS_FILES_OUTPUT_DIR_OPTION: CliOption = + CliOption("classes", "", "Output path for the class files", required = false) + + val ANNOTATION_PROCESSOR_CLASSPATH_OPTION: CliOption = + CliOption("apclasspath", "", "Annotation processor classpath", + required = false, allowMultipleOccurrences = true) + + val APT_OPTIONS_OPTION: CliOption = + CliOption("apoption", ":", "Annotation processor option", + required = false, allowMultipleOccurrences = true) + + val VERBOSE_MODE_OPTION: CliOption = + CliOption("verbose", "true | false", "Enable verbose output", required = false) + } + + override val pluginId: String = ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID + + override val pluginOptions: Collection = + listOf(GENERATED_OUTPUT_DIR_OPTION, ANNOTATION_PROCESSOR_CLASSPATH_OPTION, APT_OPTIONS_OPTION, + CLASS_FILES_OUTPUT_DIR_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 -> configuration.appendList(ANNOTATION_PROCESSOR_CLASSPATH, value) + APT_OPTIONS_OPTION -> configuration.appendList(APT_OPTIONS, value) + GENERATED_OUTPUT_DIR_OPTION -> configuration.put(Kapt3ConfigurationKeys.GENERATED_OUTPUT_DIR, value) + CLASS_FILES_OUTPUT_DIR_OPTION -> configuration.put(Kapt3ConfigurationKeys.CLASS_FILES_OUTPUT_DIR, value) + VERBOSE_MODE_OPTION -> configuration.put(Kapt3ConfigurationKeys.VERBOSE_MODE, value) + else -> throw CliOptionProcessingException("Unknown option: ${option.name}") + } + } +} + +class Kapt3ComponentRegistrar : ComponentRegistrar { + override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) { + val generatedOutputDir = configuration.get(Kapt3ConfigurationKeys.GENERATED_OUTPUT_DIR) ?: 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 sourcesOutputDir = File(generatedOutputDir) + sourcesOutputDir.mkdirs() + + val contentRoots = configuration[JVMConfigurationKeys.CONTENT_ROOTS] ?: emptyList() + + val compileClasspath = contentRoots.filterIsInstance().map { it.file } + val classpath = apClasspath + compileClasspath + + val javaSourceRoots = contentRoots.filterIsInstance().map { it.file } + + val classesOutputDir = File(configuration.get(Kapt3ConfigurationKeys.CLASS_FILES_OUTPUT_DIR) + ?: configuration[JVMConfigurationKeys.MODULES]!!.first().getOutputDirectory()) + + val isVerbose = configuration.get(Kapt3ConfigurationKeys.VERBOSE_MODE) == "true" + + Extensions.getRootArea().getExtensionPoint(DefaultErrorMessages.Extension.EP_NAME).registerExtension(DefaultErrorMessagesKapt3()) + + val kapt3AnalysisCompletedHandlerExtension = Kapt3AnalysisCompletedHandlerExtension( + classpath, javaSourceRoots, sourcesOutputDir, classesOutputDir, apOptions, isVerbose) + AnalysisCompletedHandlerExtension.registerExtension(project, kapt3AnalysisCompletedHandlerExtension) + } +} \ No newline at end of file diff --git a/plugins/kapt3/src/KaptJavaCompiler.kt b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/KaptJavaCompiler.kt similarity index 100% rename from plugins/kapt3/src/KaptJavaCompiler.kt rename to plugins/kapt3/src/org/jetbrains/kotlin/kapt3/KaptJavaCompiler.kt diff --git a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/diagnostic/DefaultErrorMessagesKapt3.kt b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/diagnostic/DefaultErrorMessagesKapt3.kt new file mode 100644 index 00000000000..49ca8cf88f4 --- /dev/null +++ b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/diagnostic/DefaultErrorMessagesKapt3.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2010-2016 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.kapt3.diagnostic + +import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages +import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap + +class DefaultErrorMessagesKapt3 : DefaultErrorMessages.Extension { + + private companion object { + val MAP = DiagnosticFactoryToRendererMap("AnnotationProcessing") + + init { + MAP.put(ErrorsKapt3.KAPT3_PROCESSING_ERROR, + "Some error(s) occurred while processing annotations. Please see the error messages above.") + } + } + + override fun getMap() = MAP +} \ No newline at end of file diff --git a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/diagnostic/ErrorsKapt3.java b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/diagnostic/ErrorsKapt3.java new file mode 100644 index 00000000000..dd3d13fd416 --- /dev/null +++ b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/diagnostic/ErrorsKapt3.java @@ -0,0 +1,35 @@ +/* + * Copyright 2010-2016 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.kapt3.diagnostic; + +import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0; +import org.jetbrains.kotlin.diagnostics.Errors; +import org.jetbrains.kotlin.psi.KtElement; + +import static org.jetbrains.kotlin.diagnostics.Severity.ERROR; + +public interface ErrorsKapt3 { + DiagnosticFactory0 KAPT3_PROCESSING_ERROR = DiagnosticFactory0.create(ERROR); + + @SuppressWarnings("UnusedDeclaration") + Object _initializer = new Object() { + { + Errors.Initializer.initializeFactoryNames(ErrorsKapt3.class); + } + }; + +} \ No newline at end of file