Kapt, Refactoring: Introduce 'KaptOptions'

1. Use 'KaptOptions' for all kapt options, including paths and flags.
2. Use a single 'KAPT_OPTIONS' compiler configuration key for setting all options (using a mutable KaptOptions.Builder).
3. Pass 'KaptOptions' instead of separate flags.
4. Remove 'KaptPaths'.
5. Remove deprecated 'aptOnly' CLI option.
This commit is contained in:
Yan Zhulanow
2018-11-06 15:30:34 +09:00
parent a621171d9b
commit 7e4069f114
17 changed files with 457 additions and 489 deletions
@@ -94,7 +94,7 @@ public class KaptJVMCompilerMojo extends K2JVMCompileMojo {
) { ) {
List<KaptOption> options = new ArrayList<>(); List<KaptOption> options = new ArrayList<>();
options.add(new KaptOption("aptOnly", true)); options.add(new KaptOption("aptMode", "stubsAndApt"));
options.add(new KaptOption("useLightAnalysis", useLightAnalysis)); options.add(new KaptOption("useLightAnalysis", useLightAnalysis));
options.add(new KaptOption("correctErrorTypes", correctErrorTypes)); options.add(new KaptOption("correctErrorTypes", correctErrorTypes));
options.add(new KaptOption("mapDiagnosticLocations", mapDiagnosticLocations)); options.add(new KaptOption("mapDiagnosticLocations", mapDiagnosticLocations));
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.kapt3.base package org.jetbrains.kotlin.kapt3.base
import com.sun.tools.javac.util.Context import com.sun.tools.javac.util.Context
import org.jetbrains.kotlin.base.kapt3.*
import org.jetbrains.kotlin.kapt3.base.util.KaptLogger import org.jetbrains.kotlin.kapt3.base.util.KaptLogger
import org.jetbrains.kotlin.kapt3.base.util.WriterBackedKaptLogger import org.jetbrains.kotlin.kapt3.base.util.WriterBackedKaptLogger
import org.jetbrains.kotlin.kapt3.base.util.info import org.jetbrains.kotlin.kapt3.base.util.info
@@ -16,32 +17,26 @@ object Kapt {
@JvmStatic @JvmStatic
@Suppress("unused") @Suppress("unused")
fun kapt( fun kaptFlags(rawFlags: Set<String>): KaptFlags {
paths: KaptPaths, return KaptFlags.fromSet(KaptFlag.values().filterTo(mutableSetOf()) { it.name in rawFlags })
isVerbose: Boolean, }
mapDiagnosticLocations: Boolean,
annotationProcessorFqNames: List<String>, @JvmStatic
processorOptions: Map<String, String>, @Suppress("unused")
javacOptions: Map<String, String> fun kapt(options: KaptOptions): Boolean {
): Boolean { val logger = WriterBackedKaptLogger(options[KaptFlag.VERBOSE])
val logger = WriterBackedKaptLogger(isVerbose)
if (!Kapt.checkJavacComponentsAccess(logger)) { if (!Kapt.checkJavacComponentsAccess(logger)) {
return false return false
} }
val kaptContext = KaptContext(paths, false, logger, mapDiagnosticLocations, processorOptions, javacOptions) val kaptContext = KaptContext(options, false, logger)
logger.info { "Kapt3 is enabled (stand-alone mode)." } logger.info { options.logString("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() val javaSourceFiles = options.collectJavaSourceFiles()
logger.info { "Java source files: " + javaSourceFiles.joinToString { it.canonicalPath } }
val processorLoader = ProcessorLoader(paths, annotationProcessorFqNames, logger) val processorLoader = ProcessorLoader(options, logger)
processorLoader.use { processorLoader.use {
val processors = processorLoader.loadProcessors(findClassLoaderWithJavac()) val processors = processorLoader.loadProcessors(findClassLoaderWithJavac())
@@ -11,6 +11,8 @@ 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.Log
import com.sun.tools.javac.util.Options import com.sun.tools.javac.util.Options
import org.jetbrains.kotlin.base.kapt3.KaptFlag
import org.jetbrains.kotlin.base.kapt3.KaptOptions
import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaCompiler import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaCompiler
import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaFileManager import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaFileManager
import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaLog import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaLog
@@ -21,18 +23,11 @@ import java.io.Closeable
import java.io.File import java.io.File
import javax.tools.JavaFileManager import javax.tools.JavaFileManager
open class KaptContext( open class KaptContext(val options: KaptOptions, val withJdk: Boolean, val logger: KaptLogger) : Closeable {
val paths: KaptPaths,
val withJdk: Boolean,
val logger: KaptLogger,
private val mapDiagnosticLocations: Boolean,
processorOptions: Map<String, String>,
javacOptions: Map<String, String> = emptyMap()
) : Closeable {
val context = Context() val context = Context()
val compiler: KaptJavaCompiler val compiler: KaptJavaCompiler
val fileManager: KaptJavaFileManager val fileManager: KaptJavaFileManager
val options: Options private val javacOptions: Options
val javaLog: KaptJavaLog val javaLog: KaptJavaLog
protected open fun preregisterTreeMaker(context: Context) {} protected open fun preregisterTreeMaker(context: Context) {}
@@ -41,8 +36,8 @@ open class KaptContext(
val interceptorData = KaptJavaLog.DiagnosticInterceptorData() val interceptorData = KaptJavaLog.DiagnosticInterceptorData()
context.put(Log.logKey, Context.Factory<Log> { newContext -> context.put(Log.logKey, Context.Factory<Log> { newContext ->
KaptJavaLog( KaptJavaLog(
paths.projectBaseDir, newContext, logger.errorWriter, logger.warnWriter, logger.infoWriter, options.projectBaseDir, newContext, logger.errorWriter, logger.warnWriter, logger.infoWriter,
interceptorData, mapDiagnosticLocations interceptorData, options[KaptFlag.MAP_DIAGNOSTIC_LOCATIONS]
) )
}) })
} }
@@ -56,13 +51,13 @@ open class KaptContext(
KaptJavaCompiler.preRegister(context) KaptJavaCompiler.preRegister(context)
options = Options.instance(context).apply { javacOptions = Options.instance(context).apply {
for ((key, value) in processorOptions) { for ((key, value) in options.processingOptions) {
val option = if (value.isEmpty()) "-A$key" else "-A$key=$value" val option = if (value.isEmpty()) "-A$key" else "-A$key=$value"
put(option, option) // key == value: it's intentional put(option, option) // key == value: it's intentional
} }
for ((key, value) in javacOptions) { for ((key, value) in options.javacOptions) {
if (value.isNotEmpty()) { if (value.isNotEmpty()) {
put(key, value) put(key, value)
} else { } else {
@@ -82,26 +77,26 @@ open class KaptContext(
} }
putJavacOption("CLASSPATH", "CLASS_PATH", putJavacOption("CLASSPATH", "CLASS_PATH",
paths.compileClasspath.joinToString(File.pathSeparator) { it.canonicalPath }) options.compileClasspath.joinToString(File.pathSeparator) { it.canonicalPath })
@Suppress("SpellCheckingInspection") @Suppress("SpellCheckingInspection")
putJavacOption("PROCESSORPATH", "PROCESSOR_PATH", putJavacOption("PROCESSORPATH", "PROCESSOR_PATH",
paths.annotationProcessingClasspath.joinToString(File.pathSeparator) { it.canonicalPath }) options.processingClasspath.joinToString(File.pathSeparator) { it.canonicalPath })
put(Option.S, paths.sourcesOutputDir.canonicalPath) put(Option.S, options.sourcesOutputDir.canonicalPath)
put(Option.D, paths.classFilesOutputDir.canonicalPath) put(Option.D, options.classesOutputDir.canonicalPath)
put(Option.ENCODING, "UTF-8") put(Option.ENCODING, "UTF-8")
} }
if (logger.isVerbose) { if (logger.isVerbose) {
logger.info("All Javac options: " + options.keySet().associateBy({ it }) { key -> options[key] ?: "" }) logger.info("All Javac options: " + javacOptions.keySet().associateBy({ it }) { key -> javacOptions[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 = javacOptions.get(option) ?: continue
fileManager.handleOptionJavac9(option, value) fileManager.handleOptionJavac9(option, value)
} }
} }
@@ -0,0 +1,144 @@
/*
* 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.base.kapt3
import java.io.File
import java.nio.file.Files
class KaptOptions(
val projectBaseDir: File?,
val compileClasspath: List<File>,
val javaSourceRoots: List<File>,
val sourcesOutputDir: File,
val classesOutputDir: File,
val stubsOutputDir: File,
val incrementalDataOutputDir: File?,
val processingClasspath: List<File>,
val processors: List<String>,
val processingOptions: Map<String, String>,
val javacOptions: Map<String, String>,
val flags: KaptFlags,
val mode: AptMode,
val detectMemoryLeaks: DetectMemoryLeaksMode
) : KaptFlags {
override fun get(flag: KaptFlag) = flags[flag]
class Builder {
var projectBaseDir: File? = null
val compileClasspath: MutableList<File> = mutableListOf()
val javaSourceRoots: MutableList<File> = mutableListOf()
var sourcesOutputDir: File? = null
var classesOutputDir: File? = null
var stubsOutputDir: File? = null
var incrementalDataOutputDir: File? = null
val processingClasspath: MutableList<File> = mutableListOf()
val processors: MutableList<String> = mutableListOf()
val processingOptions: MutableMap<String, String> = mutableMapOf()
val javacOptions: MutableMap<String, String> = mutableMapOf()
val flags: MutableSet<KaptFlag> = mutableSetOf(KaptFlag.USE_LIGHT_ANALYSIS)
var mode: AptMode = AptMode.WITH_COMPILATION
var detectMemoryLeaks: DetectMemoryLeaksMode = DetectMemoryLeaksMode.DEFAULT
fun build(): KaptOptions {
val sourcesOutputDir = this.sourcesOutputDir ?: error("'sourcesOutputDir' must be set")
val classesOutputDir = this.classesOutputDir ?: error("'classesOutputDir' must be set")
val stubsOutputDir = this.stubsOutputDir ?: error("'stubsOutputDir' must be set")
return KaptOptions(
projectBaseDir, compileClasspath, javaSourceRoots,
sourcesOutputDir, classesOutputDir, stubsOutputDir, incrementalDataOutputDir,
processingClasspath, processors, processingOptions, javacOptions, KaptFlags.fromSet(flags),
mode, detectMemoryLeaks
)
}
}
}
interface KaptFlags {
operator fun get(flag: KaptFlag): Boolean
companion object {
fun fromSet(flags: Set<KaptFlag>) = object : KaptFlags {
override fun get(flag: KaptFlag) = flag in flags
}
}
object Empty : KaptFlags {
override fun get(flag: KaptFlag) = false
}
}
enum class KaptFlag(val description: String) {
VERBOSE("Verbose mode"),
INFO_AS_WARNINGS("Info as warnings"),
USE_LIGHT_ANALYSIS("Use light analysis"),
CORRECT_ERROR_TYPES("Correct error types"),
MAP_DIAGNOSTIC_LOCATIONS("Map diagnostic locations"),
STRICT("Strict mode");
}
interface KaptSelector {
val stringValue: String
}
enum class DetectMemoryLeaksMode(override val stringValue: String) : KaptSelector {
DEFAULT("default"), PARANOID("paranoid"), NONE("none")
}
enum class AptMode(override val stringValue: String) : KaptSelector {
WITH_COMPILATION("compile"),
STUBS_AND_APT("stubsAndApt"),
STUBS_ONLY("stubs"),
APT_ONLY("apt");
val runAnnotationProcessing
get() = this != STUBS_ONLY
val generateStubs
get() = this != APT_ONLY
}
fun KaptOptions.collectJavaSourceFiles(): List<File> {
return (javaSourceRoots + stubsOutputDir)
.sortedBy { Files.isSymbolicLink(it.toPath()) } // Get non-symbolic paths first
.flatMap { root -> root.walk().filter { it.isFile && it.extension == "java" }.toList() }
.sortedBy { Files.isSymbolicLink(it.toPath()) } // This time is for .java files
.distinctBy { it.canonicalPath }
}
fun KaptOptions.logString(additionalInfo: String = "") = buildString {
val additionalInfoRendered = if (additionalInfo.isEmpty()) "" else " ($additionalInfo)"
appendln("Kapt3 is enabled$additionalInfoRendered.")
appendln("Annotation processing mode: ${mode.stringValue}")
appendln("Memory leak detection mode: ${detectMemoryLeaks.stringValue}")
KaptFlag.values().forEach { appendln(it.description + ": " + this@logString[it]) }
appendln("Project base dir: $projectBaseDir")
appendln("Compile classpath: " + compileClasspath.joinToString())
appendln("Java source roots: " + javaSourceRoots.joinToString())
appendln("Sources output directory: $sourcesOutputDir")
appendln("Class files output directory: $classesOutputDir")
appendln("Stubs output directory: $stubsOutputDir")
appendln("Incremental data output directory: $incrementalDataOutputDir")
appendln("Annotation processing classpath: " + processingClasspath.joinToString())
appendln("Annotation processors: " + processors.joinToString())
appendln("AP options: $processingOptions")
appendln("Javac options: $javacOptions")
}
@@ -1,45 +0,0 @@
/*
* 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
import java.nio.file.Files
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)
.sortedBy { Files.isSymbolicLink(it.toPath()) } // Get non-symbolic paths first
.flatMap { root -> root.walk().filter { it.isFile && it.extension == "java" }.toList() }
.sortedBy { Files.isSymbolicLink(it.toPath()) } // This time is for .java files
.distinctBy { it.canonicalPath }
}
}
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")
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.kapt3.base package org.jetbrains.kotlin.kapt3.base
import org.jetbrains.kotlin.base.kapt3.KaptOptions
import org.jetbrains.kotlin.kapt3.base.util.KaptLogger import org.jetbrains.kotlin.kapt3.base.util.KaptLogger
import org.jetbrains.kotlin.kapt3.base.util.info import org.jetbrains.kotlin.kapt3.base.util.info
import java.io.Closeable import java.io.Closeable
@@ -16,23 +17,19 @@ import javax.annotation.processing.Processor
class LoadedProcessors(val processors: List<Processor>, val classLoader: ClassLoader) class LoadedProcessors(val processors: List<Processor>, val classLoader: ClassLoader)
open class ProcessorLoader( open class ProcessorLoader(private val options: KaptOptions, private val logger: KaptLogger) : Closeable {
private val paths: KaptPaths,
private val annotationProcessorFqNames: List<String>,
private val logger: KaptLogger
) : Closeable {
private var annotationProcessingClassLoader: URLClassLoader? = null private var annotationProcessingClassLoader: URLClassLoader? = null
fun loadProcessors(parentClassLoader: ClassLoader = ClassLoader.getSystemClassLoader()): LoadedProcessors { fun loadProcessors(parentClassLoader: ClassLoader = ClassLoader.getSystemClassLoader()): LoadedProcessors {
clearJarURLCache() clearJarURLCache()
val classpath = (paths.annotationProcessingClasspath + paths.compileClasspath).distinct() val classpath = (options.processingClasspath + options.compileClasspath).distinct()
val classLoader = URLClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray(), parentClassLoader) val classLoader = URLClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray(), parentClassLoader)
this.annotationProcessingClassLoader = classLoader this.annotationProcessingClassLoader = classLoader
val processors = if (annotationProcessorFqNames.isNotEmpty()) { val processors = if (options.processors.isNotEmpty()) {
logger.info("Annotation processor class names are set, skip AP discovery") logger.info("Annotation processor class names are set, skip AP discovery")
annotationProcessorFqNames.mapNotNull { tryLoadProcessor(it, classLoader) } options.processors.mapNotNull { tryLoadProcessor(it, classLoader) }
} else { } else {
logger.info("Need to discovery annotation processors in the AP classpath") logger.info("Need to discovery annotation processors in the AP classpath")
doLoadProcessors(classLoader) doLoadProcessors(classLoader)
@@ -6,8 +6,10 @@
package org.jetbrains.kotlin.kapt.base.test package org.jetbrains.kotlin.kapt.base.test
import junit.framework.TestCase import junit.framework.TestCase
import org.jetbrains.kotlin.base.kapt3.DetectMemoryLeaksMode
import org.jetbrains.kotlin.base.kapt3.KaptFlag
import org.jetbrains.kotlin.base.kapt3.KaptOptions
import org.jetbrains.kotlin.kapt3.base.KaptContext 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.doAnnotationProcessing
import org.jetbrains.kotlin.kapt3.base.util.KaptBaseError import org.jetbrains.kotlin.kapt3.base.util.KaptBaseError
import org.jetbrains.kotlin.kapt3.base.util.WriterBackedKaptLogger import org.jetbrains.kotlin.kapt3.base.util.WriterBackedKaptLogger
@@ -49,22 +51,20 @@ class JavaKaptContextTest : TestCase() {
} }
private fun doAnnotationProcessing(javaSourceFile: File, processor: Processor, outputDir: File) { private fun doAnnotationProcessing(javaSourceFile: File, processor: Processor, outputDir: File) {
KaptContext( val options = KaptOptions.Builder().apply {
KaptPaths( projectBaseDir = javaSourceFile.parentFile
projectBaseDir = javaSourceFile.parentFile,
compileClasspath = emptyList(), sourcesOutputDir = outputDir
annotationProcessingClasspath = emptyList(), classesOutputDir = outputDir
javaSourceRoots = emptyList(), stubsOutputDir = outputDir
sourcesOutputDir = outputDir, incrementalDataOutputDir = outputDir
classFilesOutputDir = outputDir,
stubsOutputDir = outputDir, flags.add(KaptFlag.MAP_DIAGNOSTIC_LOCATIONS)
incrementalDataOutputDir = outputDir detectMemoryLeaks = DetectMemoryLeaksMode.NONE
), }.build()
withJdk = true,
logger = WriterBackedKaptLogger(isVerbose = true), val logger = WriterBackedKaptLogger(isVerbose = true)
mapDiagnosticLocations = true, KaptContext(options, true, logger).doAnnotationProcessing(listOf(javaSourceFile), listOf(processor))
processorOptions = emptyMap()
).doAnnotationProcessing(listOf(javaSourceFile), listOf(processor))
} }
@Test @Test
+8 -11
View File
@@ -6,7 +6,8 @@
package org.jetbrains.kotlin.kapt.base.test package org.jetbrains.kotlin.kapt.base.test
import junit.framework.TestCase import junit.framework.TestCase
import org.jetbrains.kotlin.kapt3.base.KaptPaths import org.jetbrains.kotlin.base.kapt3.KaptOptions
import org.jetbrains.kotlin.base.kapt3.collectJavaSourceFiles
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
@@ -36,16 +37,12 @@ class KaptPathsTest : TestCase() {
val javaRoots = listOf(simpleJava, symlinkToOtherJava, symlinkToNotJava, symlinkToJavaRootDir, javaRootDir) val javaRoots = listOf(simpleJava, symlinkToOtherJava, symlinkToNotJava, symlinkToJavaRootDir, javaRootDir)
val paths = KaptPaths( val paths = KaptOptions.Builder().apply {
projectBaseDir = null, javaSourceRoots.addAll(javaRoots)
compileClasspath = emptyList(), sourcesOutputDir = outputDir
annotationProcessingClasspath = emptyList(), classesOutputDir = outputDir
javaSourceRoots = javaRoots, stubsOutputDir = outputDir
sourcesOutputDir = outputDir, }.build()
classFilesOutputDir = outputDir,
stubsOutputDir = outputDir,
incrementalDataOutputDir = null
)
val javaSourceFiles = paths.collectJavaSourceFiles() val javaSourceFiles = paths.collectJavaSourceFiles()
+1 -4
View File
@@ -149,10 +149,7 @@ enum class KaptCliOption(
APT_OPTIONS_OPTION("apoptions", "options map", "Encoded annotation processor options", false), APT_OPTIONS_OPTION("apoptions", "options map", "Encoded annotation processor options", false),
@Deprecated("Do not use in CLI") @Deprecated("Do not use in CLI")
JAVAC_CLI_OPTIONS_OPTION("javacArguments", "javac CLI options map", "Encoded javac CLI options", false), JAVAC_CLI_OPTIONS_OPTION("javacArguments", "javac CLI options map", "Encoded javac CLI options", false);
@Deprecated("Use APT_MODE_OPTION instead.")
APT_ONLY_OPTION("aptOnly", "true | false", "Run only annotation processing, do not compile Kotlin files");
override val required: Boolean = false override val required: Boolean = false
@@ -24,6 +24,7 @@ import com.sun.tools.javac.tree.TreeMaker
import com.sun.tools.javac.util.Context import com.sun.tools.javac.util.Context
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.base.kapt3.KaptOptions
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.OUTPUT import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.OUTPUT
import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil
@@ -39,8 +40,11 @@ import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.container.ComponentProvider 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.APT_ONLY import org.jetbrains.kotlin.base.kapt3.AptMode.APT_ONLY
import org.jetbrains.kotlin.kapt3.AptMode.WITH_COMPILATION import org.jetbrains.kotlin.base.kapt3.AptMode.WITH_COMPILATION
import org.jetbrains.kotlin.base.kapt3.DetectMemoryLeaksMode
import org.jetbrains.kotlin.base.kapt3.KaptFlag
import org.jetbrains.kotlin.base.kapt3.collectJavaSourceFiles
import org.jetbrains.kotlin.kapt3.base.* import org.jetbrains.kotlin.kapt3.base.*
import org.jetbrains.kotlin.kapt3.base.stubs.KaptStubLineInformation.Companion.KAPT_METADATA_EXTENSION import org.jetbrains.kotlin.kapt3.base.stubs.KaptStubLineInformation.Companion.KAPT_METADATA_EXTENSION
import org.jetbrains.kotlin.kapt3.base.util.KaptBaseError import org.jetbrains.kotlin.kapt3.base.util.KaptBaseError
@@ -64,31 +68,17 @@ import javax.annotation.processing.Processor
import com.sun.tools.javac.util.List as JavacList import com.sun.tools.javac.util.List as JavacList
class ClasspathBasedKapt3Extension( class ClasspathBasedKapt3Extension(
paths: KaptPaths, options: KaptOptions,
options: Map<String, String>,
javacOptions: Map<String, String>,
annotationProcessorFqNames: List<String>,
aptMode: AptMode,
private val useLightAnalysis: Boolean,
correctErrorTypes: Boolean,
mapDiagnosticLocations: Boolean,
strictMode: Boolean,
detectMemoryLeaks: Boolean,
pluginInitializedTime: Long,
logger: MessageCollectorBackedKaptLogger, logger: MessageCollectorBackedKaptLogger,
compilerConfiguration: CompilerConfiguration compilerConfiguration: CompilerConfiguration
) : AbstractKapt3Extension( ) : AbstractKapt3Extension(options, logger, compilerConfiguration) {
paths, options, javacOptions, annotationProcessorFqNames,
aptMode, pluginInitializedTime, logger, correctErrorTypes, mapDiagnosticLocations, strictMode, detectMemoryLeaks,
compilerConfiguration
) {
override val analyzePartially: Boolean override val analyzePartially: Boolean
get() = useLightAnalysis && super.analyzePartially get() = options[KaptFlag.USE_LIGHT_ANALYSIS] && super.analyzePartially
private var processorLoader: ProcessorLoader? = null private var processorLoader: ProcessorLoader? = null
override fun loadProcessors(): LoadedProcessors { override fun loadProcessors(): LoadedProcessors {
val efficientProcessorLoader = object : ProcessorLoader(paths, annotationProcessorFqNames, logger) { val efficientProcessorLoader = object : ProcessorLoader(options, logger) {
override fun doLoadProcessors(classLoader: URLClassLoader): List<Processor> { override fun doLoadProcessors(classLoader: URLClassLoader): List<Processor> {
return ServiceLoaderLite.loadImplementations(Processor::class.java, classLoader) return ServiceLoaderLite.loadImplementations(Processor::class.java, classLoader)
} }
@@ -122,19 +112,12 @@ class ClasspathBasedKapt3Extension(
} }
abstract class AbstractKapt3Extension( abstract class AbstractKapt3Extension(
val paths: KaptPaths, val options: KaptOptions,
private val options: Map<String, String>,
private val javacOptions: Map<String, String>,
val annotationProcessorFqNames: List<String>,
private val aptMode: AptMode,
private val pluginInitializedTime: Long,
val logger: MessageCollectorBackedKaptLogger, val logger: MessageCollectorBackedKaptLogger,
val correctErrorTypes: Boolean,
val mapDiagnosticLocations: Boolean,
val strictMode: Boolean,
val detectMemoryLeaks: Boolean,
val compilerConfiguration: CompilerConfiguration val compilerConfiguration: CompilerConfiguration
) : PartialAnalysisHandlerExtension() { ) : PartialAnalysisHandlerExtension() {
private val pluginInitializedTime: Long = System.currentTimeMillis()
private var annotationProcessingComplete = false private var annotationProcessingComplete = false
private fun setAnnotationProcessingComplete(): Boolean { private fun setAnnotationProcessingComplete(): Boolean {
@@ -155,7 +138,7 @@ abstract class AbstractKapt3Extension(
bindingTrace: BindingTrace, bindingTrace: BindingTrace,
componentProvider: ComponentProvider componentProvider: ComponentProvider
): AnalysisResult? { ): AnalysisResult? {
if (aptMode == APT_ONLY) { if (options.mode == APT_ONLY) {
return AnalysisResult.EMPTY return AnalysisResult.EMPTY
} }
@@ -175,7 +158,7 @@ abstract class AbstractKapt3Extension(
logger.info { "Initial analysis took ${System.currentTimeMillis() - pluginInitializedTime} ms" } logger.info { "Initial analysis took ${System.currentTimeMillis() - pluginInitializedTime} ms" }
val bindingContext = bindingTrace.bindingContext val bindingContext = bindingTrace.bindingContext
if (aptMode.generateStubs) { if (options.mode.generateStubs) {
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()}>" } }
contextForStubGeneration(project, module, bindingContext, files.toList()).use { context -> contextForStubGeneration(project, module, bindingContext, files.toList()).use { context ->
@@ -183,12 +166,12 @@ abstract class AbstractKapt3Extension(
} }
} }
if (!aptMode.runAnnotationProcessing) return doNotGenerateCode() if (!options.mode.runAnnotationProcessing) return doNotGenerateCode()
val processors = loadProcessors() val processors = loadProcessors()
if (processors.processors.isEmpty()) return if (aptMode != WITH_COMPILATION) doNotGenerateCode() else null if (processors.processors.isEmpty()) return if (options.mode != WITH_COMPILATION) doNotGenerateCode() else null
val kaptContext = KaptContext(paths, false, logger, mapDiagnosticLocations, options, javacOptions) val kaptContext = KaptContext(options, false, logger)
fun handleKaptError(error: KaptError): AnalysisResult { fun handleKaptError(error: KaptError): AnalysisResult {
val cause = error.cause val cause = error.cause
@@ -218,22 +201,22 @@ abstract class AbstractKapt3Extension(
kaptContext.close() kaptContext.close()
} }
return if (aptMode != WITH_COMPILATION) { return if (options.mode != WITH_COMPILATION) {
doNotGenerateCode() doNotGenerateCode()
} else { } else {
AnalysisResult.RetryWithAdditionalJavaRoots( AnalysisResult.RetryWithAdditionalJavaRoots(
bindingTrace.bindingContext, bindingTrace.bindingContext,
module, module,
listOf(paths.sourcesOutputDir), listOf(options.sourcesOutputDir),
addToEnvironment = true addToEnvironment = true
) )
} }
} }
private fun runAnnotationProcessing(kaptContext: KaptContext, processors: LoadedProcessors) { private fun runAnnotationProcessing(kaptContext: KaptContext, processors: LoadedProcessors) {
if (!aptMode.runAnnotationProcessing) return if (!options.mode.runAnnotationProcessing) return
val javaSourceFiles = paths.collectJavaSourceFiles() val javaSourceFiles = options.collectJavaSourceFiles()
logger.info { "Java source files: " + javaSourceFiles.joinToString { it.canonicalPath } } logger.info { "Java source files: " + javaSourceFiles.joinToString { it.canonicalPath } }
val (annotationProcessingTime) = measureTimeMillis { val (annotationProcessingTime) = measureTimeMillis {
@@ -242,7 +225,7 @@ abstract class AbstractKapt3Extension(
logger.info { "Annotation processing took $annotationProcessingTime ms" } logger.info { "Annotation processing took $annotationProcessingTime ms" }
if (detectMemoryLeaks) { if (options.detectMemoryLeaks != DetectMemoryLeaksMode.NONE) {
MemoryLeakDetector.add(processors.classLoader) MemoryLeakDetector.add(processors.classLoader)
val (leakDetectionTime, leaks) = measureTimeMillis { MemoryLeakDetector.process() } val (leakDetectionTime, leaks) = measureTimeMillis { MemoryLeakDetector.process() }
@@ -291,18 +274,13 @@ abstract class AbstractKapt3Extension(
logger.info { "Compiled classes: " + compiledClasses.joinToString { it.name } } logger.info { "Compiled classes: " + compiledClasses.joinToString { it.name } }
return KaptContextForStubGeneration( return KaptContextForStubGeneration(
paths, false, logger, project, bindingContext, compiledClasses, origins, generationState, options, false, logger, project, bindingContext,
mapDiagnosticLocations, options, javacOptions compiledClasses, origins, generationState
) )
} }
private fun generateKotlinSourceStubs(kaptContext: KaptContextForStubGeneration) { private fun generateKotlinSourceStubs(kaptContext: KaptContextForStubGeneration) {
val converter = ClassFileToSourceStubConverter( val converter = ClassFileToSourceStubConverter(kaptContext, generateNonExistentClass = true)
kaptContext,
generateNonExistentClass = true,
correctErrorTypes = correctErrorTypes,
strictMode = strictMode
)
val (stubGenerationTime, kaptStubs) = measureTimeMillis { val (stubGenerationTime, kaptStubs) = measureTimeMillis {
converter.convert() converter.convert()
@@ -321,7 +299,7 @@ abstract class AbstractKapt3Extension(
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()
val packageName = stub.getPackageNameJava9Aware()?.toString() ?: "" val packageName = stub.getPackageNameJava9Aware()?.toString() ?: ""
val packageDir = if (packageName.isEmpty()) paths.stubsOutputDir else File(paths.stubsOutputDir, packageName.replace('.', '/')) val packageDir = if (packageName.isEmpty()) options.stubsOutputDir else File(options.stubsOutputDir, packageName.replace('.', '/'))
packageDir.mkdirs() packageDir.mkdirs()
val sourceFile = File(packageDir, "$className.java") val sourceFile = File(packageDir, "$className.java")
@@ -336,7 +314,7 @@ abstract class AbstractKapt3Extension(
messageCollector: MessageCollector, messageCollector: MessageCollector,
converter: ClassFileToSourceStubConverter converter: ClassFileToSourceStubConverter
) { ) {
val incrementalDataOutputDir = paths.incrementalDataOutputDir ?: return val incrementalDataOutputDir = options.incrementalDataOutputDir ?: return
val reportOutputFiles = kaptContext.generationState.configuration.getBoolean(CommonConfigurationKeys.REPORT_OUTPUT_FILES) val reportOutputFiles = kaptContext.generationState.configuration.getBoolean(CommonConfigurationKeys.REPORT_OUTPUT_FILES)
kaptContext.generationState.factory.writeAll( kaptContext.generationState.factory.writeAll(
@@ -344,7 +322,7 @@ abstract class AbstractKapt3Extension(
if (!reportOutputFiles) null else fun(file: OutputFile, sources: List<File>, output: File) { if (!reportOutputFiles) null else fun(file: OutputFile, sources: List<File>, output: File) {
val stubFileObject = converter.bindings[file.relativePath.substringBeforeLast(".class", missingDelimiterValue = "")] val stubFileObject = converter.bindings[file.relativePath.substringBeforeLast(".class", missingDelimiterValue = "")]
if (stubFileObject != null) { if (stubFileObject != null) {
val stubFile = File(paths.stubsOutputDir, stubFileObject.name) val stubFile = File(options.stubsOutputDir, stubFileObject.name)
val lineMappingsFile = File(stubFile.parentFile, stubFile.nameWithoutExtension + KAPT_METADATA_EXTENSION) val lineMappingsFile = File(stubFile.parentFile, stubFile.nameWithoutExtension + KAPT_METADATA_EXTENSION)
for (outputFile in listOf(stubFile, lineMappingsFile)) { for (outputFile in listOf(stubFile, lineMappingsFile)) {
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.kapt3
import com.intellij.mock.MockProject import com.intellij.mock.MockProject
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.AnalysisResult import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.base.kapt3.*
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
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
@@ -38,14 +39,8 @@ import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
import org.jetbrains.kotlin.kapt.cli.KaptCliOption import org.jetbrains.kotlin.kapt.cli.KaptCliOption
import org.jetbrains.kotlin.kapt.cli.KaptCliOption.Companion.ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID import org.jetbrains.kotlin.kapt.cli.KaptCliOption.Companion.ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID
import org.jetbrains.kotlin.kapt.cli.KaptCliOption.* import org.jetbrains.kotlin.kapt.cli.KaptCliOption.*
import org.jetbrains.kotlin.kapt3.Kapt3ConfigurationKeys.ANNOTATION_PROCESSOR_CLASSPATH
import org.jetbrains.kotlin.kapt3.Kapt3ConfigurationKeys.APT_OPTION
import org.jetbrains.kotlin.kapt3.Kapt3ConfigurationKeys.APT_OPTIONS
import org.jetbrains.kotlin.kapt3.Kapt3ConfigurationKeys.JAVAC_CLI_OPTIONS
import org.jetbrains.kotlin.kapt3.Kapt3ConfigurationKeys.JAVAC_OPTION
import org.jetbrains.kotlin.kapt3.base.Kapt import org.jetbrains.kotlin.kapt3.base.Kapt
import org.jetbrains.kotlin.kapt3.base.KaptPaths import org.jetbrains.kotlin.kapt3.base.util.KaptLogger
import org.jetbrains.kotlin.kapt3.base.log
import org.jetbrains.kotlin.kapt3.util.MessageCollectorBackedKaptLogger 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
@@ -58,70 +53,7 @@ import java.io.File
import java.io.ObjectInputStream import java.io.ObjectInputStream
import java.util.* import java.util.*
object Kapt3ConfigurationKeys { private val KAPT_OPTIONS = CompilerConfigurationKey.create<KaptOptions.Builder>("Kapt options")
val SOURCE_OUTPUT_DIR: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>(SOURCE_OUTPUT_DIR_OPTION.description)
val CLASS_OUTPUT_DIR: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>(CLASS_OUTPUT_DIR_OPTION.description)
val STUBS_OUTPUT_DIR: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>(STUBS_OUTPUT_DIR_OPTION.description)
val INCREMENTAL_DATA_OUTPUT_DIR: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>(INCREMENTAL_DATA_OUTPUT_DIR_OPTION.description)
val ANNOTATION_PROCESSOR_CLASSPATH: CompilerConfigurationKey<List<String>> =
CompilerConfigurationKey.create<List<String>>(ANNOTATION_PROCESSOR_CLASSPATH_OPTION.description)
@Suppress("DEPRECATION")
@Deprecated("Use APT_OPTION instead.")
val APT_OPTIONS: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>(APT_OPTIONS_OPTION.description)
val APT_OPTION: CompilerConfigurationKey<Map<String, String>> =
CompilerConfigurationKey.create<Map<String, String>>(APT_OPTION_OPTION.description)
@Suppress("DEPRECATION")
@Deprecated("Use JAVAC_OPTION instead.")
val JAVAC_CLI_OPTIONS: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>(JAVAC_CLI_OPTIONS_OPTION.description)
val JAVAC_OPTION: CompilerConfigurationKey<Map<String, String>> =
CompilerConfigurationKey.create<Map<String, String>>(JAVAC_OPTION_OPTION.description)
val ANNOTATION_PROCESSORS: CompilerConfigurationKey<List<String>> =
CompilerConfigurationKey.create<List<String>>(ANNOTATION_PROCESSORS_OPTION.description)
val VERBOSE_MODE: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>(VERBOSE_MODE_OPTION.description)
val INFO_AS_WARNINGS: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>(INFO_AS_WARNINGS_OPTION.description)
@Suppress("DEPRECATION")
@Deprecated("Use APT_MODE instead.")
val APT_ONLY: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>(APT_ONLY_OPTION.description)
val APT_MODE: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>(APT_MODE_OPTION.description)
val USE_LIGHT_ANALYSIS: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>(USE_LIGHT_ANALYSIS_OPTION.description)
val CORRECT_ERROR_TYPES: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>(CORRECT_ERROR_TYPES_OPTION.description)
val MAP_DIAGNOSTIC_LOCATIONS: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>(MAP_DIAGNOSTIC_LOCATIONS_OPTION.description)
val STRICT_MODE: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>(STRICT_MODE_OPTION.description)
val DETECT_MEMORY_LEAKS: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>(DETECT_MEMORY_LEAKS_OPTION.description)
}
class Kapt3CommandLineProcessor : CommandLineProcessor { class Kapt3CommandLineProcessor : CommandLineProcessor {
override val pluginId: String = ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID override val pluginId: String = ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID
@@ -133,52 +65,61 @@ class Kapt3CommandLineProcessor : CommandLineProcessor {
throw CliOptionProcessingException("Unknown option: ${option.optionName}") throw CliOptionProcessingException("Unknown option: ${option.optionName}")
} }
val kaptOptions = configuration[KAPT_OPTIONS]
?: KaptOptions.Builder().also { configuration.put(KAPT_OPTIONS, it) }
if (option == @Suppress("DEPRECATION") KaptCliOption.CONFIGURATION) {
configuration.applyOptionsFrom(decodePluginOptions(value), pluginOptions)
} else {
kaptOptions.processOption(option, value)
}
}
private fun KaptOptions.Builder.processOption(option: KaptCliOption, value: String) {
fun setFlag(flag: KaptFlag, value: String) = if (value == "true") flags.add(flag) else flags.remove(flag)
fun <T : KaptSelector> setSelector(values: Array<T>, rawValue: String, selector: (T) -> Unit) {
selector(values.firstOrNull { it.stringValue == rawValue }
?: throw CliOptionProcessingException("Unknown value $rawValue for option ${option.optionName}"))
}
fun setKeyValue(rawValue: String, apply: (String, String) -> Unit) {
val keyValuePair = rawValue.split('=', limit = 2).takeIf { it.size == 2 }
?: throw CliOptionProcessingException("Invalid option format for ${option.optionName}: key=value expected")
apply(keyValuePair[0], keyValuePair[1])
}
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
return when (option) { when (option) {
ANNOTATION_PROCESSOR_CLASSPATH_OPTION -> configuration.appendList(ANNOTATION_PROCESSOR_CLASSPATH, value) SOURCE_OUTPUT_DIR_OPTION -> sourcesOutputDir = File(value)
ANNOTATION_PROCESSORS_OPTION -> configuration.appendList( CLASS_OUTPUT_DIR_OPTION -> classesOutputDir = File(value)
Kapt3ConfigurationKeys.ANNOTATION_PROCESSORS, value.split(',').map { it.trim() }.filter { it.isNotEmpty() }) STUBS_OUTPUT_DIR_OPTION -> stubsOutputDir = File(value)
APT_OPTIONS_OPTION -> configuration.put(APT_OPTIONS, value) INCREMENTAL_DATA_OUTPUT_DIR_OPTION -> incrementalDataOutputDir = File(value)
JAVAC_CLI_OPTIONS_OPTION -> configuration.put(JAVAC_CLI_OPTIONS, value)
APT_OPTION_OPTION -> configuration.appendMap(APT_OPTION, option, value) ANNOTATION_PROCESSOR_CLASSPATH_OPTION -> processingClasspath += File(value)
JAVAC_OPTION_OPTION -> configuration.appendMap(JAVAC_OPTION, option, value) ANNOTATION_PROCESSORS_OPTION -> processors.addAll(value.split(',').map { it.trim() }.filter { it.isNotEmpty() })
SOURCE_OUTPUT_DIR_OPTION -> configuration.put(Kapt3ConfigurationKeys.SOURCE_OUTPUT_DIR, value)
CLASS_OUTPUT_DIR_OPTION -> configuration.put(Kapt3ConfigurationKeys.CLASS_OUTPUT_DIR, value) APT_OPTION_OPTION -> setKeyValue(value) { k, v -> processingOptions[k] = v }
STUBS_OUTPUT_DIR_OPTION -> configuration.put(Kapt3ConfigurationKeys.STUBS_OUTPUT_DIR, value) JAVAC_OPTION_OPTION -> setKeyValue(value) { k, v -> javacOptions[k] = v }
INCREMENTAL_DATA_OUTPUT_DIR_OPTION -> configuration.put(Kapt3ConfigurationKeys.INCREMENTAL_DATA_OUTPUT_DIR, value)
VERBOSE_MODE_OPTION -> configuration.put(Kapt3ConfigurationKeys.VERBOSE_MODE, value) VERBOSE_MODE_OPTION -> setFlag(KaptFlag.VERBOSE, value)
APT_ONLY_OPTION -> configuration.put(Kapt3ConfigurationKeys.APT_ONLY, value) USE_LIGHT_ANALYSIS_OPTION -> setFlag(KaptFlag.USE_LIGHT_ANALYSIS, value)
APT_MODE_OPTION -> configuration.put(Kapt3ConfigurationKeys.APT_MODE, value) CORRECT_ERROR_TYPES_OPTION -> setFlag(KaptFlag.CORRECT_ERROR_TYPES, value)
USE_LIGHT_ANALYSIS_OPTION -> configuration.put(Kapt3ConfigurationKeys.USE_LIGHT_ANALYSIS, value) MAP_DIAGNOSTIC_LOCATIONS_OPTION -> setFlag(KaptFlag.MAP_DIAGNOSTIC_LOCATIONS, value)
CORRECT_ERROR_TYPES_OPTION -> configuration.put(Kapt3ConfigurationKeys.CORRECT_ERROR_TYPES, value) INFO_AS_WARNINGS_OPTION -> setFlag(KaptFlag.INFO_AS_WARNINGS, value)
MAP_DIAGNOSTIC_LOCATIONS_OPTION -> configuration.put(Kapt3ConfigurationKeys.MAP_DIAGNOSTIC_LOCATIONS, value) STRICT_MODE_OPTION -> setFlag(KaptFlag.STRICT, value)
INFO_AS_WARNINGS_OPTION -> configuration.put(Kapt3ConfigurationKeys.INFO_AS_WARNINGS, value)
STRICT_MODE_OPTION -> configuration.put(Kapt3ConfigurationKeys.STRICT_MODE, value) DETECT_MEMORY_LEAKS_OPTION -> setSelector(enumValues<DetectMemoryLeaksMode>(), value) { detectMemoryLeaks = it }
DETECT_MEMORY_LEAKS_OPTION -> configuration.put(Kapt3ConfigurationKeys.DETECT_MEMORY_LEAKS, value) APT_MODE_OPTION -> setSelector(enumValues<AptMode>(), value) { mode = it }
CONFIGURATION -> configuration.applyOptionsFrom(decodePluginOptions(value), pluginOptions)
APT_OPTIONS_OPTION -> processingOptions.putAll(decodeMap(value))
JAVAC_CLI_OPTIONS_OPTION -> javacOptions.putAll(decodeMap(value))
CONFIGURATION -> throw CliOptionProcessingException("${CONFIGURATION.optionName} should be handled earlier")
TOOLS_JAR_OPTION -> throw CliOptionProcessingException("'${TOOLS_JAR_OPTION.optionName}' is only supported in the kapt CLI tool") TOOLS_JAR_OPTION -> throw CliOptionProcessingException("'${TOOLS_JAR_OPTION.optionName}' is only supported in the kapt CLI tool")
} }
} }
private fun CompilerConfiguration.appendMap(
key: CompilerConfigurationKey<Map<String, String>>,
option: AbstractCliOption,
keyValuePair: String
) {
val keyValueDecoded = keyValuePair.split('=', limit = 2)
if (keyValueDecoded.size != 2) {
throw CliOptionProcessingException("Invalid option format for ${option.optionName}: key=value expected")
}
val oldMap = getMap(key)
val newMap = (oldMap as? MutableMap<String, String>) ?: oldMap.toMutableMap()
newMap[keyValueDecoded[0]] = keyValueDecoded[1]
put(key, newMap)
}
}
class Kapt3ComponentRegistrar : ComponentRegistrar {
private fun decodeMap(options: String): Map<String, String> { private fun decodeMap(options: String): Map<String, String> {
if (options.isEmpty()) { if (options.isEmpty()) {
return emptyMap() return emptyMap()
@@ -200,115 +141,90 @@ class Kapt3ComponentRegistrar : ComponentRegistrar {
return map return map
} }
}
class Kapt3ComponentRegistrar : ComponentRegistrar {
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) { override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
@Suppress("DEPRECATION") val contentRoots = configuration[CLIConfigurationKeys.CONTENT_ROOTS] ?: emptyList()
val aptMode = AptMode.parse(configuration.get(Kapt3ConfigurationKeys.APT_MODE) ?: configuration.get(Kapt3ConfigurationKeys.APT_ONLY))
val optionsBuilder = (configuration[KAPT_OPTIONS] ?: KaptOptions.Builder()).apply {
projectBaseDir = project.basePath?.let(::File)
compileClasspath.addAll(contentRoots.filterIsInstance<JvmClasspathRoot>().map { it.file })
javaSourceRoots.addAll(contentRoots.filterIsInstance<JavaSourceRoot>().map { it.file })
classesOutputDir = classesOutputDir ?: configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY)
}
val isVerbose = configuration.get(Kapt3ConfigurationKeys.VERBOSE_MODE) == "true"
val infoAsWarnings = configuration.get(Kapt3ConfigurationKeys.INFO_AS_WARNINGS) == "true"
val strictMode = configuration.get(Kapt3ConfigurationKeys.STRICT_MODE) == "true"
val detectMemoryLeaks = configuration.get(Kapt3ConfigurationKeys.DETECT_MEMORY_LEAKS) != "false"
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, optionsBuilder.flags.contains(KaptFlag.VERBOSE))
val logger = MessageCollectorBackedKaptLogger(isVerbose, infoAsWarnings, messageCollector)
val logger = MessageCollectorBackedKaptLogger(
optionsBuilder.flags.contains(KaptFlag.VERBOSE),
optionsBuilder.flags.contains(KaptFlag.INFO_AS_WARNINGS),
messageCollector
)
if (!optionsBuilder.checkOptions(project, logger, configuration)) {
return
}
val options = optionsBuilder.build()
options.sourcesOutputDir.mkdirs()
if (options[KaptFlag.VERBOSE]) {
logger.info(options.logString())
}
val kapt3AnalysisCompletedHandlerExtension = ClasspathBasedKapt3Extension(options, logger, configuration)
AnalysisHandlerExtension.registerExtension(project, kapt3AnalysisCompletedHandlerExtension)
StorageComponentContainerContributor.registerExtension(project, KaptComponentContributor())
}
private fun KaptOptions.Builder.checkOptions(project: MockProject, logger: KaptLogger, configuration: CompilerConfiguration): Boolean {
fun abortAnalysis() = AnalysisHandlerExtension.registerExtension(project, AbortAnalysisHandlerExtension()) fun abortAnalysis() = AnalysisHandlerExtension.registerExtension(project, AbortAnalysisHandlerExtension())
if (!Kapt.checkJavacComponentsAccess(logger)) { if (classesOutputDir == null) {
abortAnalysis() if (configuration.get(JVMConfigurationKeys.OUTPUT_JAR) != null) {
return logger.error("Kapt does not support specifying JAR file outputs. Please specify the classes output directory explicitly.")
abortAnalysis()
return false
} else {
classesOutputDir = configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY)
}
} }
val sourcesOutputDir = configuration.get(Kapt3ConfigurationKeys.SOURCE_OUTPUT_DIR)?.let(::File) if (processingClasspath.isEmpty()) {
val stubsOutputDir = configuration.get(Kapt3ConfigurationKeys.STUBS_OUTPUT_DIR)?.let(::File)
val incrementalDataOutputDir = configuration.get(Kapt3ConfigurationKeys.INCREMENTAL_DATA_OUTPUT_DIR)?.let(::File)
val classFilesOutputDir = configuration.get(Kapt3ConfigurationKeys.CLASS_OUTPUT_DIR)?.let(::File)
?: configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY)
if (classFilesOutputDir == null && configuration.get(JVMConfigurationKeys.OUTPUT_JAR) != null) {
logger.error("Kapt does not support specifying JAR file outputs. Please specify the classes output directory explicitly.")
abortAnalysis()
return
}
val annotationProcessors = configuration.get(Kapt3ConfigurationKeys.ANNOTATION_PROCESSORS) ?: emptyList()
val apClasspath = configuration.get(ANNOTATION_PROCESSOR_CLASSPATH)?.map(::File) ?: emptyList()
if (apClasspath.isEmpty()) {
// Skip annotation processing if no annotation processors were provided // Skip annotation processing if no annotation processors were provided
if (aptMode != AptMode.WITH_COMPILATION) { if (mode != AptMode.WITH_COMPILATION) {
abortAnalysis() abortAnalysis()
} }
return return false
} }
if (sourcesOutputDir == null || classFilesOutputDir == null || stubsOutputDir == null) { if (sourcesOutputDir == null || classesOutputDir == null || stubsOutputDir == null) {
if (aptMode != AptMode.WITH_COMPILATION) { if (mode != AptMode.WITH_COMPILATION) {
val nonExistentOptionName = when { val nonExistentOptionName = when {
sourcesOutputDir == null -> "Sources output directory" sourcesOutputDir == null -> "Sources output directory"
classFilesOutputDir == null -> "Classes output directory" classesOutputDir == null -> "Classes output directory"
stubsOutputDir == null -> "Stubs output directory" stubsOutputDir == null -> "Stubs output directory"
else -> throw IllegalStateException() else -> throw IllegalStateException()
} }
val moduleName = configuration.get(CommonConfigurationKeys.MODULE_NAME) val moduleName = configuration.get(CommonConfigurationKeys.MODULE_NAME)
?: configuration.get(JVMConfigurationKeys.MODULES).orEmpty().joinToString() ?: configuration.get(JVMConfigurationKeys.MODULES).orEmpty().joinToString()
logger.warn("$nonExistentOptionName is not specified for $moduleName, skipping annotation processing") logger.warn("$nonExistentOptionName is not specified for $moduleName, skipping annotation processing")
abortAnalysis() abortAnalysis()
} }
return return false
} }
@Suppress("DEPRECATION") if (!Kapt.checkJavacComponentsAccess(logger)) {
val apOptions = decodeMap(configuration.get(APT_OPTIONS).orEmpty()).toMutableMap() abortAnalysis()
configuration.get(APT_OPTION)?.let { apOptions += it } return false
@Suppress("DEPRECATION")
val javacOptions = decodeMap(configuration.get(JAVAC_CLI_OPTIONS).orEmpty()).toMutableMap()
configuration.get(JAVAC_OPTION)?.let { javacOptions += it }
sourcesOutputDir.mkdirs()
val contentRoots = configuration[CLIConfigurationKeys.CONTENT_ROOTS] ?: emptyList()
val compileClasspath = contentRoots.filterIsInstance<JvmClasspathRoot>().map { it.file }
val javaSourceRoots = contentRoots.filterIsInstance<JavaSourceRoot>().map { it.file }
val useLightAnalysis = configuration.get(Kapt3ConfigurationKeys.USE_LIGHT_ANALYSIS) != "false"
val correctErrorTypes = configuration.get(Kapt3ConfigurationKeys.CORRECT_ERROR_TYPES) == "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) {
logger.info("Kapt3 is enabled.")
logger.info("Annotation processing mode: $aptMode")
logger.info("Use light analysis: $useLightAnalysis")
logger.info("Correct error types: $correctErrorTypes")
logger.info("Map diagnostic locations: $mapDiagnosticLocations")
logger.info("Info as warnings: $infoAsWarnings")
logger.info("Strict mode: $strictMode")
paths.log(logger)
logger.info("Annotation processors: " + annotationProcessors.joinToString())
logger.info("Javac options: $apOptions")
logger.info("AP options: $apOptions")
} }
val kapt3AnalysisCompletedHandlerExtension = ClasspathBasedKapt3Extension( return true
paths, apOptions, javacOptions, annotationProcessors,
aptMode, useLightAnalysis, correctErrorTypes, mapDiagnosticLocations, strictMode, detectMemoryLeaks,
System.currentTimeMillis(), logger, configuration
)
AnalysisHandlerExtension.registerExtension(project, kapt3AnalysisCompletedHandlerExtension)
StorageComponentContainerContributor.registerExtension(project, KaptComponentContributor())
} }
class KaptComponentContributor : StorageComponentContainerContributor { class KaptComponentContributor : StorageComponentContainerContributor {
@@ -327,47 +243,23 @@ class Kapt3ComponentRegistrar : ComponentRegistrar {
* */ * */
private class AbortAnalysisHandlerExtension : AnalysisHandlerExtension { private class AbortAnalysisHandlerExtension : AnalysisHandlerExtension {
override fun doAnalysis( override fun doAnalysis(
project: Project, project: Project,
module: ModuleDescriptor, module: ModuleDescriptor,
projectContext: ProjectContext, projectContext: ProjectContext,
files: Collection<KtFile>, files: Collection<KtFile>,
bindingTrace: BindingTrace, bindingTrace: BindingTrace,
componentProvider: ComponentProvider componentProvider: ComponentProvider
): AnalysisResult? { ): AnalysisResult? {
return AnalysisResult.success(bindingTrace.bindingContext, module, shouldGenerateCode = false) return AnalysisResult.success(bindingTrace.bindingContext, module, shouldGenerateCode = false)
} }
override fun analysisCompleted( override fun analysisCompleted(
project: Project, project: Project,
module: ModuleDescriptor, module: ModuleDescriptor,
bindingTrace: BindingTrace, bindingTrace: BindingTrace,
files: Collection<KtFile> files: Collection<KtFile>
): AnalysisResult? { ): AnalysisResult? {
return AnalysisResult.success(bindingTrace.bindingContext, module, shouldGenerateCode = false) return AnalysisResult.success(bindingTrace.bindingContext, module, shouldGenerateCode = false)
} }
} }
}
enum class AptMode(val optionName: String) {
WITH_COMPILATION("compile"),
STUBS_AND_APT("stubsAndApt"),
STUBS_ONLY("stubs"),
APT_ONLY("apt");
val runAnnotationProcessing
get() = this != STUBS_ONLY
val generateStubs
get() = this != APT_ONLY
companion object {
// Supports both deprecated APT_ONLY and new APT_MODE options
fun parse(mode: String?): AptMode {
return when (mode) {
"true" -> STUBS_AND_APT
"false" -> WITH_COMPILATION
else -> AptMode.values().firstOrNull { it.optionName == mode } ?: WITH_COMPILATION
}
}
}
} }
@@ -19,9 +19,9 @@ package org.jetbrains.kotlin.kapt3
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
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 org.jetbrains.kotlin.base.kapt3.KaptOptions
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.kapt3.base.KaptContext 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.base.util.KaptLogger
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
@@ -29,18 +29,15 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.org.objectweb.asm.tree.ClassNode import org.jetbrains.org.objectweb.asm.tree.ClassNode
class KaptContextForStubGeneration( class KaptContextForStubGeneration(
paths: KaptPaths, options: KaptOptions,
withJdk: Boolean, withJdk: Boolean,
logger: KaptLogger, logger: KaptLogger,
val project: Project, val project: Project,
val bindingContext: BindingContext, val bindingContext: BindingContext,
val compiledClasses: List<ClassNode>, val compiledClasses: List<ClassNode>,
val origins: Map<Any, JvmDeclarationOrigin>, val origins: Map<Any, JvmDeclarationOrigin>,
val generationState: GenerationState, val generationState: GenerationState
mapDiagnosticLocations: Boolean, ) : KaptContext(options, withJdk, logger) {
processorOptions: Map<String, String>,
javacOptions: Map<String, String> = emptyMap()
) : KaptContext(paths, withJdk, logger, mapDiagnosticLocations, processorOptions, javacOptions) {
private val treeMaker = TreeMaker.instance(context) private val treeMaker = TreeMaker.instance(context)
override fun preregisterTreeMaker(context: Context) { override fun preregisterTreeMaker(context: Context) {
@@ -24,6 +24,7 @@ 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.base.kapt3.KaptFlag
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
@@ -63,12 +64,7 @@ import java.io.File
import javax.lang.model.element.ElementKind 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: KaptContextForStubGeneration, val generateNonExistentClass: Boolean) {
val kaptContext: KaptContextForStubGeneration,
val generateNonExistentClass: Boolean,
val correctErrorTypes: Boolean,
val strictMode: Boolean
) {
private companion object { private companion object {
private const val VISIBILITY_MODIFIERS = (Opcodes.ACC_PUBLIC or Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED).toLong() private const val VISIBILITY_MODIFIERS = (Opcodes.ACC_PUBLIC or Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED).toLong()
private const val MODALITY_MODIFIERS = (Opcodes.ACC_FINAL or Opcodes.ACC_ABSTRACT).toLong() private const val MODALITY_MODIFIERS = (Opcodes.ACC_FINAL or Opcodes.ACC_ABSTRACT).toLong()
@@ -95,15 +91,19 @@ class ClassFileToSourceStubConverter(
private val JAVA_KEYWORD_FILTER_REGEX = "[a-z]+".toRegex() private val JAVA_KEYWORD_FILTER_REGEX = "[a-z]+".toRegex()
@Suppress("UselessCallOnNotNull") // nullable toString(), KT-27724
private val JAVA_KEYWORDS = Tokens.TokenKind.values() private val JAVA_KEYWORDS = Tokens.TokenKind.values()
.filter { JAVA_KEYWORD_FILTER_REGEX.matches(it.toString().orEmpty()) } .filter { JAVA_KEYWORD_FILTER_REGEX.matches(it.toString().orEmpty()) }
.mapTo(hashSetOf(), Any::toString) .mapTo(hashSetOf(), Any::toString)
} }
private val _bindings = mutableMapOf<String, KaptJavaFileObject>() private val correctErrorTypes = kaptContext.options[KaptFlag.CORRECT_ERROR_TYPES]
private val strictMode = kaptContext.options[KaptFlag.STRICT]
private val mutableBindings = mutableMapOf<String, KaptJavaFileObject>()
val bindings: Map<String, KaptJavaFileObject> val bindings: Map<String, KaptJavaFileObject>
get() = _bindings get() = mutableBindings
private val typeMapper private val typeMapper
get() = kaptContext.generationState.typeMapper get() = kaptContext.generationState.typeMapper
@@ -193,7 +193,7 @@ class ClassFileToSourceStubConverter(
KaptJavaFileObject(topLevel, classDeclaration).apply { KaptJavaFileObject(topLevel, classDeclaration).apply {
topLevel.sourcefile = this topLevel.sourcefile = this
_bindings[clazz.name] = this mutableBindings[clazz.name] = this
} }
postProcess(topLevel) postProcess(topLevel)
@@ -5,10 +5,12 @@
package org.jetbrains.kotlin.kapt3.util package org.jetbrains.kotlin.kapt3.util
import org.jetbrains.kotlin.base.kapt3.KaptFlag
import org.jetbrains.kotlin.base.kapt3.KaptFlags
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.* 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.PLAIN_FULL_PATHS
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 org.jetbrains.kotlin.kapt3.base.util.KaptLogger
import java.io.PrintWriter import java.io.PrintWriter
@@ -16,16 +18,20 @@ import java.io.StringWriter
class MessageCollectorBackedKaptLogger( class MessageCollectorBackedKaptLogger(
override val isVerbose: Boolean, override val isVerbose: Boolean,
infoAsWarnings: Boolean = true, isInfoAsWarnings: Boolean,
val messageCollector: MessageCollector = PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, isVerbose) val messageCollector: MessageCollector = defaultMessageCollector(isVerbose)
) : KaptLogger { ) : KaptLogger {
constructor(flags: KaptFlags, messageCollector: MessageCollector = defaultMessageCollector(flags[KaptFlag.VERBOSE]))
: this(flags[KaptFlag.VERBOSE], flags[KaptFlag.INFO_AS_WARNINGS], messageCollector)
private companion object { private companion object {
const val PREFIX = "[kapt] " const val PREFIX = "[kapt] "
fun defaultMessageCollector(isVerbose: Boolean) = PrintingMessageCollector(System.err, PLAIN_FULL_PATHS, isVerbose)
} }
override val errorWriter = makeWriter(ERROR) override val errorWriter = makeWriter(ERROR)
override val warnWriter = makeWriter(STRONG_WARNING) override val warnWriter = makeWriter(STRONG_WARNING)
override val infoWriter = makeWriter(if (infoAsWarnings) WARNING else INFO) override val infoWriter = makeWriter(if (isInfoAsWarnings) WARNING else INFO)
override fun info(message: String) { override fun info(message: String) {
if (isVerbose) { if (isVerbose) {
@@ -16,8 +16,9 @@
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 org.jetbrains.kotlin.base.kapt3.DetectMemoryLeaksMode
import org.jetbrains.kotlin.base.kapt3.KaptOptions
import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.codegen.ClassBuilderMode import org.jetbrains.kotlin.codegen.ClassBuilderMode
import org.jetbrains.kotlin.codegen.CodegenTestCase import org.jetbrains.kotlin.codegen.CodegenTestCase
@@ -25,9 +26,7 @@ import org.jetbrains.kotlin.codegen.GenerationUtils
import org.jetbrains.kotlin.codegen.OriginCollectingClassBuilderFactory import org.jetbrains.kotlin.codegen.OriginCollectingClassBuilderFactory
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.base.KaptContext import org.jetbrains.kotlin.kapt3.base.KaptContext
import org.jetbrains.kotlin.kapt3.base.KaptPaths
import org.jetbrains.kotlin.kapt3.base.LoadedProcessors import org.jetbrains.kotlin.kapt3.base.LoadedProcessors
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
@@ -59,12 +58,11 @@ abstract class AbstractKotlinKapt3IntegrationTest : CodegenTestCase() {
private var _processors: List<Processor>? = null private var _processors: List<Processor>? = null
private val processors get() = _processors!! private val processors get() = _processors!!
private var _options: Map<String, String>? = null private var mutableOptions: Map<String, String>? = null
private val options get() = _options!!
override fun tearDown() { override fun tearDown() {
_processors = null _processors = null
_options = null mutableOptions = null
super.tearDown() super.tearDown()
} }
@@ -82,7 +80,7 @@ abstract class AbstractKotlinKapt3IntegrationTest : CodegenTestCase() {
process: (Set<TypeElement>, RoundEnvironment, ProcessingEnvironment) -> Unit, process: (Set<TypeElement>, RoundEnvironment, ProcessingEnvironment) -> Unit,
vararg supportedAnnotations: String vararg supportedAnnotations: String
) { ) {
this._options = options this.mutableOptions = options
val ktFileName = File(TEST_DATA_DIR, "$name.kt") val ktFileName = File(TEST_DATA_DIR, "$name.kt")
var started = false var started = false
@@ -128,18 +126,25 @@ abstract class AbstractKotlinKapt3IntegrationTest : CodegenTestCase() {
val javaSources = javaFilesDir?.let { arrayOf(it) } ?: emptyArray() val javaSources = javaFilesDir?.let { arrayOf(it) } ?: emptyArray()
val txtFile = File(wholeFile.parentFile, wholeFile.nameWithoutExtension + ".it.txt") val txtFile = File(wholeFile.parentFile, wholeFile.nameWithoutExtension + ".it.txt")
val sourceOutputDir = Files.createTempDirectory("kaptRunner").toFile()
val stubsDir = Files.createTempDirectory("kaptStubs").toFile()
val incrementalDataDir = Files.createTempDirectory("kaptIncrementalData").toFile()
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL, *javaSources) createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL, *javaSources)
val project = myEnvironment.project val project = myEnvironment.project
val kapt3Extension = Kapt3ExtensionForTests(
project, processors, javaSources.toList(), sourceOutputDir, this.options,
stubsOutputDir = stubsDir, incrementalDataOutputDir = incrementalDataDir
)
val options = KaptOptions.Builder().apply {
projectBaseDir = project.basePath?.let(::File)
compileClasspath.addAll(PathUtil.getJdkClassesRootsFromCurrentJre() + PathUtil.kotlinPathsForIdeaPlugin.stdlibPath)
javaSourceRoots.addAll(javaSources)
sourcesOutputDir = Files.createTempDirectory("kaptRunner").toFile()
classesOutputDir = sourcesOutputDir
stubsOutputDir = Files.createTempDirectory("kaptStubs").toFile()
incrementalDataOutputDir = Files.createTempDirectory("kaptIncrementalData").toFile()
mutableOptions?.let { processingOptions.putAll(it) }
detectMemoryLeaks = DetectMemoryLeaksMode.NONE
}.build()
val kapt3Extension = Kapt3ExtensionForTests(options, processors)
AnalysisHandlerExtension.registerExtension(project, kapt3Extension) AnalysisHandlerExtension.registerExtension(project, kapt3Extension)
try { try {
@@ -155,28 +160,13 @@ abstract class AbstractKotlinKapt3IntegrationTest : CodegenTestCase() {
KotlinTestUtils.assertEqualsToFile(txtFile, actual) KotlinTestUtils.assertEqualsToFile(txtFile, actual)
} finally { } finally {
sourceOutputDir.deleteRecursively() options.sourcesOutputDir.deleteRecursively()
incrementalDataDir.deleteRecursively() options.incrementalDataOutputDir?.deleteRecursively()
} }
} }
protected class Kapt3ExtensionForTests( protected class Kapt3ExtensionForTests(options: KaptOptions, private val processors: List<Processor>) : AbstractKapt3Extension(
project: Project, options, MessageCollectorBackedKaptLogger(options), compilerConfiguration = CompilerConfiguration.EMPTY
private val processors: List<Processor>,
javaSourceRoots: List<File>,
outputDir: File,
options: Map<String, String>,
stubsOutputDir: File,
incrementalDataOutputDir: File
) : AbstractKapt3Extension(
KaptPaths(
project.basePath?.let(::File),
PathUtil.getJdkClassesRootsFromCurrentJre() + PathUtil.kotlinPathsForIdeaPlugin.stdlibPath,
emptyList(), javaSourceRoots, outputDir, outputDir, stubsOutputDir, incrementalDataOutputDir
), options, emptyMap(), emptyList(), STUBS_AND_APT, System.currentTimeMillis(),
MessageCollectorBackedKaptLogger(true),
correctErrorTypes = true, mapDiagnosticLocations = true, strictMode = true, detectMemoryLeaks = false,
compilerConfiguration = CompilerConfiguration.EMPTY
) { ) {
internal var savedStubs: String? = null internal var savedStubs: String? = null
internal var savedBindings: Map<String, KaptJavaFileObject>? = null internal var savedBindings: Map<String, KaptJavaFileObject>? = null
@@ -24,6 +24,9 @@ import com.sun.tools.javac.tree.JCTree.JCCompilationUnit
import com.sun.tools.javac.util.JCDiagnostic import com.sun.tools.javac.util.JCDiagnostic
import com.sun.tools.javac.util.Log import com.sun.tools.javac.util.Log
import junit.framework.ComparisonFailure import junit.framework.ComparisonFailure
import org.jetbrains.kotlin.base.kapt3.DetectMemoryLeaksMode
import org.jetbrains.kotlin.base.kapt3.KaptFlag
import org.jetbrains.kotlin.base.kapt3.KaptOptions
import org.jetbrains.kotlin.checkers.CheckerTestUtil import org.jetbrains.kotlin.checkers.CheckerTestUtil
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
@@ -35,7 +38,6 @@ import org.jetbrains.kotlin.kapt.base.test.JavaKaptContextTest
import org.jetbrains.kotlin.kapt3.* import org.jetbrains.kotlin.kapt3.*
import org.jetbrains.kotlin.kapt3.Kapt3ComponentRegistrar.KaptComponentContributor import org.jetbrains.kotlin.kapt3.Kapt3ComponentRegistrar.KaptComponentContributor
import org.jetbrains.kotlin.kapt3.base.KaptContext 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.doAnnotationProcessing
import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaLog import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaLog
import org.jetbrains.kotlin.kapt3.base.parseJavaFiles import org.jetbrains.kotlin.kapt3.base.parseJavaFiles
@@ -57,7 +59,6 @@ import java.nio.file.Files
import java.util.* import java.util.*
import com.sun.tools.javac.util.List as JavacList import com.sun.tools.javac.util.List as JavacList
abstract class AbstractKotlinKapt3Test : CodegenTestCase() { abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
companion object { companion object {
const val FILE_SEPARATOR = "\n\n////////////////////\n\n" const val FILE_SEPARATOR = "\n\n////////////////////\n\n"
@@ -67,8 +68,14 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
val messageCollector = PrintingMessageCollector(ERR_PRINT_STREAM, MessageRenderer.PLAIN_FULL_PATHS, false) val messageCollector = PrintingMessageCollector(ERR_PRINT_STREAM, MessageRenderer.PLAIN_FULL_PATHS, false)
} }
val kaptFlags = mutableListOf<KaptFlag>()
private val tempFiles = mutableListOf<File>() private val tempFiles = mutableListOf<File>()
override fun setUp() {
super.setUp()
kaptFlags.clear()
}
override fun tearDown() { override fun tearDown() {
ERR_BYTE_STREAM.reset() ERR_BYTE_STREAM.reset()
super.tearDown() super.tearDown()
@@ -81,12 +88,15 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
} }
} }
private fun createTempDir(prefix: String): File {
return Files.createTempDirectory(prefix).toFile().apply { tempFiles += this }
}
override fun loadMultiFiles(files: List<TestFile>) { override fun loadMultiFiles(files: List<TestFile>) {
val project = myEnvironment.project val project = myEnvironment.project
val psiManager = PsiManager.getInstance(project) val psiManager = PsiManager.getInstance(project)
val tmpDir = Files.createTempDirectory("kaptTest").toFile() val tmpDir = createTempDir("kaptTest")
tempFiles += tmpDir
val ktFiles = ArrayList<KtFile>(files.size) val ktFiles = ArrayList<KtFile>(files.size)
for (file in files.sorted()) { for (file in files.sorted()) {
@@ -118,7 +128,7 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
val classBuilderFactory = OriginCollectingClassBuilderFactory(ClassBuilderMode.KAPT3) val classBuilderFactory = OriginCollectingClassBuilderFactory(ClassBuilderMode.KAPT3)
val generationState = GenerationUtils.compileFiles(myFiles.psiFiles, myEnvironment, classBuilderFactory) val generationState = GenerationUtils.compileFiles(myFiles.psiFiles, myEnvironment, classBuilderFactory)
val logger = MessageCollectorBackedKaptLogger(isVerbose = true, messageCollector = messageCollector) val logger = MessageCollectorBackedKaptLogger(isVerbose = true, isInfoAsWarnings = false, messageCollector = messageCollector)
val javacOptions = wholeFile.getOptionValues("JAVAC_OPTION") val javacOptions = wholeFile.getOptionValues("JAVAC_OPTION")
.map { opt -> .map { opt ->
@@ -130,21 +140,25 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
var kaptContext: KaptContext? = null var kaptContext: KaptContext? = null
try { try {
val sourceOutputDir = Files.createTempDirectory("kaptRunner").toFile() val options = KaptOptions.Builder().apply {
projectBaseDir = generationState.project.basePath?.let(::File)
compileClasspath.addAll(PathUtil.getJdkClassesRootsFromCurrentJre() + PathUtil.kotlinPathsForIdeaPlugin.stdlibPath)
val paths = KaptPaths( sourcesOutputDir = createTempDir("kaptRunner")
generationState.project.basePath?.let(::File), classesOutputDir = sourcesOutputDir
compileClasspath = PathUtil.getJdkClassesRootsFromCurrentJre() + PathUtil.kotlinPathsForIdeaPlugin.stdlibPath, stubsOutputDir = sourcesOutputDir
annotationProcessingClasspath = emptyList(), javaSourceRoots = emptyList(), incrementalDataOutputDir = sourcesOutputDir
sourcesOutputDir = sourceOutputDir, classFilesOutputDir = sourceOutputDir,
stubsOutputDir = sourceOutputDir, incrementalDataOutputDir = sourceOutputDir this.javacOptions.putAll(javacOptions)
) flags.addAll(kaptFlags)
detectMemoryLeaks = DetectMemoryLeaksMode.NONE
}.build()
kaptContext = KaptContextForStubGeneration( kaptContext = KaptContextForStubGeneration(
paths, true, options, true, logger,
logger, generationState.project, generationState.bindingContext, classBuilderFactory.compiledClasses, generationState.project, generationState.bindingContext, classBuilderFactory.compiledClasses,
classBuilderFactory.origins, generationState, mapDiagnosticLocations = true, classBuilderFactory.origins, generationState
processorOptions = emptyMap(), javacOptions = javacOptions
) )
javaFiles = files javaFiles = files
@@ -165,11 +179,9 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
protected fun convert( protected fun convert(
kaptContext: KaptContextForStubGeneration, kaptContext: KaptContextForStubGeneration,
javaFiles: List<File>, javaFiles: List<File>,
generateNonExistentClass: Boolean, generateNonExistentClass: Boolean
correctErrorTypes: Boolean,
strictMode: Boolean
): JavacList<JCCompilationUnit> { ): JavacList<JCCompilationUnit> {
val converter = ClassFileToSourceStubConverter(kaptContext, generateNonExistentClass, correctErrorTypes, strictMode) val converter = ClassFileToSourceStubConverter(kaptContext, generateNonExistentClass)
val kaptStubs = converter.convert() val kaptStubs = converter.convert()
val convertedFiles = kaptStubs.map { stub -> val convertedFiles = kaptStubs.map { stub ->
@@ -245,18 +257,28 @@ open class AbstractClassFileToSourceStubConverterTest : AbstractKotlinKapt3Test(
fun testSuppressWarning() {} fun testSuppressWarning() {}
override fun doTest(filePath: String) { override fun doTest(filePath: String) {
val wholeFile = File(filePath)
kaptFlags.add(KaptFlag.MAP_DIAGNOSTIC_LOCATIONS)
if (wholeFile.isOptionSet("CORRECT_ERROR_TYPES")) {
kaptFlags.add(KaptFlag.CORRECT_ERROR_TYPES)
}
if (wholeFile.isOptionSet("STRICT_MODE")) {
kaptFlags.add(KaptFlag.STRICT)
}
super.doTest(filePath) super.doTest(filePath)
doTestWithJdk9(AbstractClassFileToSourceStubConverterTest::class.java, filePath) doTestWithJdk9(AbstractClassFileToSourceStubConverterTest::class.java, filePath)
} }
override fun check(kaptContext: KaptContextForStubGeneration, 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 validate = !wholeFile.isOptionSet("NO_VALIDATION") val validate = !wholeFile.isOptionSet("NO_VALIDATION")
val strictMode = wholeFile.isOptionSet("STRICT_MODE")
val expectedErrors = wholeFile.getRawOptionValues(EXPECTED_ERROR).sorted() val expectedErrors = wholeFile.getRawOptionValues(EXPECTED_ERROR).sorted()
val convertedFiles = convert(kaptContext, javaFiles, generateNonExistentClass, correctErrorTypes, strictMode) val convertedFiles = convert(kaptContext, javaFiles, generateNonExistentClass)
kaptContext.javaLog.interceptorData.files = convertedFiles.map { it.sourceFile to it }.toMap() kaptContext.javaLog.interceptorData.files = convertedFiles.map { it.sourceFile to it }.toMap()
if (validate) kaptContext.compiler.enterTrees(convertedFiles) if (validate) kaptContext.compiler.enterTrees(convertedFiles)
@@ -309,19 +331,22 @@ open class AbstractClassFileToSourceStubConverterTest : AbstractKotlinKapt3Test(
} }
abstract class AbstractKotlinKaptContextTest : AbstractKotlinKapt3Test() { abstract class AbstractKotlinKaptContextTest : AbstractKotlinKapt3Test() {
override fun check(kaptContext: KaptContextForStubGeneration, javaFiles: List<File>, txtFile: File, wholeFile: File) { override fun doTest(filePath: String?) {
val compilationUnits = convert( kaptFlags.add(KaptFlag.CORRECT_ERROR_TYPES)
kaptContext, javaFiles, kaptFlags.add(KaptFlag.STRICT)
generateNonExistentClass = false, correctErrorTypes = true, strictMode = false kaptFlags.add(KaptFlag.MAP_DIAGNOSTIC_LOCATIONS)
) super.doTest(filePath)
}
override fun check(kaptContext: KaptContextForStubGeneration, javaFiles: List<File>, txtFile: File, wholeFile: File) {
val compilationUnits = convert(kaptContext, javaFiles, generateNonExistentClass = false)
kaptContext.doAnnotationProcessing( kaptContext.doAnnotationProcessing(
emptyList(), emptyList(),
listOf(JavaKaptContextTest.simpleProcessor()), listOf(JavaKaptContextTest.simpleProcessor()),
additionalSources = compilationUnits additionalSources = compilationUnits
) )
val stubJavaFiles = kaptContext.paths.sourcesOutputDir.walkTopDown().filter { it.isFile && it.extension == "java" } val stubJavaFiles = kaptContext.options.sourcesOutputDir.walkTopDown().filter { it.isFile && it.extension == "java" }
val actualRaw = stubJavaFiles.sortedBy { it.name }.joinToString(FILE_SEPARATOR) { it.name + ":\n\n" + it.readText() } val actualRaw = stubJavaFiles.sortedBy { it.name }.joinToString(FILE_SEPARATOR) { it.name + ":\n\n" + it.readText() }
val actual = StringUtil.convertLineSeparators(actualRaw.trim({ it <= ' ' })).trimTrailingWhitespacesAndAddNewlineAtEOF() val actual = StringUtil.convertLineSeparators(actualRaw.trim({ it <= ' ' })).trimTrailingWhitespacesAndAddNewlineAtEOF()
KotlinTestUtils.assertEqualsToFile(txtFile, actual) KotlinTestUtils.assertEqualsToFile(txtFile, actual)
@@ -96,8 +96,8 @@ class KotlinKapt3IntegrationTests : AbstractKotlinKapt3IntegrationTest(), Java9T
test(name, "test.MyAnnotation") { _, _, _ -> test(name, "test.MyAnnotation") { _, _, _ ->
val kaptExtension = AnalysisHandlerExtension.getInstances(myEnvironment.project).firstIsInstance<Kapt3ExtensionForTests>() val kaptExtension = AnalysisHandlerExtension.getInstances(myEnvironment.project).firstIsInstance<Kapt3ExtensionForTests>()
val stubsOutputDir = kaptExtension.paths.stubsOutputDir val stubsOutputDir = kaptExtension.options.stubsOutputDir
val incrementalDataOutputDir = kaptExtension.paths.incrementalDataOutputDir val incrementalDataOutputDir = kaptExtension.options.incrementalDataOutputDir
val bindings = kaptExtension.savedBindings!! val bindings = kaptExtension.savedBindings!!