Kapt: Extract annotation processing running logic from the compiler plugin

This commit is contained in:
Yan Zhulanow
2018-05-14 16:37:05 +03:00
parent 3ecf355e7a
commit 2bc45e0484
43 changed files with 843 additions and 577 deletions
@@ -7,6 +7,7 @@
<properties> <properties>
<maven.version>3.0.4</maven.version> <maven.version>3.0.4</maven.version>
<annotation-processing.src>${basedir}/../../../plugins/kapt3/kapt3-compiler/src</annotation-processing.src> <annotation-processing.src>${basedir}/../../../plugins/kapt3/kapt3-compiler/src</annotation-processing.src>
<annotation-processing.base.src>${basedir}/../../../plugins/kapt3/kapt3-base/src</annotation-processing.base.src>
<annotation-processing.runtime.src>${basedir}/../../../plugins/kapt3/kapt3-runtime/src</annotation-processing.runtime.src> <annotation-processing.runtime.src>${basedir}/../../../plugins/kapt3/kapt3-runtime/src</annotation-processing.runtime.src>
<annotation-processing.target-src>${basedir}/target/src/main/kotlin</annotation-processing.target-src> <annotation-processing.target-src>${basedir}/target/src/main/kotlin</annotation-processing.target-src>
<annotation-processing.target-src-test>${basedir}/target/src/test/kotlin</annotation-processing.target-src-test> <annotation-processing.target-src-test>${basedir}/target/src/test/kotlin</annotation-processing.target-src-test>
@@ -69,6 +70,7 @@
<outputDirectory>${annotation-processing.target-src}</outputDirectory> <outputDirectory>${annotation-processing.target-src}</outputDirectory>
<resources> <resources>
<resource><directory>${annotation-processing.src}</directory></resource> <resource><directory>${annotation-processing.src}</directory></resource>
<resource><directory>${annotation-processing.base.src}</directory></resource>
<resource><directory>${annotation-processing.runtime.src}</directory></resource> <resource><directory>${annotation-processing.runtime.src}</directory></resource>
</resources> </resources>
</configuration> </configuration>
@@ -120,24 +120,6 @@ open class Kapt3IT : Kapt3BaseIT() {
} }
} }
@Test
fun testArguments() {
Project("arguments", directoryPrefix = "kapt2").build("build") {
assertSuccessful()
assertKaptSuccessful()
assertContains(
"Options: {suffix=Customized, justColon=:, justEquals==, containsColon=a:b, " +
"containsEquals=a=b, startsWithColon=:a, startsWithEquals==a, endsWithColon=a:, " +
"endsWithEquals=a:, withSpace=a b c,"
)
assertContains("-Xmaxerrs=500, -Xlint:all=-Xlint:all") // Javac options test
assertFileExists("build/generated/source/kapt/main/example/TestClassCustomized.java")
assertFileExists(kotlinClassesDir() + "example/TestClass.class")
assertFileExists(javaClassesDir() + "example/TestClassCustomized.class")
assertContains("Annotation processor class names are set, skip AP discovery")
}
}
@Test @Test
fun testInheritedAnnotations() { fun testInheritedAnnotations() {
Project("inheritedAnnotations", directoryPrefix = "kapt2").build("build") { Project("inheritedAnnotations", directoryPrefix = "kapt2").build("build") {
@@ -150,6 +132,24 @@ open class Kapt3IT : Kapt3BaseIT() {
} }
} }
@Test
fun testArguments() {
Project("arguments", directoryPrefix = "kapt2").build("build") {
assertSuccessful()
assertKaptSuccessful()
assertContains(
"AP options: {suffix=Customized, justColon=:, justEquals==, containsColon=a:b, " +
"containsEquals=a=b, startsWithColon=:a, startsWithEquals==a, endsWithColon=a:, " +
"endsWithEquals=a:, withSpace=a b c,"
)
assertContains("-Xmaxerrs=500, -Xlint:all=-Xlint:all") // Javac options test
assertFileExists("build/generated/source/kapt/main/example/TestClassCustomized.java")
assertFileExists(kotlinClassesDir() + "example/TestClass.class")
assertFileExists(javaClassesDir() + "example/TestClassCustomized.class")
assertContains("Annotation processor class names are set, skip AP discovery")
}
}
@Test @Test
fun testGeneratedDirectoryIsUpToDate() { fun testGeneratedDirectoryIsUpToDate() {
val project = Project("generatedDirUpToDate", directoryPrefix = "kapt2") val project = Project("generatedDirUpToDate", directoryPrefix = "kapt2")
+22
View File
@@ -0,0 +1,22 @@
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
compile(projectDist(":kotlin-stdlib"))
compile(files("${System.getProperty("java.home")}/../lib/tools.jar"))
testCompile(commonDep("junit:junit"))
}
sourceSets {
"main" { projectDefault() }
"test" { projectDefault() }
}
testsJar {}
projectTest {
workingDir = rootDir
dependsOn(":dist")
}
@@ -0,0 +1,88 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.kapt3.base
import com.sun.tools.javac.util.Context
import org.jetbrains.kotlin.kapt3.base.util.KaptLogger
import org.jetbrains.kotlin.kapt3.base.util.WriterBackedKaptLogger
import org.jetbrains.kotlin.kapt3.base.util.info
import kotlin.system.measureTimeMillis
object Kapt {
private const val JAVAC_CONTEXT_CLASS = "com.sun.tools.javac.util.Context"
@JvmStatic
@Suppress("unused")
fun kapt(
paths: KaptPaths,
isVerbose: Boolean,
mapDiagnosticLocations: Boolean,
annotationProcessorFqNames: List<String>,
processorOptions: Map<String, String>,
javacOptions: Map<String, String>
): Boolean {
val logger = WriterBackedKaptLogger(isVerbose)
if (!Kapt.checkJavacComponentsAccess(logger)) {
return false
}
val kaptContext = KaptContext(paths, false, logger, mapDiagnosticLocations, processorOptions, javacOptions)
logger.info { "Kapt3 is enabled (stand-alone mode)." }
logger.info { "Map diagnostic locations: $mapDiagnosticLocations" }
paths.log(logger)
logger.info { "Javac options: $javacOptions" }
logger.info { "AP options: $processorOptions" }
val javaSourceFiles = paths.collectJavaSourceFiles()
logger.info { "Java source files: " + javaSourceFiles.joinToString { it.canonicalPath } }
val processorLoader = ProcessorLoader(paths, annotationProcessorFqNames, logger)
processorLoader.use {
val processors = processorLoader.loadProcessors(findClassLoaderWithJavac())
val annotationProcessingTime = measureTimeMillis {
kaptContext.doAnnotationProcessing(javaSourceFiles, processors)
}
logger.info { "Annotation processing took $annotationProcessingTime ms" }
}
return true
}
fun checkJavacComponentsAccess(logger: KaptLogger): Boolean {
try {
Class.forName(JAVAC_CONTEXT_CLASS)
return true
} catch (e: ClassNotFoundException) {
logger.error("'$JAVAC_CONTEXT_CLASS' class can't be found ('tools.jar' is absent in the plugin classpath). Kapt won't work.")
return false
}
}
private fun findClassLoaderWithJavac(): ClassLoader {
fun Class<*>.toClassFilePath() = name.replace('.', '/') + ".class"
// find topmost class loader with javac
val javacContextPath = Context::class.java.toClassFilePath()
val kaptPath = Kapt::class.java.toClassFilePath()
fun findRightClassLoader(current: ClassLoader): ClassLoader? {
if (current.getResource(javacContextPath) != null && current.getResource(kaptPath) == null) {
return current
}
val parent = current.parent ?: return null
return findRightClassLoader(parent)
}
val kaptClassLoader = Kapt::class.java.classLoader
return findRightClassLoader(kaptClassLoader) ?: kaptClassLoader
}
}
@@ -1,54 +1,30 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* * that can be found in the license/LICENSE.txt file.
* 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 package org.jetbrains.kotlin.kapt3.base
import com.intellij.openapi.project.Project
import com.sun.tools.javac.jvm.ClassReader import com.sun.tools.javac.jvm.ClassReader
import com.sun.tools.javac.main.JavaCompiler import com.sun.tools.javac.main.JavaCompiler
import com.sun.tools.javac.tree.TreeMaker
import com.sun.tools.javac.main.Option import com.sun.tools.javac.main.Option
import com.sun.tools.javac.util.Context import com.sun.tools.javac.util.Context
import com.sun.tools.javac.util.Log
import com.sun.tools.javac.util.Options import com.sun.tools.javac.util.Options
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaCompiler
import org.jetbrains.kotlin.kapt3.javac.KaptJavaCompiler import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaFileManager
import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileManager import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaLog
import org.jetbrains.kotlin.kapt3.javac.KaptJavaLog import org.jetbrains.kotlin.kapt3.base.util.KaptLogger
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker import org.jetbrains.kotlin.kapt3.base.util.isJava9OrLater
import org.jetbrains.kotlin.kapt3.util.KaptLogger import org.jetbrains.kotlin.kapt3.base.util.putJavacOption
import org.jetbrains.kotlin.kapt3.util.isJava9OrLater
import org.jetbrains.kotlin.kapt3.util.putJavacOption
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.utils.keysToMap
import org.jetbrains.org.objectweb.asm.tree.ClassNode
import java.io.File import java.io.File
import javax.tools.JavaFileManager import javax.tools.JavaFileManager
class KaptContext<out GState : GenerationState?>( open class KaptContext(
val paths: KaptPaths, val paths: KaptPaths,
private val withJdk: Boolean, val withJdk: Boolean,
aptMode: AptMode,
val logger: KaptLogger, val logger: KaptLogger,
val project: Project, private val mapDiagnosticLocations: Boolean,
val bindingContext: BindingContext,
val compiledClasses: List<ClassNode>,
val origins: Map<Any, JvmDeclarationOrigin>,
val generationState: GState,
mapDiagnosticLocations: Boolean,
processorOptions: Map<String, String>, processorOptions: Map<String, String>,
javacOptions: Map<String, String> = emptyMap() javacOptions: Map<String, String> = emptyMap()
) : AutoCloseable { ) : AutoCloseable {
@@ -57,14 +33,26 @@ class KaptContext<out GState : GenerationState?>(
val fileManager: KaptJavaFileManager val fileManager: KaptJavaFileManager
val options: Options val options: Options
val javaLog: KaptJavaLog val javaLog: KaptJavaLog
private val treeMaker: TreeMaker
protected open fun preregisterTreeMaker(context: Context) {}
private fun preregisterLog(context: Context) {
val interceptorData = KaptJavaLog.DiagnosticInterceptorData()
context.put(Log.logKey, Context.Factory<Log> { newContext ->
KaptJavaLog(
paths.projectBaseDir, newContext, logger.errorWriter, logger.warnWriter, logger.infoWriter,
interceptorData, mapDiagnosticLocations
)
})
}
init { init {
KaptJavaLog.preRegister(this, logger.messageCollector, mapDiagnosticLocations) preregisterLog(context)
KaptJavaFileManager.preRegister(context) KaptJavaFileManager.preRegister(context)
if (aptMode != AptMode.APT_ONLY) {
KaptTreeMaker.preRegister(context, this) @Suppress("LeakingThis")
} preregisterTreeMaker(context)
KaptJavaCompiler.preRegister(context) KaptJavaCompiler.preRegister(context)
options = Options.instance(context).apply { options = Options.instance(context).apply {
@@ -87,7 +75,7 @@ class KaptContext<out GState : GenerationState?>(
putJavacOption("BOOTCLASSPATH", "BOOT_CLASS_PATH", "") // No boot classpath putJavacOption("BOOTCLASSPATH", "BOOT_CLASS_PATH", "") // No boot classpath
} }
if (isJava9OrLater) { if (isJava9OrLater()) {
put("accessInternalAPI", "true") put("accessInternalAPI", "true")
} }
@@ -102,12 +90,12 @@ class KaptContext<out GState : GenerationState?>(
} }
if (logger.isVerbose) { if (logger.isVerbose) {
logger.info("Javac options: " + options.keySet().keysToMap { key -> options[key] ?: "" }) logger.info("All Javac options: " + options.keySet().associateBy({ it }) { key -> options[key] ?: "" })
} }
fileManager = context.get(JavaFileManager::class.java) as KaptJavaFileManager fileManager = context.get(JavaFileManager::class.java) as KaptJavaFileManager
if (isJava9OrLater) { if (isJava9OrLater()) {
for (option in Option.getJavacFileManagerOptions()) { for (option in Option.getJavacFileManagerOptions()) {
val value = options.get(option) ?: continue val value = options.get(option) ?: continue
fileManager.handleOptionJavac9(option, value) fileManager.handleOptionJavac9(option, value)
@@ -120,13 +108,10 @@ class KaptContext<out GState : GenerationState?>(
ClassReader.instance(context).saveParameterNames = true ClassReader.instance(context).saveParameterNames = true
javaLog = compiler.log as KaptJavaLog javaLog = compiler.log as KaptJavaLog
treeMaker = TreeMaker.instance(context)
} }
override fun close() { override fun close() {
(treeMaker as? KaptTreeMaker)?.dispose()
compiler.close() compiler.close()
fileManager.close() fileManager.close()
generationState?.destroy()
} }
} }
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.kapt3.base
import org.jetbrains.kotlin.kapt3.base.util.KaptLogger
import java.io.File
class KaptPaths(
val projectBaseDir: File?,
compileClasspath: List<File>,
annotationProcessingClasspath: List<File>,
val javaSourceRoots: List<File>,
val sourcesOutputDir: File,
val classFilesOutputDir: File,
val stubsOutputDir: File,
val incrementalDataOutputDir: File?
) {
val compileClasspath = compileClasspath.distinct()
val annotationProcessingClasspath = annotationProcessingClasspath.distinct()
fun collectJavaSourceFiles(): List<File> {
return (javaSourceRoots + stubsOutputDir).flatMap { root ->
root.walk().filter { it.isFile && it.extension == "java" }.toList()
}
}
}
fun KaptPaths.log(logger: KaptLogger) {
if (!logger.isVerbose) return
logger.info("Project base dir: $projectBaseDir")
logger.info("Compile classpath: ${compileClasspath.joinToString()}")
logger.info("Annotation processing classpath: ${annotationProcessingClasspath.joinToString()}")
logger.info("Java source roots: ${javaSourceRoots.joinToString()}")
logger.info("Sources output directory: $sourcesOutputDir")
logger.info("Class files output directory: $classFilesOutputDir")
logger.info("Stubs output directory: $stubsOutputDir")
logger.info("Incremental data output directory: $incrementalDataOutputDir")
}
@@ -0,0 +1,96 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.kapt3.base
import org.jetbrains.kotlin.kapt3.base.util.KaptLogger
import org.jetbrains.kotlin.kapt3.base.util.info
import java.io.Closeable
import java.lang.reflect.Field
import java.lang.reflect.Modifier
import java.net.URLClassLoader
import java.util.*
import javax.annotation.processing.Processor
class ProcessorLoader(
private val paths: KaptPaths,
private val annotationProcessorFqNames: List<String>,
private val logger: KaptLogger
) : Closeable {
private var annotationProcessingClassLoader: URLClassLoader? = null
fun loadProcessors(parentClassLoader: ClassLoader = ClassLoader.getSystemClassLoader()): List<Processor> {
clearJarURLCache()
val classpath = (paths.annotationProcessingClasspath + paths.compileClasspath).distinct()
val classLoader = URLClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray(), parentClassLoader)
this.annotationProcessingClassLoader = classLoader
val processors = if (annotationProcessorFqNames.isNotEmpty()) {
logger.info("Annotation processor class names are set, skip AP discovery")
annotationProcessorFqNames.mapNotNull { tryLoadProcessor(it, classLoader) }
} else {
logger.info("Need to discovery annotation processors in the AP classpath")
ServiceLoader.load(Processor::class.java, classLoader).toList()
}
if (processors.isEmpty()) {
logger.info("No annotation processors available, aborting")
} else {
logger.info { "Annotation processors: " + processors.joinToString { it::class.java.canonicalName } }
}
return processors
}
private fun tryLoadProcessor(fqName: String, classLoader: ClassLoader): Processor? {
val annotationProcessorClass = try {
Class.forName(fqName, true, classLoader)
} catch (e: Throwable) {
logger.warn("Can't find annotation processor class $fqName: ${e.message}")
return null
}
try {
val annotationProcessorInstance = annotationProcessorClass.newInstance()
if (annotationProcessorInstance !is Processor) {
logger.warn("$fqName is not an instance of 'Processor'")
return null
}
return annotationProcessorInstance
} catch (e: Throwable) {
logger.warn("Can't load annotation processor class $fqName: ${e.message}")
return null
}
}
override fun close() {
annotationProcessingClassLoader?.close()
clearJarURLCache()
}
}
// Copied from com.intellij.ide.ClassUtilCore
private fun clearJarURLCache() {
fun clearMap(cache: Field) {
cache.isAccessible = true
if (!Modifier.isFinal(cache.modifiers)) {
cache.set(null, hashMapOf<Any, Any>())
} else {
val map = cache.get(null) as MutableMap<*, *>
map.clear()
}
}
try {
val jarFileFactory = Class.forName("sun.net.www.protocol.jar.JarFileFactory")
clearMap(jarFileFactory.getDeclaredField("fileCache"))
clearMap(jarFileFactory.getDeclaredField("urlCache"))
} catch (ignore: Exception) {}
}
@@ -1,32 +1,19 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* * that can be found in the license/LICENSE.txt file.
* 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 package org.jetbrains.kotlin.kapt3.base
import com.sun.tools.javac.comp.CompileStates.* import com.sun.tools.javac.comp.CompileStates.*
import com.sun.tools.javac.main.JavaCompiler import com.sun.tools.javac.main.JavaCompiler
import com.sun.tools.javac.main.Option
import com.sun.tools.javac.processing.AnnotationProcessingError import com.sun.tools.javac.processing.AnnotationProcessingError
import com.sun.tools.javac.processing.JavacFiler import com.sun.tools.javac.processing.JavacFiler
import com.sun.tools.javac.processing.JavacProcessingEnvironment import com.sun.tools.javac.processing.JavacProcessingEnvironment
import com.sun.tools.javac.tree.JCTree import com.sun.tools.javac.tree.JCTree
import org.jetbrains.kotlin.kapt3.diagnostic.KaptError import org.jetbrains.kotlin.kapt3.base.util.KaptBaseError
import org.jetbrains.kotlin.kapt3.util.isJava9OrLater import org.jetbrains.kotlin.kapt3.base.util.isJava9OrLater
import org.jetbrains.kotlin.kapt3.util.putJavacOption import org.jetbrains.kotlin.kapt3.base.util.measureTimeMillisWithResult
import org.jetbrains.kotlin.utils.addToStdlib.measureTimeMillisWithResult
import java.io.File import java.io.File
import javax.annotation.processing.Processor import javax.annotation.processing.Processor
import javax.annotation.processing.RoundEnvironment import javax.annotation.processing.RoundEnvironment
@@ -34,7 +21,7 @@ import javax.lang.model.element.TypeElement
import javax.tools.JavaFileObject import javax.tools.JavaFileObject
import com.sun.tools.javac.util.List as JavacList import com.sun.tools.javac.util.List as JavacList
fun KaptContext<*>.doAnnotationProcessing( fun KaptContext.doAnnotationProcessing(
javaSourceFiles: List<File>, javaSourceFiles: List<File>,
processors: List<Processor>, processors: List<Processor>,
additionalSources: JavacList<JCTree.JCCompilationUnit> = JavacList.nil() additionalSources: JavacList<JCTree.JCCompilationUnit> = JavacList.nil()
@@ -44,7 +31,7 @@ fun KaptContext<*>.doAnnotationProcessing(
val compilerAfterAP: JavaCompiler val compilerAfterAP: JavaCompiler
try { try {
if (isJava9OrLater) { if (isJava9OrLater()) {
val initProcessAnnotationsMethod = JavaCompiler::class.java.declaredMethods.single { it.name == "initProcessAnnotations" } val initProcessAnnotationsMethod = JavaCompiler::class.java.declaredMethods.single { it.name == "initProcessAnnotations" }
initProcessAnnotationsMethod.invoke(compiler, wrappedProcessors, emptyList<JavaFileObject>(), emptyList<String>()) initProcessAnnotationsMethod.invoke(compiler, wrappedProcessors, emptyList<JavaFileObject>(), emptyList<String>())
} }
@@ -59,7 +46,7 @@ fun KaptContext<*>.doAnnotationProcessing(
val analyzedFiles = compiler.stopIfErrorOccurred( val analyzedFiles = compiler.stopIfErrorOccurred(
CompileState.PARSE, compiler.enterTrees(parsedJavaFiles + additionalSources)) CompileState.PARSE, compiler.enterTrees(parsedJavaFiles + additionalSources))
if (isJava9OrLater) { if (isJava9OrLater()) {
val processAnnotationsMethod = compiler.javaClass.getMethod("processAnnotations", JavacList::class.java) val processAnnotationsMethod = compiler.javaClass.getMethod("processAnnotations", JavacList::class.java)
processAnnotationsMethod.invoke(compiler, analyzedFiles) processAnnotationsMethod.invoke(compiler, analyzedFiles)
compiler compiler
@@ -68,7 +55,7 @@ fun KaptContext<*>.doAnnotationProcessing(
compiler.processAnnotations(analyzedFiles) compiler.processAnnotations(analyzedFiles)
} }
} catch (e: AnnotationProcessingError) { } catch (e: AnnotationProcessingError) {
throw KaptError(KaptError.Kind.EXCEPTION, e.cause ?: e) throw KaptBaseError(KaptBaseError.Kind.EXCEPTION, e.cause ?: e)
} }
val log = compilerAfterAP.log val log = compilerAfterAP.log
@@ -93,7 +80,7 @@ fun KaptContext<*>.doAnnotationProcessing(
} }
if (log.nerrors > 0) { if (log.nerrors > 0) {
throw KaptError(KaptError.Kind.ERROR_RAISED) throw KaptBaseError(KaptBaseError.Kind.ERROR_RAISED)
} }
} finally { } finally {
processingEnvironment.close() processingEnvironment.close()
@@ -117,7 +104,7 @@ private class ProcessorWrapper(private val delegate: Processor) : Processor by d
} }
} }
internal fun KaptContext<*>.parseJavaFiles(javaSourceFiles: List<File>): JavacList<JCTree.JCCompilationUnit> { fun KaptContext.parseJavaFiles(javaSourceFiles: List<File>): JavacList<JCTree.JCCompilationUnit> {
val javaFileObjects = fileManager.getJavaFileObjectsFromFiles(javaSourceFiles) val javaFileObjects = fileManager.getJavaFileObjectsFromFiles(javaSourceFiles)
return compiler.stopIfErrorOccurred(CompileState.PARSE, return compiler.stopIfErrorOccurred(CompileState.PARSE,
@@ -126,8 +113,8 @@ internal fun KaptContext<*>.parseJavaFiles(javaSourceFiles: List<File>): JavacLi
compiler.parseFiles(javaFileObjects)))) compiler.parseFiles(javaFileObjects))))
} }
private fun KaptContext<*>.initModulesIfNeeded(files: JavacList<JCTree.JCCompilationUnit>): JavacList<JCTree.JCCompilationUnit> { private fun KaptContext.initModulesIfNeeded(files: JavacList<JCTree.JCCompilationUnit>): JavacList<JCTree.JCCompilationUnit> {
if (isJava9OrLater) { if (isJava9OrLater()) {
val initModulesMethod = compiler.javaClass.getMethod("initModules", JavacList::class.java) val initModulesMethod = compiler.javaClass.getMethod("initModules", JavacList::class.java)
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@@ -1,20 +1,9 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* * that can be found in the license/LICENSE.txt file.
* 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.javac package org.jetbrains.kotlin.kapt3.base.javac
import com.sun.tools.javac.comp.CompileStates import com.sun.tools.javac.comp.CompileStates
import com.sun.tools.javac.main.JavaCompiler import com.sun.tools.javac.main.JavaCompiler
@@ -1,4 +1,9 @@
package org.jetbrains.kotlin.kapt3.javac /*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.kapt3.base.javac
import com.sun.tools.javac.file.JavacFileManager import com.sun.tools.javac.file.JavacFileManager
import com.sun.tools.javac.main.Option import com.sun.tools.javac.main.Option
@@ -1,32 +1,17 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* * that can be found in the license/LICENSE.txt file.
* 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.javac package org.jetbrains.kotlin.kapt3.base.javac
import com.sun.tools.javac.tree.JCTree import com.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.util.* import com.sun.tools.javac.util.*
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.kapt3.base.KaptContext
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.* import org.jetbrains.kotlin.kapt3.base.stubs.KaptStubLineInformation
import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.kapt3.base.stubs.KotlinPosition
import org.jetbrains.kotlin.kapt3.KaptContext import org.jetbrains.kotlin.kapt3.base.util.isJava9OrLater
import org.jetbrains.kotlin.kapt3.stubs.KaptStubLineInformation
import org.jetbrains.kotlin.kapt3.stubs.KotlinPosition
import org.jetbrains.kotlin.kapt3.util.MessageCollectorBackedWriter
import org.jetbrains.kotlin.kapt3.util.isJava9OrLater
import java.io.* import java.io.*
import javax.tools.Diagnostic import javax.tools.Diagnostic
import javax.tools.JavaFileObject import javax.tools.JavaFileObject
@@ -35,14 +20,14 @@ import javax.tools.SimpleJavaFileObject
import com.sun.tools.javac.util.List as JavacList import com.sun.tools.javac.util.List as JavacList
class KaptJavaLog( class KaptJavaLog(
private val projectBasePath: String?, private val projectBaseDir: File?,
context: Context, context: Context,
errWriter: PrintWriter, errWriter: PrintWriter,
warnWriter: PrintWriter, warnWriter: PrintWriter,
noticeWriter: PrintWriter, noticeWriter: PrintWriter,
val interceptorData: DiagnosticInterceptorData, val interceptorData: DiagnosticInterceptorData,
val mapDiagnosticLocations: Boolean private val mapDiagnosticLocations: Boolean
) : Log(context, errWriter, warnWriter, noticeWriter) { ) : Log(context, errWriter, warnWriter, noticeWriter) {
private val stubLineInfo = KaptStubLineInformation() private val stubLineInfo = KaptStubLineInformation()
private val javacMessages = JavacMessages.instance(context) private val javacMessages = JavacMessages.instance(context)
@@ -183,7 +168,7 @@ class KaptJavaLog(
private fun getKotlinSourceFile(pos: KotlinPosition): File? { private fun getKotlinSourceFile(pos: KotlinPosition): File? {
return if (pos.isRelativePath) { return if (pos.isRelativePath) {
val basePath = this.projectBasePath val basePath = this.projectBaseDir
if (basePath != null) File(basePath, pos.path) else null if (basePath != null) File(basePath, pos.path) else null
} }
else { else {
@@ -232,21 +217,6 @@ class KaptJavaLog(
"compiler.err.not.def.access.package.cant.access", "compiler.err.not.def.access.package.cant.access",
"compiler.err.package.not.visible" "compiler.err.package.not.visible"
) )
internal fun preRegister(kaptContext: KaptContext<*>, messageCollector: MessageCollector, mapDiagnosticLocations: Boolean) {
val interceptorData = DiagnosticInterceptorData()
kaptContext.context.put(Log.logKey, Context.Factory<Log> { newContext ->
fun makeWriter(severity: CompilerMessageSeverity) = PrintWriter(MessageCollectorBackedWriter(messageCollector, severity))
val errWriter = makeWriter(ERROR)
val warnWriter = makeWriter(STRONG_WARNING)
val noticeWriter = makeWriter(WARNING)
KaptJavaLog(
kaptContext.project.basePath, newContext, errWriter, warnWriter, noticeWriter,
interceptorData, mapDiagnosticLocations)
})
}
} }
class DiagnosticInterceptorData { class DiagnosticInterceptorData {
@@ -254,7 +224,7 @@ class KaptJavaLog(
} }
} }
fun KaptContext<*>.kaptError(text: String): JCDiagnostic { fun KaptContext.kaptError(text: String): JCDiagnostic {
return JCDiagnostic.Factory.instance(context).errorJava9Aware(null, null, "proc.messager", text) return JCDiagnostic.Factory.instance(context).errorJava9Aware(null, null, "proc.messager", text)
} }
@@ -264,7 +234,7 @@ private fun JCDiagnostic.Factory.errorJava9Aware(
key: String, key: String,
vararg args: String vararg args: String
): JCDiagnostic { ): JCDiagnostic {
return if (isJava9OrLater) { return if (isJava9OrLater()) {
val errorMethod = this::class.java.getDeclaredMethod( val errorMethod = this::class.java.getDeclaredMethod(
"error", "error",
JCDiagnostic.DiagnosticFlag::class.java, JCDiagnostic.DiagnosticFlag::class.java,
@@ -0,0 +1,25 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.kapt3.base.stubs
import com.sun.tools.javac.tree.JCTree
fun JCTree.JCMethodDecl.getJavacSignature(): String {
val name = name.toString()
val params = parameters.joinToString { it.getType().toString() }
return "$name($params)"
}
typealias LineInfoMap = MutableMap<String, KotlinPosition>
class FileInfo(private val lineInfo: LineInfoMap, private val signatureInfo: Map<String, String>) {
companion object {
val EMPTY = FileInfo(mutableMapOf(), emptyMap())
}
fun getPositionFor(fqName: String) = lineInfo[fqName]
fun getMethodDescriptor(decl: JCTree.JCMethodDecl) = signatureInfo[decl.getJavacSignature()]
}
@@ -1,34 +1,77 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* * that can be found in the license/LICENSE.txt file.
* 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.stubs package org.jetbrains.kotlin.kapt3.base.stubs
import com.sun.tools.javac.tree.JCTree import com.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.tree.TreeScanner import com.sun.tools.javac.tree.TreeScanner
import org.jetbrains.kotlin.kapt3.stubs.KaptLineMappingCollector.FileInfo import org.jetbrains.kotlin.kapt3.base.util.getPackageNameJava9Aware
import org.jetbrains.kotlin.kapt3.util.getPackageNameJava9Aware import java.io.ByteArrayInputStream
import java.io.File
import java.io.ObjectInputStream
class KaptStubLineInformation { class KaptStubLineInformation {
private val offsets = mutableMapOf<JCTree.JCCompilationUnit, FileInfo>() private val offsets = mutableMapOf<JCTree.JCCompilationUnit, FileInfo>()
private val declarations = mutableMapOf<JCTree.JCCompilationUnit, List<JCTree>>() private val declarations = mutableMapOf<JCTree.JCCompilationUnit, List<JCTree>>()
companion object {
const val KAPT_METADATA_EXTENSION = ".kapt_metadata"
const val METADATA_VERSION = 1
fun parseFileInfo(file: JCTree.JCCompilationUnit): FileInfo {
val sourceUri = file.sourcefile
?.toUri()
?.takeIf { it.isAbsolute && !it.isOpaque && it.path != null && it.scheme?.toLowerCase() == "file" } ?: return FileInfo.EMPTY
val sourceFile = File(sourceUri).takeIf { it.exists() } ?: return FileInfo.EMPTY
val kaptMetadataFile = File(sourceFile.parentFile, sourceFile.nameWithoutExtension + KAPT_METADATA_EXTENSION)
if (!kaptMetadataFile.isFile) {
return FileInfo.EMPTY
}
return deserialize(kaptMetadataFile.readBytes())
}
private fun deserialize(data: ByteArray): FileInfo {
val lineInfo: LineInfoMap = mutableMapOf()
val signatureInfo = mutableMapOf<String, String>()
val ois = ObjectInputStream(ByteArrayInputStream(data))
val version = ois.readInt()
if (version != METADATA_VERSION) {
return FileInfo.EMPTY
}
val lineInfoCount = ois.readInt()
repeat(lineInfoCount) {
val fqName = ois.readUTF()
val path = ois.readUTF()
val isRelative = ois.readBoolean()
val pos = ois.readInt()
lineInfo[fqName] = KotlinPosition(path, isRelative, pos)
}
val signatureCount = ois.readInt()
repeat(signatureCount) {
val javacSignature = ois.readUTF()
val methodDesc = ois.readUTF()
signatureInfo[javacSignature] = methodDesc
}
return FileInfo(lineInfo, signatureInfo)
}
}
fun getPositionInKotlinFile(file: JCTree.JCCompilationUnit, element: JCTree): KotlinPosition? { fun getPositionInKotlinFile(file: JCTree.JCCompilationUnit, element: JCTree): KotlinPosition? {
val declaration = findDeclarationFor(element, file) ?: return null val declaration = findDeclarationFor(element, file) ?: return null
val fileInfo = offsets.getOrPut(file) { KaptLineMappingCollector.parseFileInfo(file) } val fileInfo = offsets.getOrPut(file) { parseFileInfo(file) }
val elementDescriptor = getKaptDescriptor(declaration, file, fileInfo) ?: return null val elementDescriptor = getKaptDescriptor(declaration, file, fileInfo) ?: return null
return fileInfo.getPositionFor(elementDescriptor) return fileInfo.getPositionFor(elementDescriptor)
@@ -0,0 +1,8 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.kapt3.base.stubs
data class KotlinPosition(val path: String, val isRelativePath: Boolean, val pos: Int)
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.kapt3.base.util
class KaptBaseError : RuntimeException {
val kind: Kind
enum class Kind(val message: String) {
EXCEPTION("Exception while annotation processing"),
ERROR_RAISED("Error while annotation processing"),
}
constructor(kind: Kind) : super(kind.message) {
this.kind = kind
}
constructor(kind: Kind, cause: Throwable) : super(kind.message, cause) {
this.kind = kind
}
}
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.kapt3.base.util
import java.io.PrintWriter
interface KaptLogger {
val isVerbose: Boolean
val infoWriter: PrintWriter
val warnWriter: PrintWriter
val errorWriter: PrintWriter
fun info(message: String)
fun warn(message: String)
fun error(message: String)
fun exception(e: Throwable)
}
inline fun KaptLogger.info(message: () -> String) {
if (isVerbose) {
info(message())
}
}
@@ -0,0 +1,40 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.kapt3.base.util
import java.io.PrintWriter
class WriterBackedKaptLogger(
override val isVerbose: Boolean,
override val infoWriter: PrintWriter = PrintWriter(System.out),
override val warnWriter: PrintWriter = PrintWriter(System.out),
override val errorWriter: PrintWriter = PrintWriter(System.err)
) : KaptLogger {
override fun info(message: String) {
if (isVerbose) {
report("INFO", message, infoWriter)
}
}
override fun warn(message: String) {
report("WARN", message, warnWriter)
}
override fun error(message: String) {
report("ERROR", message, errorWriter)
}
override fun exception(e: Throwable) {
errorWriter.println("An error occurred:")
e.printStackTrace(errorWriter)
errorWriter.flush()
}
private fun report(prefix: String, message: String, writer: PrintWriter) {
writer.println("[$prefix] $message")
writer.flush()
}
}
@@ -14,32 +14,29 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.kapt3.util package org.jetbrains.kotlin.kapt3.base.util
import com.intellij.openapi.util.SystemInfo
import com.sun.tools.javac.main.Option import com.sun.tools.javac.main.Option
import com.sun.tools.javac.tree.JCTree import com.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.tree.TreeMaker import com.sun.tools.javac.tree.TreeMaker
import com.sun.tools.javac.util.Options import com.sun.tools.javac.util.Options
import com.sun.tools.javac.util.List as JavacList import com.sun.tools.javac.util.List as JavacList
import org.jetbrains.kotlin.kapt3.plus import org.jetbrains.kotlin.kapt3.base.plus
internal val isJava9OrLater: Boolean fun isJava9OrLater(): Boolean = !System.getProperty("java.version").startsWith("1.")
get() = SystemInfo.isJavaVersionAtLeast("9")
internal fun Options.putJavacOption(jdk8Name: String, jdk9Name: String, value: String) { fun Options.putJavacOption(jdk8Name: String, jdk9Name: String, value: String) {
val option = if (isJava9OrLater) { val option = if (isJava9OrLater()) {
Option.valueOf(jdk9Name) Option.valueOf(jdk9Name)
} } else {
else {
Option.valueOf(jdk8Name) Option.valueOf(jdk8Name)
} }
put(option, value) put(option, value)
} }
internal fun TreeMaker.TopLevelJava9Aware(packageClause: JCTree.JCExpression?, declarations: JavacList<JCTree>): JCTree.JCCompilationUnit { fun TreeMaker.TopLevelJava9Aware(packageClause: JCTree.JCExpression?, declarations: JavacList<JCTree>): JCTree.JCCompilationUnit {
return if (isJava9OrLater) { return if (isJava9OrLater()) {
val topLevelMethod = TreeMaker::class.java.declaredMethods.single { it.name == "TopLevel" } val topLevelMethod = TreeMaker::class.java.declaredMethods.single { it.name == "TopLevel" }
val packageDecl: JCTree? = packageClause?.let { val packageDecl: JCTree? = packageClause?.let {
val packageDeclMethod = TreeMaker::class.java.methods.single { it.name == "PackageDecl" } val packageDeclMethod = TreeMaker::class.java.methods.single { it.name == "PackageDecl" }
@@ -47,17 +44,15 @@ internal fun TreeMaker.TopLevelJava9Aware(packageClause: JCTree.JCExpression?, d
} }
val allDeclarations = if (packageDecl != null) JavacList.of(packageDecl) + declarations else declarations val allDeclarations = if (packageDecl != null) JavacList.of(packageDecl) + declarations else declarations
topLevelMethod.invoke(this, allDeclarations) as JCTree.JCCompilationUnit topLevelMethod.invoke(this, allDeclarations) as JCTree.JCCompilationUnit
} } else {
else {
TopLevel(JavacList.nil(), packageClause, declarations) TopLevel(JavacList.nil(), packageClause, declarations)
} }
} }
internal fun JCTree.JCCompilationUnit.getPackageNameJava9Aware(): JCTree? { fun JCTree.JCCompilationUnit.getPackageNameJava9Aware(): JCTree? {
return if (isJava9OrLater) { return if (isJava9OrLater()) {
JCTree.JCCompilationUnit::class.java.getDeclaredMethod("getPackageName").invoke(this) as JCTree? JCTree.JCCompilationUnit::class.java.getDeclaredMethod("getPackageName").invoke(this) as JCTree?
} } else {
else {
this.packageName this.packageName
} }
} }
@@ -0,0 +1,58 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.kapt3.base
import com.sun.tools.javac.util.List as JavacList
inline fun <T, R> mapJList(values: Iterable<T>?, f: (T) -> R?): JavacList<R> {
if (values == null) return JavacList.nil()
var result = JavacList.nil<R>()
for (item in values) {
f(item)?.let { result = result.append(it) }
}
return result
}
inline fun <T, R> mapJListIndexed(values: Iterable<T>?, f: (Int, T) -> R?): JavacList<R> {
if (values == null) return JavacList.nil()
var result = JavacList.nil<R>()
values.forEachIndexed { index, item ->
f(index, item)?.let { result = result.append(it) }
}
return result
}
inline fun <T> mapPairedValuesJList(valuePairs: List<Any>?, f: (String, Any) -> T?): JavacList<T> {
if (valuePairs == null || valuePairs.isEmpty()) return JavacList.nil()
val size = valuePairs.size
var result = JavacList.nil<T>()
assert(size % 2 == 0)
var index = 0
while (index < size) {
val key = valuePairs[index] as String
val value = valuePairs[index + 1]
f(key, value)?.let { result = result.prepend(it) }
index += 2
}
return result.reverse()
}
fun pairedListToMap(valuePairs: List<Any>?): Map<String, Any?> {
val map = mutableMapOf<String, Any?>()
mapPairedValuesJList(valuePairs) { key, value ->
map.put(key, value)
}
return map
}
operator fun <T : Any> JavacList<T>.plus(other: JavacList<T>): JavacList<T> {
return this.appendList(other)
}
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.kapt3.base.util
inline fun <T> measureTimeMillisWithResult(block: () -> T) : Pair<Long, T> {
val start = System.currentTimeMillis()
val result = block()
return Pair(System.currentTimeMillis() - start, result)
}
@@ -1,32 +1,15 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* * that can be found in the license/LICENSE.txt file.
* 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.test import org.jetbrains.kotlin.kapt3.base.KaptContext
import org.jetbrains.kotlin.kapt3.base.KaptPaths
import com.intellij.openapi.command.impl.DummyProject import org.jetbrains.kotlin.kapt3.base.doAnnotationProcessing
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer import org.jetbrains.kotlin.kapt3.base.util.KaptBaseError
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector import org.jetbrains.kotlin.kapt3.base.util.WriterBackedKaptLogger
import org.jetbrains.kotlin.kapt3.AptMode import org.junit.Assert.assertEquals
import org.jetbrains.kotlin.kapt3.KaptContext import org.junit.Assert.assertTrue
import org.jetbrains.kotlin.kapt3.KaptPaths
import org.jetbrains.kotlin.kapt3.diagnostic.KaptError
import org.jetbrains.kotlin.kapt3.doAnnotationProcessing
import org.jetbrains.kotlin.kapt3.util.KaptLogger
import org.jetbrains.kotlin.resolve.BindingContext
import org.junit.Assert.*
import org.junit.Test import org.junit.Test
import java.io.File import java.io.File
import java.nio.file.Files import java.nio.file.Files
@@ -35,26 +18,9 @@ import javax.annotation.processing.Processor
import javax.annotation.processing.RoundEnvironment import javax.annotation.processing.RoundEnvironment
import javax.lang.model.element.TypeElement import javax.lang.model.element.TypeElement
/*
* 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.
*/
class JavaKaptContextTest { class JavaKaptContextTest {
companion object { companion object {
private val TEST_DATA_DIR = File("plugins/kapt3/kapt3-compiler/testData/runner") private val TEST_DATA_DIR = File("plugins/kapt3/kapt3-base/testData/runner")
val messageCollector = PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, false)
fun simpleProcessor() = object : AbstractProcessor() { fun simpleProcessor() = object : AbstractProcessor() {
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean { override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
@@ -84,6 +50,7 @@ class JavaKaptContextTest {
private fun doAnnotationProcessing(javaSourceFile: File, processor: Processor, outputDir: File) { private fun doAnnotationProcessing(javaSourceFile: File, processor: Processor, outputDir: File) {
KaptContext( KaptContext(
KaptPaths( KaptPaths(
projectBaseDir = javaSourceFile.parentFile,
compileClasspath = emptyList(), compileClasspath = emptyList(),
annotationProcessingClasspath = emptyList(), annotationProcessingClasspath = emptyList(),
javaSourceRoots = emptyList(), javaSourceRoots = emptyList(),
@@ -93,13 +60,7 @@ class JavaKaptContextTest {
incrementalDataOutputDir = outputDir incrementalDataOutputDir = outputDir
), ),
withJdk = true, withJdk = true,
aptMode = AptMode.STUBS_AND_APT, logger = WriterBackedKaptLogger(isVerbose = true),
logger = KaptLogger(isVerbose = true, messageCollector = messageCollector),
project = DummyProject.getInstance(),
bindingContext = BindingContext.EMPTY,
compiledClasses = emptyList(),
origins = emptyMap(),
generationState = null,
mapDiagnosticLocations = true, mapDiagnosticLocations = true,
processorOptions = emptyMap() processorOptions = emptyMap()
).doAnnotationProcessing(listOf(javaSourceFile), listOf(processor)) ).doAnnotationProcessing(listOf(javaSourceFile), listOf(processor))
@@ -117,7 +78,7 @@ class JavaKaptContextTest {
} }
} }
@Test(expected = KaptError::class) @Test(expected = KaptBaseError::class)
fun testException() { fun testException() {
val exceptionMessage = "Here we are!" val exceptionMessage = "Here we are!"
@@ -131,19 +92,19 @@ class JavaKaptContextTest {
try { try {
doAnnotationProcessing(File(TEST_DATA_DIR, "Simple.java"), processor, TEST_DATA_DIR) doAnnotationProcessing(File(TEST_DATA_DIR, "Simple.java"), processor, TEST_DATA_DIR)
} catch (e: KaptError) { } catch (e: KaptBaseError) {
assertEquals(KaptError.Kind.EXCEPTION, e.kind) assertEquals(KaptBaseError.Kind.EXCEPTION, e.kind)
assertEquals("Here we are!", e.cause!!.message) assertEquals("Here we are!", e.cause!!.message)
throw e throw e
} }
} }
@Test(expected = KaptError::class) @Test(expected = KaptBaseError::class)
fun testParsingError() { fun testParsingError() {
try { try {
doAnnotationProcessing(File(TEST_DATA_DIR, "ParseError.java"), simpleProcessor(), TEST_DATA_DIR) doAnnotationProcessing(File(TEST_DATA_DIR, "ParseError.java"), simpleProcessor(), TEST_DATA_DIR)
} catch (e: KaptError) { } catch (e: KaptBaseError) {
assertEquals(KaptError.Kind.ERROR_RAISED, e.kind) assertEquals(KaptBaseError.Kind.ERROR_RAISED, e.kind)
throw e throw e
} }
} }
@@ -17,16 +17,20 @@ dependencies {
compile(project(":compiler:frontend")) compile(project(":compiler:frontend"))
compile(project(":compiler:frontend.java")) compile(project(":compiler:frontend.java"))
compile(project(":compiler:plugin-api")) compile(project(":compiler:plugin-api"))
compileOnly(project(":kotlin-annotation-processing-base"))
compileOnly(project(":kotlin-annotation-processing-runtime")) compileOnly(project(":kotlin-annotation-processing-runtime"))
compileOnly(intellijCoreDep()) { includeJars("intellij-core") } compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
compileOnly(intellijDep()) { includeJars("asm-all") } compileOnly(intellijDep()) { includeJars("asm-all") }
testCompile(project(":compiler:tests-common")) testCompile(project(":compiler:tests-common"))
testCompile(projectTests(":compiler:tests-common")) testCompile(projectTests(":compiler:tests-common"))
testCompile(project(":kotlin-annotation-processing-base"))
testCompile(projectTests(":kotlin-annotation-processing-base"))
testCompile(commonDep("junit:junit")) testCompile(commonDep("junit:junit"))
testCompile(project(":kotlin-annotation-processing-runtime")) testCompile(project(":kotlin-annotation-processing-runtime"))
embeddedComponents(project(":kotlin-annotation-processing-runtime")) { isTransitive = false } embeddedComponents(project(":kotlin-annotation-processing-runtime")) { isTransitive = false }
embeddedComponents(project(":kotlin-annotation-processing-base")) { isTransitive = false }
} }
sourceSets { sourceSets {
@@ -17,16 +17,20 @@ dependencies {
compile(project(":compiler:frontend")) compile(project(":compiler:frontend"))
compile(project(":compiler:frontend.java")) compile(project(":compiler:frontend.java"))
compile(project(":compiler:plugin-api")) compile(project(":compiler:plugin-api"))
compileOnly(project(":kotlin-annotation-processing-base"))
compileOnly(project(":kotlin-annotation-processing-runtime")) compileOnly(project(":kotlin-annotation-processing-runtime"))
compileOnly(intellijCoreDep()) { includeJars("intellij-core") } compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
compileOnly(intellijDep()) { includeJars("asm-all") } compileOnly(intellijDep()) { includeJars("asm-all") }
testCompile(project(":compiler:tests-common")) testCompile(project(":compiler:tests-common"))
testCompile(projectTests(":compiler:tests-common")) testCompile(projectTests(":compiler:tests-common"))
testCompile(project(":kotlin-annotation-processing-base"))
testCompile(projectTests(":kotlin-annotation-processing-base"))
testCompile(commonDep("junit:junit")) testCompile(commonDep("junit:junit"))
testCompile(project(":kotlin-annotation-processing-runtime")) testCompile(project(":kotlin-annotation-processing-runtime"))
embeddedComponents(project(":kotlin-annotation-processing-runtime")) { isTransitive = false } embeddedComponents(project(":kotlin-annotation-processing-runtime")) { isTransitive = false }
embeddedComponents(project(":kotlin-annotation-processing-base")) { isTransitive = false }
} }
sourceSets { sourceSets {
@@ -17,16 +17,20 @@ dependencies {
compile(project(":compiler:frontend")) compile(project(":compiler:frontend"))
compile(project(":compiler:frontend.java")) compile(project(":compiler:frontend.java"))
compile(project(":compiler:plugin-api")) compile(project(":compiler:plugin-api"))
compileOnly(project(":kotlin-annotation-processing-base"))
compileOnly(project(":kotlin-annotation-processing-runtime")) compileOnly(project(":kotlin-annotation-processing-runtime"))
compileOnly(intellijCoreDep()) { includeJars("intellij-core") } compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
compileOnly(intellijDep()) { includeJars("asm-all") } compileOnly(intellijDep()) { includeJars("asm-all") }
testCompile(project(":compiler:tests-common")) testCompile(project(":compiler:tests-common"))
testCompile(projectTests(":compiler:tests-common")) testCompile(projectTests(":compiler:tests-common"))
testCompile(project(":kotlin-annotation-processing-base"))
testCompile(projectTests(":kotlin-annotation-processing-base"))
testCompile(commonDep("junit:junit")) testCompile(commonDep("junit:junit"))
testCompile(project(":kotlin-annotation-processing-runtime")) testCompile(project(":kotlin-annotation-processing-runtime"))
embeddedComponents(project(":kotlin-annotation-processing-runtime")) { isTransitive = false } embeddedComponents(project(":kotlin-annotation-processing-runtime")) { isTransitive = false }
embeddedComponents(project(":kotlin-annotation-processing-base")) { isTransitive = false }
} }
sourceSets { sourceSets {
@@ -16,14 +16,12 @@
package org.jetbrains.kotlin.kapt3 package org.jetbrains.kotlin.kapt3
import com.intellij.ide.ClassUtilCore
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.sun.tools.javac.code.Flags import com.sun.tools.javac.code.Flags
import com.sun.tools.javac.tree.JCTree import com.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.tree.Pretty import com.sun.tools.javac.tree.Pretty
import com.sun.tools.javac.tree.TreeMaker import com.sun.tools.javac.tree.TreeMaker
import com.sun.tools.javac.util.Context import com.sun.tools.javac.util.Context
import com.sun.tools.javac.util.Convert
import org.jetbrains.kotlin.analyzer.AnalysisResult import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.backend.common.output.OutputFile import org.jetbrains.kotlin.backend.common.output.OutputFile
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.OUTPUT import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.OUTPUT
@@ -39,39 +37,29 @@ import org.jetbrains.kotlin.container.ComponentProvider
import org.jetbrains.kotlin.context.ProjectContext import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.kapt3.AptMode.* import org.jetbrains.kotlin.kapt3.AptMode.*
import org.jetbrains.kotlin.kapt3.base.KaptContext
import org.jetbrains.kotlin.kapt3.base.KaptPaths
import org.jetbrains.kotlin.kapt3.base.ProcessorLoader
import org.jetbrains.kotlin.kapt3.base.doAnnotationProcessing
import org.jetbrains.kotlin.kapt3.base.util.KaptBaseError
import org.jetbrains.kotlin.kapt3.diagnostic.KaptError import org.jetbrains.kotlin.kapt3.diagnostic.KaptError
import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter
import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter.KaptStub import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter.KaptStub
import org.jetbrains.kotlin.kapt3.stubs.KaptLineMappingCollector.Companion.KAPT_METADATA_EXTENSION import org.jetbrains.kotlin.kapt3.base.stubs.KaptStubLineInformation.Companion.KAPT_METADATA_EXTENSION
import org.jetbrains.kotlin.kapt3.util.KaptLogger import org.jetbrains.kotlin.kapt3.base.util.getPackageNameJava9Aware
import org.jetbrains.kotlin.kapt3.util.getPackageNameJava9Aware import org.jetbrains.kotlin.kapt3.base.util.info
import org.jetbrains.kotlin.kapt3.util.MessageCollectorBackedKaptLogger
import org.jetbrains.kotlin.modules.TargetId import org.jetbrains.kotlin.modules.TargetId
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension import org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension
import java.io.File import java.io.File
import java.io.IOException
import java.io.StringWriter import java.io.StringWriter
import java.io.Writer import java.io.Writer
import java.net.URLClassLoader
import java.util.*
import javax.annotation.processing.Processor import javax.annotation.processing.Processor
import com.sun.tools.javac.util.List as JavacList import com.sun.tools.javac.util.List as JavacList
class KaptPaths(
compileClasspath: List<File>,
annotationProcessingClasspath: List<File>,
val javaSourceRoots: List<File>,
val sourcesOutputDir: File,
val classFilesOutputDir: File,
val stubsOutputDir: File,
val incrementalDataOutputDir: File?
) {
val compileClasspath = compileClasspath.distinct()
val annotationProcessingClasspath = annotationProcessingClasspath.distinct()
}
class ClasspathBasedKapt3Extension( class ClasspathBasedKapt3Extension(
paths: KaptPaths, paths: KaptPaths,
options: Map<String, String>, options: Map<String, String>,
@@ -82,14 +70,18 @@ class ClasspathBasedKapt3Extension(
correctErrorTypes: Boolean, correctErrorTypes: Boolean,
mapDiagnosticLocations: Boolean, mapDiagnosticLocations: Boolean,
pluginInitializedTime: Long, pluginInitializedTime: Long,
logger: KaptLogger, logger: MessageCollectorBackedKaptLogger,
compilerConfiguration: CompilerConfiguration compilerConfiguration: CompilerConfiguration
) : AbstractKapt3Extension(paths, options, javacOptions, annotationProcessorFqNames, ) : AbstractKapt3Extension(paths, options, javacOptions, annotationProcessorFqNames,
aptMode, pluginInitializedTime, logger, correctErrorTypes, mapDiagnosticLocations, compilerConfiguration) { aptMode, pluginInitializedTime, logger, correctErrorTypes, mapDiagnosticLocations, compilerConfiguration) {
override val analyzePartially: Boolean override val analyzePartially: Boolean
get() = useLightAnalysis get() = useLightAnalysis
private var annotationProcessingClassLoader: URLClassLoader? = null private var processorLoader: ProcessorLoader? = null
override fun loadProcessors(): List<Processor> {
return ProcessorLoader(paths, annotationProcessorFqNames, logger).also { this.processorLoader = it }.loadProcessors()
}
override fun analysisCompleted( override fun analysisCompleted(
project: Project, project: Project,
@@ -100,54 +92,7 @@ class ClasspathBasedKapt3Extension(
try { try {
return super.analysisCompleted(project, module, bindingTrace, files) return super.analysisCompleted(project, module, bindingTrace, files)
} finally { } finally {
annotationProcessingClassLoader?.close() processorLoader?.close()
ClassUtilCore.clearJarURLCache()
}
}
override fun loadProcessors(): List<Processor> {
ClassUtilCore.clearJarURLCache()
val classpath = (paths.annotationProcessingClasspath + paths.compileClasspath).distinct()
val classLoader = URLClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray())
this.annotationProcessingClassLoader = classLoader
val processors = if (annotationProcessorFqNames.isNotEmpty()) {
logger.info("Annotation processor class names are set, skip AP discovery")
annotationProcessorFqNames.mapNotNull { tryLoadProcessor(it, classLoader) }
} else {
logger.info("Need to discovery annotation processors in the AP classpath")
ServiceLoader.load(Processor::class.java, classLoader).toList()
}
if (processors.isEmpty()) {
logger.info("No annotation processors available, aborting")
} else {
logger.info { "Annotation processors: " + processors.joinToString { it::class.java.canonicalName } }
}
return processors
}
private fun tryLoadProcessor(fqName: String, classLoader: ClassLoader): Processor? {
val annotationProcessorClass = try {
Class.forName(fqName, true, classLoader)
} catch (e: Throwable) {
logger.warn("Can't find annotation processor class $fqName: ${e.message}")
return null
}
try {
val annotationProcessorInstance = annotationProcessorClass.newInstance()
if (annotationProcessorInstance !is Processor) {
logger.warn("$fqName is not an instance of 'Processor'")
return null
}
return annotationProcessorInstance
} catch (e: Throwable) {
logger.warn("Can't load annotation processor class $fqName: ${e.message}")
return null
} }
} }
} }
@@ -159,7 +104,7 @@ abstract class AbstractKapt3Extension(
val annotationProcessorFqNames: List<String>, val annotationProcessorFqNames: List<String>,
val aptMode: AptMode, val aptMode: AptMode,
val pluginInitializedTime: Long, val pluginInitializedTime: Long,
val logger: KaptLogger, val logger: MessageCollectorBackedKaptLogger,
val correctErrorTypes: Boolean, val correctErrorTypes: Boolean,
val mapDiagnosticLocations: Boolean, val mapDiagnosticLocations: Boolean,
val compilerConfiguration: CompilerConfiguration val compilerConfiguration: CompilerConfiguration
@@ -205,9 +150,7 @@ abstract class AbstractKapt3Extension(
val kaptContext = generateStubs(project, module, bindingTrace.bindingContext, files) val kaptContext = generateStubs(project, module, bindingTrace.bindingContext, files)
try { fun handleKaptError(error: KaptError): AnalysisResult {
runAnnotationProcessing(kaptContext, processors)
} catch (error: KaptError) {
val cause = error.cause val cause = error.cause
if (cause != null) { if (cause != null) {
@@ -215,6 +158,20 @@ abstract class AbstractKapt3Extension(
} }
return AnalysisResult.compilationError(bindingTrace.bindingContext) return AnalysisResult.compilationError(bindingTrace.bindingContext)
}
try {
runAnnotationProcessing(kaptContext, processors)
} catch (error: KaptBaseError) {
val kind = when (error.kind) {
KaptBaseError.Kind.EXCEPTION -> KaptError.Kind.EXCEPTION
KaptBaseError.Kind.ERROR_RAISED -> KaptError.Kind.ERROR_RAISED
}
val cause = error.cause
return handleKaptError(if (cause != null) KaptError(kind, cause) else KaptError(kind))
} catch (error: KaptError) {
return handleKaptError(error)
} catch (thr: Throwable) { } catch (thr: Throwable) {
return AnalysisResult.internalError(bindingTrace.bindingContext, thr) return AnalysisResult.internalError(bindingTrace.bindingContext, thr)
} finally { } finally {
@@ -232,10 +189,14 @@ abstract class AbstractKapt3Extension(
} }
} }
private fun generateStubs(project: Project, module: ModuleDescriptor, context: BindingContext, files: Collection<KtFile>): KaptContext<*> { private fun generateStubs(
project: Project,
module: ModuleDescriptor,
context: BindingContext,
files: Collection<KtFile>
): KaptContext {
if (!aptMode.generateStubs) { if (!aptMode.generateStubs) {
return KaptContext(paths, false, aptMode, logger, project, BindingContext.EMPTY, emptyList(), emptyMap(), null, return KaptContext(paths, false, logger, mapDiagnosticLocations, options, javacOptions)
mapDiagnosticLocations, options, javacOptions)
} }
logger.info { "Kotlin files to compile: " + files.map { it.virtualFile?.name ?: "<in memory ${it.hashCode()}>" } } logger.info { "Kotlin files to compile: " + files.map { it.virtualFile?.name ?: "<in memory ${it.hashCode()}>" } }
@@ -245,10 +206,11 @@ abstract class AbstractKapt3Extension(
} }
} }
private fun runAnnotationProcessing(kaptContext: KaptContext<*>, processors: List<Processor>) { private fun runAnnotationProcessing(kaptContext: KaptContext, processors: List<Processor>) {
if (!aptMode.runAnnotationProcessing) return if (!aptMode.runAnnotationProcessing) return
val javaSourceFiles = collectJavaSourceFiles() val javaSourceFiles = paths.collectJavaSourceFiles()
logger.info { "Java source files: " + javaSourceFiles.joinToString { it.canonicalPath } }
val (annotationProcessingTime) = measureTimeMillis { val (annotationProcessingTime) = measureTimeMillis {
kaptContext.doAnnotationProcessing(javaSourceFiles, processors) kaptContext.doAnnotationProcessing(javaSourceFiles, processors)
@@ -262,7 +224,7 @@ abstract class AbstractKapt3Extension(
module: ModuleDescriptor, module: ModuleDescriptor,
bindingContext: BindingContext, bindingContext: BindingContext,
files: List<KtFile> files: List<KtFile>
): KaptContext<GenerationState> { ): KaptContextForStubGeneration {
val builderFactory = Kapt3BuilderFactory() val builderFactory = Kapt3BuilderFactory()
val targetId = TargetId( val targetId = TargetId(
@@ -288,11 +250,13 @@ abstract class AbstractKapt3Extension(
logger.info { "Stubs compilation took $classFilesCompilationTime ms" } logger.info { "Stubs compilation took $classFilesCompilationTime ms" }
logger.info { "Compiled classes: " + compiledClasses.joinToString { it.name } } logger.info { "Compiled classes: " + compiledClasses.joinToString { it.name } }
return KaptContext(paths, false, aptMode, logger, project, bindingContext, compiledClasses, origins, generationState, return KaptContextForStubGeneration(
mapDiagnosticLocations, options, javacOptions) paths, false, logger, project, bindingContext, compiledClasses, origins, generationState,
mapDiagnosticLocations, options, javacOptions
)
} }
private fun generateKotlinSourceStubs(kaptContext: KaptContext<GenerationState>) { private fun generateKotlinSourceStubs(kaptContext: KaptContextForStubGeneration) {
val converter = ClassFileToSourceStubConverter(kaptContext, generateNonExistentClass = true, correctErrorTypes = correctErrorTypes) val converter = ClassFileToSourceStubConverter(kaptContext, generateNonExistentClass = true, correctErrorTypes = correctErrorTypes)
val (stubGenerationTime, kaptStubs) = measureTimeMillis { val (stubGenerationTime, kaptStubs) = measureTimeMillis {
@@ -306,16 +270,7 @@ abstract class AbstractKapt3Extension(
saveIncrementalData(kaptContext, logger.messageCollector, converter) saveIncrementalData(kaptContext, logger.messageCollector, converter)
} }
private fun collectJavaSourceFiles(): List<File> { protected open fun saveStubs(kaptContext: KaptContext, stubs: List<KaptStub>) {
val javaFilesFromJavaSourceRoots = (paths.javaSourceRoots + paths.stubsOutputDir).flatMap {
root -> root.walk().filter { it.isFile && it.extension == "java" }.toList()
}
logger.info { "Java source files: " + javaFilesFromJavaSourceRoots.joinToString { it.canonicalPath } }
return javaFilesFromJavaSourceRoots
}
protected open fun saveStubs(kaptContext: KaptContext<*>, stubs: List<KaptStub>) {
for (kaptStub in stubs) { for (kaptStub in stubs) {
val stub = kaptStub.file val stub = kaptStub.file
val className = (stub.defs.first { it is JCTree.JCClassDecl } as JCTree.JCClassDecl).simpleName.toString() val className = (stub.defs.first { it is JCTree.JCClassDecl } as JCTree.JCClassDecl).simpleName.toString()
@@ -332,7 +287,7 @@ abstract class AbstractKapt3Extension(
} }
protected open fun saveIncrementalData( protected open fun saveIncrementalData(
kaptContext: KaptContext<GenerationState>, kaptContext: KaptContextForStubGeneration,
messageCollector: MessageCollector, messageCollector: MessageCollector,
converter: ClassFileToSourceStubConverter) { converter: ClassFileToSourceStubConverter) {
val incrementalDataOutputDir = paths.incrementalDataOutputDir ?: return val incrementalDataOutputDir = paths.incrementalDataOutputDir ?: return
@@ -52,7 +52,10 @@ import org.jetbrains.kotlin.kapt3.Kapt3CommandLineProcessor.Companion.SOURCE_OUT
import org.jetbrains.kotlin.kapt3.Kapt3CommandLineProcessor.Companion.STUBS_OUTPUT_DIR_OPTION import org.jetbrains.kotlin.kapt3.Kapt3CommandLineProcessor.Companion.STUBS_OUTPUT_DIR_OPTION
import org.jetbrains.kotlin.kapt3.Kapt3CommandLineProcessor.Companion.USE_LIGHT_ANALYSIS_OPTION import org.jetbrains.kotlin.kapt3.Kapt3CommandLineProcessor.Companion.USE_LIGHT_ANALYSIS_OPTION
import org.jetbrains.kotlin.kapt3.Kapt3CommandLineProcessor.Companion.VERBOSE_MODE_OPTION import org.jetbrains.kotlin.kapt3.Kapt3CommandLineProcessor.Companion.VERBOSE_MODE_OPTION
import org.jetbrains.kotlin.kapt3.util.KaptLogger import org.jetbrains.kotlin.kapt3.base.Kapt
import org.jetbrains.kotlin.kapt3.base.KaptPaths
import org.jetbrains.kotlin.kapt3.base.log
import org.jetbrains.kotlin.kapt3.util.MessageCollectorBackedKaptLogger
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
@@ -195,10 +198,6 @@ class Kapt3CommandLineProcessor : CommandLineProcessor {
} }
class Kapt3ComponentRegistrar : ComponentRegistrar { class Kapt3ComponentRegistrar : ComponentRegistrar {
private companion object {
private const val JAVAC_CONTEXT_CLASS = "com.sun.tools.javac.util.Context"
}
private fun decodeList(options: String): Map<String, String> { private fun decodeList(options: String): Map<String, String> {
val map = LinkedHashMap<String, String>() val map = LinkedHashMap<String, String>()
@@ -224,14 +223,11 @@ class Kapt3ComponentRegistrar : ComponentRegistrar {
val isVerbose = configuration.get(Kapt3ConfigurationKeys.VERBOSE_MODE) == "true" val isVerbose = configuration.get(Kapt3ConfigurationKeys.VERBOSE_MODE) == "true"
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
?: PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, isVerbose) ?: PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, isVerbose)
val logger = KaptLogger(isVerbose, messageCollector) val logger = MessageCollectorBackedKaptLogger(isVerbose, messageCollector)
fun abortAnalysis() = AnalysisHandlerExtension.registerExtension(project, AbortAnalysisHandlerExtension()) fun abortAnalysis() = AnalysisHandlerExtension.registerExtension(project, AbortAnalysisHandlerExtension())
try { if (!Kapt.checkJavacComponentsAccess(logger)) {
Class.forName(JAVAC_CONTEXT_CLASS)
} catch (e: ClassNotFoundException) {
logger.error("'$JAVAC_CONTEXT_CLASS' class can't be found ('tools.jar' is absent in the plugin classpath). Kapt won't work.")
abortAnalysis() abortAnalysis()
return return
} }
@@ -284,28 +280,24 @@ class Kapt3ComponentRegistrar : ComponentRegistrar {
val correctErrorTypes = configuration.get(Kapt3ConfigurationKeys.CORRECT_ERROR_TYPES) == "true" val correctErrorTypes = configuration.get(Kapt3ConfigurationKeys.CORRECT_ERROR_TYPES) == "true"
val mapDiagnosticLocations = configuration.get(Kapt3ConfigurationKeys.MAP_DIAGNOSTIC_LOCATIONS) == "true" val mapDiagnosticLocations = configuration.get(Kapt3ConfigurationKeys.MAP_DIAGNOSTIC_LOCATIONS) == "true"
val paths = KaptPaths(
project.basePath?.let(::File),
compileClasspath, apClasspath, javaSourceRoots, sourcesOutputDir, classFilesOutputDir,
stubsOutputDir, incrementalDataOutputDir
)
if (isVerbose) { if (isVerbose) {
logger.info("Kapt3 is enabled.") logger.info("Kapt3 is enabled.")
logger.info("Annotation processing mode: $aptMode") logger.info("Annotation processing mode: $aptMode")
logger.info("Use light analysis: $useLightAnalysis") logger.info("Use light analysis: $useLightAnalysis")
logger.info("Correct error types: $correctErrorTypes") logger.info("Correct error types: $correctErrorTypes")
logger.info("Map diagnostic locations: $mapDiagnosticLocations") logger.info("Map diagnostic locations: $mapDiagnosticLocations")
logger.info("Source output directory: $sourcesOutputDir") paths.log(logger)
logger.info("Classes output directory: $classFilesOutputDir")
logger.info("Stubs output directory: $stubsOutputDir")
logger.info("Incremental data output directory: $incrementalDataOutputDir")
logger.info("Compile classpath: " + compileClasspath.joinToString())
logger.info("Annotation processing classpath: " + apClasspath.joinToString())
logger.info("Annotation processors: " + annotationProcessors.joinToString()) logger.info("Annotation processors: " + annotationProcessors.joinToString())
logger.info("Java source roots: " + javaSourceRoots.joinToString()) logger.info("Javac options: $apOptions")
logger.info("Options: $apOptions") logger.info("AP options: $apOptions")
} }
val paths = KaptPaths(
compileClasspath, apClasspath, javaSourceRoots, sourcesOutputDir, classFilesOutputDir,
stubsOutputDir, incrementalDataOutputDir
)
val kapt3AnalysisCompletedHandlerExtension = ClasspathBasedKapt3Extension( val kapt3AnalysisCompletedHandlerExtension = ClasspathBasedKapt3Extension(
paths, apOptions, javacCliOptions, annotationProcessors, paths, apOptions, javacCliOptions, annotationProcessors,
aptMode, useLightAnalysis, correctErrorTypes, mapDiagnosticLocations, System.currentTimeMillis(), logger, configuration aptMode, useLightAnalysis, correctErrorTypes, mapDiagnosticLocations, System.currentTimeMillis(), logger, configuration
@@ -0,0 +1,55 @@
/*
* 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 com.sun.tools.javac.tree.TreeMaker
import com.sun.tools.javac.util.Context
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.kapt3.base.KaptContext
import org.jetbrains.kotlin.kapt3.base.KaptPaths
import org.jetbrains.kotlin.kapt3.base.util.KaptLogger
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.org.objectweb.asm.tree.ClassNode
class KaptContextForStubGeneration(
paths: KaptPaths,
withJdk: Boolean,
logger: KaptLogger,
val project: Project,
val bindingContext: BindingContext,
val compiledClasses: List<ClassNode>,
val origins: Map<Any, JvmDeclarationOrigin>,
val generationState: GenerationState,
mapDiagnosticLocations: Boolean,
processorOptions: Map<String, String>,
javacOptions: Map<String, String> = emptyMap()
) : KaptContext(paths, withJdk, logger, mapDiagnosticLocations, processorOptions, javacOptions) {
private val treeMaker = TreeMaker.instance(context)
override fun preregisterTreeMaker(context: Context) {
KaptTreeMaker.preRegister(context, this)
}
override fun close() {
(treeMaker as? KaptTreeMaker)?.dispose()
generationState.destroy()
super.close()
}
}
@@ -17,10 +17,9 @@
package org.jetbrains.kotlin.kapt3.javac package org.jetbrains.kotlin.kapt3.javac
import com.sun.tools.javac.tree.JCTree import com.sun.tools.javac.tree.JCTree
import org.jetbrains.kotlin.kapt3.util.getPackageNameJava9Aware import org.jetbrains.kotlin.kapt3.base.util.getPackageNameJava9Aware
import java.io.File import java.io.File
import java.net.URI import java.net.URI
import java.net.URL
import javax.lang.model.element.Modifier import javax.lang.model.element.Modifier
import javax.lang.model.element.NestingKind import javax.lang.model.element.NestingKind
import javax.tools.JavaFileObject import javax.tools.JavaFileObject
@@ -27,13 +27,13 @@ import com.sun.tools.javac.util.Context
import com.sun.tools.javac.util.Name import com.sun.tools.javac.util.Name
import com.sun.tools.javac.util.Names import com.sun.tools.javac.util.Names
import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.kapt3.KaptContext import org.jetbrains.kotlin.kapt3.KaptContextForStubGeneration
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.Type.* import org.jetbrains.org.objectweb.asm.Type.*
import org.jetbrains.org.objectweb.asm.tree.ClassNode import org.jetbrains.org.objectweb.asm.tree.ClassNode
class KaptTreeMaker(context: Context, kaptContext: KaptContext<*>) : TreeMaker(context), Disposable { class KaptTreeMaker(context: Context, kaptContext: KaptContextForStubGeneration) : TreeMaker(context), Disposable {
private var kaptContext = DisposableReference(kaptContext) private var kaptContext = DisposableReference(kaptContext)
val nameTable: Name.Table = Names.instance(context).table val nameTable: Name.Table = Names.instance(context).table
@@ -171,7 +171,7 @@ class KaptTreeMaker(context: Context, kaptContext: KaptContext<*>) : TreeMaker(c
} }
companion object { companion object {
internal fun preRegister(context: Context, kaptContext: KaptContext<*>) { internal fun preRegister(context: Context, kaptContext: KaptContextForStubGeneration) {
context.put(treeMakerKey, Context.Factory<TreeMaker> { KaptTreeMaker(it, kaptContext) }) context.put(treeMakerKey, Context.Factory<TreeMaker> { KaptTreeMaker(it, kaptContext) })
} }
} }
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.kapt3.mapJList import org.jetbrains.kotlin.kapt3.base.mapJList
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
@@ -24,14 +24,19 @@ import com.sun.tools.javac.tree.JCTree.*
import com.sun.tools.javac.tree.TreeMaker import com.sun.tools.javac.tree.TreeMaker
import com.sun.tools.javac.tree.TreeScanner import com.sun.tools.javac.tree.TreeScanner
import kotlinx.kapt.KaptIgnored import kotlinx.kapt.KaptIgnored
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.kapt3.* import org.jetbrains.kotlin.kapt3.*
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject
import org.jetbrains.kotlin.kapt3.javac.kaptError import org.jetbrains.kotlin.kapt3.base.plus
import org.jetbrains.kotlin.kapt3.base.javac.kaptError
import org.jetbrains.kotlin.kapt3.base.mapJList
import org.jetbrains.kotlin.kapt3.base.mapJListIndexed
import org.jetbrains.kotlin.kapt3.base.pairedListToMap
import org.jetbrains.kotlin.kapt3.base.util.TopLevelJava9Aware
import org.jetbrains.kotlin.kapt3.base.stubs.KaptStubLineInformation
import org.jetbrains.kotlin.kapt3.stubs.ErrorTypeCorrector.TypeKind.* import org.jetbrains.kotlin.kapt3.stubs.ErrorTypeCorrector.TypeKind.*
import org.jetbrains.kotlin.kapt3.util.* import org.jetbrains.kotlin.kapt3.util.*
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
@@ -55,7 +60,7 @@ import javax.lang.model.element.ElementKind
import com.sun.tools.javac.util.List as JavacList import com.sun.tools.javac.util.List as JavacList
class ClassFileToSourceStubConverter( class ClassFileToSourceStubConverter(
val kaptContext: KaptContext<GenerationState>, val kaptContext: KaptContextForStubGeneration,
val generateNonExistentClass: Boolean, val generateNonExistentClass: Boolean,
val correctErrorTypes: Boolean val correctErrorTypes: Boolean
) { ) {
@@ -146,7 +151,7 @@ class ClassFileToSourceStubConverter(
val metadataFile = File( val metadataFile = File(
forSource.parentFile, forSource.parentFile,
forSource.nameWithoutExtension + KaptLineMappingCollector.KAPT_METADATA_EXTENSION forSource.nameWithoutExtension + KaptStubLineInformation.KAPT_METADATA_EXTENSION
) )
metadataFile.writeBytes(kaptMetadata) metadataFile.writeBytes(kaptMetadata)
@@ -22,9 +22,9 @@ import com.sun.tools.javac.tree.JCTree
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.codegen.state.updateArgumentModeFromAnnotations import org.jetbrains.kotlin.codegen.state.updateArgumentModeFromAnnotations
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.kapt3.javac.kaptError import org.jetbrains.kotlin.kapt3.base.javac.kaptError
import org.jetbrains.kotlin.kapt3.mapJList import org.jetbrains.kotlin.kapt3.base.mapJList
import org.jetbrains.kotlin.kapt3.mapJListIndexed import org.jetbrains.kotlin.kapt3.base.mapJListIndexed
import org.jetbrains.kotlin.kapt3.stubs.ErrorTypeCorrector.TypeKind.METHOD_PARAMETER_TYPE import org.jetbrains.kotlin.kapt3.stubs.ErrorTypeCorrector.TypeKind.METHOD_PARAMETER_TYPE
import org.jetbrains.kotlin.kapt3.stubs.ErrorTypeCorrector.TypeKind.RETURN_TYPE import org.jetbrains.kotlin.kapt3.stubs.ErrorTypeCorrector.TypeKind.RETURN_TYPE
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
@@ -26,7 +26,7 @@ import com.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.tree.TreeScanner import com.sun.tools.javac.tree.TreeScanner
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor
import org.jetbrains.kotlin.kapt3.KaptContext import org.jetbrains.kotlin.kapt3.KaptContextForStubGeneration
import org.jetbrains.kotlin.kdoc.lexer.KDocTokens import org.jetbrains.kotlin.kdoc.lexer.KDocTokens
import org.jetbrains.kotlin.kdoc.psi.api.KDoc import org.jetbrains.kotlin.kdoc.psi.api.KDoc
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
@@ -35,7 +35,7 @@ import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.FieldNode import org.jetbrains.org.objectweb.asm.tree.FieldNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode import org.jetbrains.org.objectweb.asm.tree.MethodNode
class KDocCommentKeeper(private val kaptContext: KaptContext<*>) { class KDocCommentKeeper(private val kaptContext: KaptContextForStubGeneration) {
private val docCommentTable = KaptDocCommentTable() private val docCommentTable = KaptDocCommentTable()
fun getDocTable(file: JCTree.JCCompilationUnit): DocCommentTable { fun getDocTable(file: JCTree.JCCompilationUnit): DocCommentTable {
@@ -19,75 +19,17 @@ package org.jetbrains.kotlin.kapt3.stubs
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.sun.tools.javac.tree.JCTree import com.sun.tools.javac.tree.JCTree
import org.jetbrains.kotlin.kapt3.KaptContext import org.jetbrains.kotlin.kapt3.KaptContextForStubGeneration
import org.jetbrains.kotlin.kapt3.base.stubs.KaptStubLineInformation
import org.jetbrains.kotlin.kapt3.base.stubs.KotlinPosition
import org.jetbrains.kotlin.kapt3.base.stubs.LineInfoMap
import org.jetbrains.kotlin.kapt3.base.stubs.getJavacSignature
import org.jetbrains.org.objectweb.asm.tree.ClassNode import org.jetbrains.org.objectweb.asm.tree.ClassNode
import org.jetbrains.org.objectweb.asm.tree.FieldNode import org.jetbrains.org.objectweb.asm.tree.FieldNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode import org.jetbrains.org.objectweb.asm.tree.MethodNode
import java.io.* import java.io.*
data class KotlinPosition(val path: String, val isRelativePath: Boolean, val pos: Int) class KaptLineMappingCollector(private val kaptContext: KaptContextForStubGeneration) {
private typealias LineInfoMap = MutableMap<String, KotlinPosition>
class KaptLineMappingCollector(private val kaptContext: KaptContext<*>) {
companion object {
const val KAPT_METADATA_EXTENSION = ".kapt_metadata"
private const val METADATA_VERSION = 1
fun parseFileInfo(file: JCTree.JCCompilationUnit): FileInfo {
val sourceUri = file.sourcefile
?.toUri()
?.takeIf { it.isAbsolute && !it.isOpaque && it.path != null && it.scheme?.toLowerCase() == "file" } ?: return FileInfo.EMPTY
val sourceFile = File(sourceUri).takeIf { it.exists() } ?: return FileInfo.EMPTY
val kaptMetadataFile = File(sourceFile.parentFile, sourceFile.nameWithoutExtension + KAPT_METADATA_EXTENSION)
if (!kaptMetadataFile.isFile) {
return FileInfo.EMPTY
}
return deserialize(kaptMetadataFile.readBytes())
}
private fun deserialize(data: ByteArray): FileInfo {
val lineInfo: LineInfoMap = mutableMapOf()
val signatureInfo = mutableMapOf<String, String>()
val ois = ObjectInputStream(ByteArrayInputStream(data))
val version = ois.readInt()
if (version != METADATA_VERSION) {
return FileInfo.EMPTY
}
val lineInfoCount = ois.readInt()
repeat(lineInfoCount) {
val fqName = ois.readUTF()
val path = ois.readUTF()
val isRelative = ois.readBoolean()
val pos = ois.readInt()
lineInfo[fqName] = KotlinPosition(path, isRelative, pos)
}
val signatureCount = ois.readInt()
repeat(signatureCount) {
val javacSignature = ois.readUTF()
val methodDesc = ois.readUTF()
signatureInfo[javacSignature] = methodDesc
}
return FileInfo(lineInfo, signatureInfo)
}
private fun getJavacSignature(decl: JCTree.JCMethodDecl): String {
val name = decl.name.toString()
val params = decl.parameters.joinToString { it.getType().toString() }
return "$name($params)"
}
}
private val lineInfo: LineInfoMap = mutableMapOf() private val lineInfo: LineInfoMap = mutableMapOf()
private val signatureInfo = mutableMapOf<String, String>() private val signatureInfo = mutableMapOf<String, String>()
@@ -106,7 +48,7 @@ class KaptLineMappingCollector(private val kaptContext: KaptContext<*>) {
} }
fun registerSignature(decl: JCTree.JCMethodDecl, method: MethodNode) { fun registerSignature(decl: JCTree.JCMethodDecl, method: MethodNode) {
signatureInfo[getJavacSignature(decl)] = method.name + method.desc signatureInfo[decl.getJavacSignature()] = method.name + method.desc
} }
private fun register(asmNode: Any, fqName: String) { private fun register(asmNode: Any, fqName: String) {
@@ -142,7 +84,7 @@ class KaptLineMappingCollector(private val kaptContext: KaptContext<*>) {
val os = ByteArrayOutputStream() val os = ByteArrayOutputStream()
val oos = ObjectOutputStream(os) val oos = ObjectOutputStream(os)
oos.writeInt(METADATA_VERSION) oos.writeInt(KaptStubLineInformation.METADATA_VERSION)
oos.writeInt(lineInfo.size) oos.writeInt(lineInfo.size)
for ((fqName, kotlinPosition) in lineInfo) { for ((fqName, kotlinPosition) in lineInfo) {
@@ -161,13 +103,4 @@ class KaptLineMappingCollector(private val kaptContext: KaptContext<*>) {
oos.flush() oos.flush()
return os.toByteArray() return os.toByteArray()
} }
class FileInfo(private val lineInfo: LineInfoMap, private val signatureInfo: Map<String, String>) {
companion object {
val EMPTY = FileInfo(mutableMapOf(), emptyMap())
}
fun getPositionFor(fqName: String) = lineInfo[fqName]
fun getMethodDescriptor(decl: JCTree.JCMethodDecl) = signatureInfo[getJavacSignature(decl)]
}
} }
@@ -24,8 +24,8 @@ import org.jetbrains.org.objectweb.asm.signature.SignatureVisitor
import java.util.* import java.util.*
import org.jetbrains.kotlin.kapt3.stubs.ElementKind.* import org.jetbrains.kotlin.kapt3.stubs.ElementKind.*
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
import org.jetbrains.kotlin.kapt3.mapJList import org.jetbrains.kotlin.kapt3.base.mapJList
import org.jetbrains.kotlin.kapt3.mapJListIndexed import org.jetbrains.kotlin.kapt3.base.mapJListIndexed
import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.org.objectweb.asm.signature.SignatureReader import org.jetbrains.org.objectweb.asm.signature.SignatureReader
import com.sun.tools.javac.util.List as JavacList import com.sun.tools.javac.util.List as JavacList
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* * that can be found in the license/LICENSE.txt file.
* 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.util package org.jetbrains.kotlin.kapt3.util
@@ -20,38 +9,37 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
import org.jetbrains.kotlin.kapt3.base.util.KaptLogger
import java.io.PrintWriter import java.io.PrintWriter
import java.io.StringWriter import java.io.StringWriter
class KaptLogger( class MessageCollectorBackedKaptLogger(
val isVerbose: Boolean, override val isVerbose: Boolean,
val messageCollector: MessageCollector = PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, isVerbose) val messageCollector: MessageCollector = PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, isVerbose)
) { ) : KaptLogger {
private companion object { private companion object {
val PREFIX = "[kapt] " val PREFIX = "[kapt] "
} }
fun info(message: String) { override val errorWriter = makeWriter(CompilerMessageSeverity.ERROR)
override val warnWriter = makeWriter(CompilerMessageSeverity.STRONG_WARNING)
override val infoWriter = makeWriter(CompilerMessageSeverity.WARNING)
override fun info(message: String) {
if (isVerbose) { if (isVerbose) {
messageCollector.report(CompilerMessageSeverity.INFO, PREFIX + message) messageCollector.report(CompilerMessageSeverity.INFO, PREFIX + message)
} }
} }
inline fun info(message: () -> String) { override fun warn(message: String) {
if (isVerbose) {
info(message())
}
}
fun warn(message: String) {
messageCollector.report(CompilerMessageSeverity.WARNING, PREFIX + message) messageCollector.report(CompilerMessageSeverity.WARNING, PREFIX + message)
} }
fun error(message: String) { override fun error(message: String) {
messageCollector.report(CompilerMessageSeverity.ERROR, PREFIX + message) messageCollector.report(CompilerMessageSeverity.ERROR, PREFIX + message)
} }
fun exception(e: Throwable) { override fun exception(e: Throwable) {
val stacktrace = run { val stacktrace = run {
val writer = StringWriter() val writer = StringWriter()
e.printStackTrace(PrintWriter(writer)) e.printStackTrace(PrintWriter(writer))
@@ -59,4 +47,8 @@ class KaptLogger(
} }
messageCollector.report(CompilerMessageSeverity.ERROR, PREFIX + "An exception occurred: " + stacktrace) messageCollector.report(CompilerMessageSeverity.ERROR, PREFIX + "An exception occurred: " + stacktrace)
} }
private fun makeWriter(severity: CompilerMessageSeverity): PrintWriter {
return PrintWriter(MessageCollectorBackedWriter(messageCollector, severity))
}
} }
@@ -1,69 +0,0 @@
/*
* 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.sun.tools.javac.util.List as JavacList
internal inline fun <T, R> mapJList(values: Iterable<T>?, f: (T) -> R?): JavacList<R> {
if (values == null) return JavacList.nil()
var result = JavacList.nil<R>()
for (item in values) {
f(item)?.let { result = result.append(it) }
}
return result
}
internal inline fun <T, R> mapJListIndexed(values: Iterable<T>?, f: (Int, T) -> R?): JavacList<R> {
if (values == null) return JavacList.nil()
var result = JavacList.nil<R>()
values.forEachIndexed { index, item ->
f(index, item)?.let { result = result.append(it) }
}
return result
}
internal inline fun <T> mapPairedValuesJList(valuePairs: List<Any>?, f: (String, Any) -> T?): JavacList<T> {
if (valuePairs == null || valuePairs.isEmpty()) return JavacList.nil()
val size = valuePairs.size
var result = JavacList.nil<T>()
assert(size % 2 == 0)
var index = 0
while (index < size) {
val key = valuePairs[index] as String
val value = valuePairs[index + 1]
f(key, value)?.let { result = result.prepend(it) }
index += 2
}
return result.reverse()
}
internal fun pairedListToMap(valuePairs: List<Any>?): Map<String, Any?> {
val map = mutableMapOf<String, Any?>()
mapPairedValuesJList(valuePairs) { key, value ->
map.put(key, value)
}
return map
}
internal operator fun <T : Any> JavacList<T>.plus(other: JavacList<T>): JavacList<T> {
return this.appendList(other)
}
@@ -16,19 +16,20 @@
package org.jetbrains.kotlin.kapt3.test package org.jetbrains.kotlin.kapt3.test
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.text.StringUtil
import com.sun.tools.javac.tree.JCTree
import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.codegen.CodegenTestCase import org.jetbrains.kotlin.codegen.CodegenTestCase
import org.jetbrains.kotlin.codegen.GenerationUtils import org.jetbrains.kotlin.codegen.GenerationUtils
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.kapt3.* import org.jetbrains.kotlin.kapt3.*
import org.jetbrains.kotlin.kapt3.AptMode.STUBS_AND_APT import org.jetbrains.kotlin.kapt3.AptMode.STUBS_AND_APT
import org.jetbrains.kotlin.kapt3.base.KaptContext
import org.jetbrains.kotlin.kapt3.base.KaptPaths
import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject
import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter
import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter.KaptStub import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter.KaptStub
import org.jetbrains.kotlin.kapt3.util.KaptLogger import org.jetbrains.kotlin.kapt3.util.MessageCollectorBackedKaptLogger
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
import org.jetbrains.kotlin.test.ConfigurationKind import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
@@ -130,10 +131,11 @@ abstract class AbstractKotlinKapt3IntegrationTest : CodegenTestCase() {
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL, *javaSources) createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL, *javaSources)
val kapt3Extension = Kapt3ExtensionForTests(processors, javaSources.toList(), sourceOutputDir, this.options, val project = myEnvironment.project
val kapt3Extension = Kapt3ExtensionForTests(project, processors, javaSources.toList(), sourceOutputDir, this.options,
stubsOutputDir = stubsDir, incrementalDataOutputDir = incrementalDataDir) stubsOutputDir = stubsDir, incrementalDataOutputDir = incrementalDataDir)
AnalysisHandlerExtension.registerExtension(myEnvironment.project, kapt3Extension) AnalysisHandlerExtension.registerExtension(project, kapt3Extension)
try { try {
loadMultiFiles(files) loadMultiFiles(files)
@@ -153,6 +155,7 @@ abstract class AbstractKotlinKapt3IntegrationTest : CodegenTestCase() {
} }
protected class Kapt3ExtensionForTests( protected class Kapt3ExtensionForTests(
project: Project,
private val processors: List<Processor>, private val processors: List<Processor>,
javaSourceRoots: List<File>, javaSourceRoots: List<File>,
outputDir: File, outputDir: File,
@@ -161,10 +164,11 @@ abstract class AbstractKotlinKapt3IntegrationTest : CodegenTestCase() {
incrementalDataOutputDir: File incrementalDataOutputDir: File
) : AbstractKapt3Extension( ) : AbstractKapt3Extension(
KaptPaths( KaptPaths(
project.basePath?.let(::File),
PathUtil.getJdkClassesRootsFromCurrentJre() + PathUtil.kotlinPathsForIdeaPlugin.stdlibPath, PathUtil.getJdkClassesRootsFromCurrentJre() + PathUtil.kotlinPathsForIdeaPlugin.stdlibPath,
emptyList(), javaSourceRoots, outputDir, outputDir, stubsOutputDir, incrementalDataOutputDir emptyList(), javaSourceRoots, outputDir, outputDir, stubsOutputDir, incrementalDataOutputDir
), options, emptyMap(), emptyList(), STUBS_AND_APT, System.currentTimeMillis(), ), options, emptyMap(), emptyList(), STUBS_AND_APT, System.currentTimeMillis(),
KaptLogger(true), correctErrorTypes = true, mapDiagnosticLocations = true, MessageCollectorBackedKaptLogger(true), correctErrorTypes = true, mapDiagnosticLocations = true,
compilerConfiguration = CompilerConfiguration.EMPTY compilerConfiguration = CompilerConfiguration.EMPTY
) { ) {
internal var savedStubs: String? = null internal var savedStubs: String? = null
@@ -172,7 +176,7 @@ abstract class AbstractKotlinKapt3IntegrationTest : CodegenTestCase() {
override fun loadProcessors() = processors override fun loadProcessors() = processors
override fun saveStubs(kaptContext: KaptContext<*>, stubs: List<KaptStub>) { override fun saveStubs(kaptContext: KaptContext, stubs: List<KaptStub>) {
if (this.savedStubs != null) { if (this.savedStubs != null) {
error("Stubs are already saved") error("Stubs are already saved")
} }
@@ -186,7 +190,7 @@ abstract class AbstractKotlinKapt3IntegrationTest : CodegenTestCase() {
} }
override fun saveIncrementalData( override fun saveIncrementalData(
kaptContext: KaptContext<GenerationState>, kaptContext: KaptContextForStubGeneration,
messageCollector: MessageCollector, messageCollector: MessageCollector,
converter: ClassFileToSourceStubConverter converter: ClassFileToSourceStubConverter
) { ) {
@@ -32,12 +32,15 @@ import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
import org.jetbrains.kotlin.codegen.CodegenTestCase import org.jetbrains.kotlin.codegen.CodegenTestCase
import org.jetbrains.kotlin.codegen.CodegenTestFiles import org.jetbrains.kotlin.codegen.CodegenTestFiles
import org.jetbrains.kotlin.codegen.GenerationUtils import org.jetbrains.kotlin.codegen.GenerationUtils
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.kapt3.* import org.jetbrains.kotlin.kapt3.*
import org.jetbrains.kotlin.kapt3.base.KaptContext
import org.jetbrains.kotlin.kapt3.base.KaptPaths
import org.jetbrains.kotlin.kapt3.base.doAnnotationProcessing
import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaLog
import org.jetbrains.kotlin.kapt3.base.parseJavaFiles
import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject
import org.jetbrains.kotlin.kapt3.javac.KaptJavaLog
import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter
import org.jetbrains.kotlin.kapt3.util.KaptLogger import org.jetbrains.kotlin.kapt3.util.MessageCollectorBackedKaptLogger
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
import org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension import org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension
@@ -112,7 +115,7 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
val classBuilderFactory = Kapt3BuilderFactory() val classBuilderFactory = Kapt3BuilderFactory()
val generationState = GenerationUtils.compileFiles(myFiles.psiFiles, myEnvironment, classBuilderFactory) val generationState = GenerationUtils.compileFiles(myFiles.psiFiles, myEnvironment, classBuilderFactory)
val logger = KaptLogger(isVerbose = true, messageCollector = messageCollector) val logger = MessageCollectorBackedKaptLogger(isVerbose = true, messageCollector = messageCollector)
val javacOptions = wholeFile.getOptionValues("JAVAC_OPTION") val javacOptions = wholeFile.getOptionValues("JAVAC_OPTION")
.map { opt -> .map { opt ->
@@ -121,22 +124,25 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
}.toMap() }.toMap()
var javaFiles: List<File>? = null var javaFiles: List<File>? = null
var kaptContext: KaptContext<*>? = null var kaptContext: KaptContext? = null
try { try {
val sourceOutputDir = Files.createTempDirectory("kaptRunner").toFile() val sourceOutputDir = Files.createTempDirectory("kaptRunner").toFile()
val paths = KaptPaths( val paths = KaptPaths(
generationState.project.basePath?.let(::File),
compileClasspath = PathUtil.getJdkClassesRootsFromCurrentJre() + PathUtil.kotlinPathsForIdeaPlugin.stdlibPath, compileClasspath = PathUtil.getJdkClassesRootsFromCurrentJre() + PathUtil.kotlinPathsForIdeaPlugin.stdlibPath,
annotationProcessingClasspath = emptyList(), javaSourceRoots = emptyList(), annotationProcessingClasspath = emptyList(), javaSourceRoots = emptyList(),
sourcesOutputDir = sourceOutputDir, classFilesOutputDir = sourceOutputDir, sourcesOutputDir = sourceOutputDir, classFilesOutputDir = sourceOutputDir,
stubsOutputDir = sourceOutputDir, incrementalDataOutputDir = sourceOutputDir stubsOutputDir = sourceOutputDir, incrementalDataOutputDir = sourceOutputDir
) )
kaptContext = KaptContext(paths, true, AptMode.STUBS_AND_APT, kaptContext = KaptContextForStubGeneration(
logger, generationState.project, generationState.bindingContext, classBuilderFactory.compiledClasses, paths, true,
classBuilderFactory.origins, generationState, mapDiagnosticLocations = true, logger, generationState.project, generationState.bindingContext, classBuilderFactory.compiledClasses,
processorOptions = emptyMap(), javacOptions = javacOptions) classBuilderFactory.origins, generationState, mapDiagnosticLocations = true,
processorOptions = emptyMap(), javacOptions = javacOptions
)
javaFiles = files javaFiles = files
.filter { it.name.toLowerCase().endsWith(".java") } .filter { it.name.toLowerCase().endsWith(".java") }
@@ -154,7 +160,7 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
} }
protected fun convert( protected fun convert(
kaptContext: KaptContext<GenerationState>, kaptContext: KaptContextForStubGeneration,
javaFiles: List<File>, javaFiles: List<File>,
generateNonExistentClass: Boolean, generateNonExistentClass: Boolean,
correctErrorTypes: Boolean correctErrorTypes: Boolean
@@ -201,7 +207,7 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
protected fun File.getOptionValues(name: String) = getRawOptionValues(name).map { it.drop("// ".length + name.length).trim() } protected fun File.getOptionValues(name: String) = getRawOptionValues(name).map { it.drop("// ".length + name.length).trim() }
protected abstract fun check( protected abstract fun check(
kaptContext: KaptContext<GenerationState>, kaptContext: KaptContextForStubGeneration,
javaFiles: List<File>, javaFiles: List<File>,
txtFile: File, txtFile: File,
wholeFile: File) wholeFile: File)
@@ -239,7 +245,7 @@ open class AbstractClassFileToSourceStubConverterTest : AbstractKotlinKapt3Test(
doTestWithJdk9(AbstractClassFileToSourceStubConverterTest::class.java, filePath) doTestWithJdk9(AbstractClassFileToSourceStubConverterTest::class.java, filePath)
} }
override fun check(kaptContext: KaptContext<GenerationState>, javaFiles: List<File>, txtFile: File, wholeFile: File) { override fun check(kaptContext: KaptContextForStubGeneration, javaFiles: List<File>, txtFile: File, wholeFile: File) {
val generateNonExistentClass = wholeFile.isOptionSet("NON_EXISTENT_CLASS") val generateNonExistentClass = wholeFile.isOptionSet("NON_EXISTENT_CLASS")
val correctErrorTypes = wholeFile.isOptionSet("CORRECT_ERROR_TYPES") val correctErrorTypes = wholeFile.isOptionSet("CORRECT_ERROR_TYPES")
val validate = !wholeFile.isOptionSet("NO_VALIDATION") val validate = !wholeFile.isOptionSet("NO_VALIDATION")
@@ -298,7 +304,7 @@ open class AbstractClassFileToSourceStubConverterTest : AbstractKotlinKapt3Test(
} }
abstract class AbstractKotlinKaptContextTest : AbstractKotlinKapt3Test() { abstract class AbstractKotlinKaptContextTest : AbstractKotlinKapt3Test() {
override fun check(kaptContext: KaptContext<GenerationState>, javaFiles: List<File>, txtFile: File, wholeFile: File) { override fun check(kaptContext: KaptContextForStubGeneration, javaFiles: List<File>, txtFile: File, wholeFile: File) {
val compilationUnits = convert(kaptContext, javaFiles, generateNonExistentClass = false, correctErrorTypes = true) val compilationUnits = convert(kaptContext, javaFiles, generateNonExistentClass = false, correctErrorTypes = true)
kaptContext.doAnnotationProcessing( kaptContext.doAnnotationProcessing(
@@ -17,7 +17,7 @@
package org.jetbrains.kotlin.kapt3.test package org.jetbrains.kotlin.kapt3.test
import com.intellij.openapi.util.SystemInfoRt import com.intellij.openapi.util.SystemInfoRt
import org.jetbrains.kotlin.kapt3.util.isJava9OrLater import org.jetbrains.kotlin.kapt3.base.util.isJava9OrLater
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File import java.io.File
import java.net.URL import java.net.URL
@@ -27,7 +27,7 @@ import java.util.concurrent.TimeUnit
interface Java9TestLauncher { interface Java9TestLauncher {
fun doTestWithJdk9(mainClass: Class<*>, arg: String) { fun doTestWithJdk9(mainClass: Class<*>, arg: String) {
// Already under Java 9 // Already under Java 9
if (isJava9OrLater) return if (isJava9OrLater()) return
//TODO unmute after investigation (tests are failing on TeamCity) //TODO unmute after investigation (tests are failing on TeamCity)
if (SystemInfoRt.isWindows) return if (SystemInfoRt.isWindows) return
+2
View File
@@ -147,6 +147,7 @@ include ":kotlin-build-common",
":examples:annotation-processor-example", ":examples:annotation-processor-example",
":kotlin-script-util", ":kotlin-script-util",
":kotlin-annotation-processing", ":kotlin-annotation-processing",
":kotlin-annotation-processing-base",
":kotlin-annotation-processing-runtime", ":kotlin-annotation-processing-runtime",
":kotlin-annotation-processing-gradle", ":kotlin-annotation-processing-gradle",
":kotlin-annotation-processing-embeddable", ":kotlin-annotation-processing-embeddable",
@@ -255,6 +256,7 @@ project(':kotlin-script-util').projectDir = "$rootDir/libraries/tools/kotlin-scr
project(':kotlin-annotation-processing-gradle').projectDir = "$rootDir/libraries/tools/kotlin-annotation-processing" as File project(':kotlin-annotation-processing-gradle').projectDir = "$rootDir/libraries/tools/kotlin-annotation-processing" as File
project(':kotlin-annotation-processing-embeddable').projectDir = "$rootDir/prepare/kotlin-annotation-processing-embeddable" as File project(':kotlin-annotation-processing-embeddable').projectDir = "$rootDir/prepare/kotlin-annotation-processing-embeddable" as File
project(':kotlin-annotation-processing').projectDir = "$rootDir/plugins/kapt3/kapt3-compiler" as File project(':kotlin-annotation-processing').projectDir = "$rootDir/plugins/kapt3/kapt3-compiler" as File
project(':kotlin-annotation-processing-base').projectDir = "$rootDir/plugins/kapt3/kapt3-base" as File
project(':kotlin-annotation-processing-runtime').projectDir = "$rootDir/plugins/kapt3/kapt3-runtime" as File project(':kotlin-annotation-processing-runtime').projectDir = "$rootDir/plugins/kapt3/kapt3-runtime" as File
project(':plugins:kapt3-idea').projectDir = "$rootDir/plugins/kapt3/kapt3-idea" as File project(':plugins:kapt3-idea').projectDir = "$rootDir/plugins/kapt3/kapt3-idea" as File
project(':examples:kotlin-jsr223-local-example').projectDir = "$rootDir/libraries/examples/kotlin-jsr223-local-example" as File project(':examples:kotlin-jsr223-local-example').projectDir = "$rootDir/libraries/examples/kotlin-jsr223-local-example" as File