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
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.kapt3.base
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.WriterBackedKaptLogger
import org.jetbrains.kotlin.kapt3.base.util.info
@@ -16,32 +17,26 @@ object Kapt {
@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)
fun kaptFlags(rawFlags: Set<String>): KaptFlags {
return KaptFlags.fromSet(KaptFlag.values().filterTo(mutableSetOf()) { it.name in rawFlags })
}
@JvmStatic
@Suppress("unused")
fun kapt(options: KaptOptions): Boolean {
val logger = WriterBackedKaptLogger(options[KaptFlag.VERBOSE])
if (!Kapt.checkJavacComponentsAccess(logger)) {
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 { "Map diagnostic locations: $mapDiagnosticLocations" }
paths.log(logger)
logger.info { "Javac options: $javacOptions" }
logger.info { "AP options: $processorOptions" }
logger.info { options.logString("stand-alone mode") }
val javaSourceFiles = paths.collectJavaSourceFiles()
logger.info { "Java source files: " + javaSourceFiles.joinToString { it.canonicalPath } }
val javaSourceFiles = options.collectJavaSourceFiles()
val processorLoader = ProcessorLoader(paths, annotationProcessorFqNames, logger)
val processorLoader = ProcessorLoader(options, logger)
processorLoader.use {
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.Log
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.KaptJavaFileManager
import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaLog
@@ -21,18 +23,11 @@ import java.io.Closeable
import java.io.File
import javax.tools.JavaFileManager
open class KaptContext(
val paths: KaptPaths,
val withJdk: Boolean,
val logger: KaptLogger,
private val mapDiagnosticLocations: Boolean,
processorOptions: Map<String, String>,
javacOptions: Map<String, String> = emptyMap()
) : Closeable {
open class KaptContext(val options: KaptOptions, val withJdk: Boolean, val logger: KaptLogger) : Closeable {
val context = Context()
val compiler: KaptJavaCompiler
val fileManager: KaptJavaFileManager
val options: Options
private val javacOptions: Options
val javaLog: KaptJavaLog
protected open fun preregisterTreeMaker(context: Context) {}
@@ -41,8 +36,8 @@ open class KaptContext(
val interceptorData = KaptJavaLog.DiagnosticInterceptorData()
context.put(Log.logKey, Context.Factory<Log> { newContext ->
KaptJavaLog(
paths.projectBaseDir, newContext, logger.errorWriter, logger.warnWriter, logger.infoWriter,
interceptorData, mapDiagnosticLocations
options.projectBaseDir, newContext, logger.errorWriter, logger.warnWriter, logger.infoWriter,
interceptorData, options[KaptFlag.MAP_DIAGNOSTIC_LOCATIONS]
)
})
}
@@ -56,13 +51,13 @@ open class KaptContext(
KaptJavaCompiler.preRegister(context)
options = Options.instance(context).apply {
for ((key, value) in processorOptions) {
javacOptions = Options.instance(context).apply {
for ((key, value) in options.processingOptions) {
val option = if (value.isEmpty()) "-A$key" else "-A$key=$value"
put(option, option) // key == value: it's intentional
}
for ((key, value) in javacOptions) {
for ((key, value) in options.javacOptions) {
if (value.isNotEmpty()) {
put(key, value)
} else {
@@ -82,26 +77,26 @@ open class KaptContext(
}
putJavacOption("CLASSPATH", "CLASS_PATH",
paths.compileClasspath.joinToString(File.pathSeparator) { it.canonicalPath })
options.compileClasspath.joinToString(File.pathSeparator) { it.canonicalPath })
@Suppress("SpellCheckingInspection")
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.D, paths.classFilesOutputDir.canonicalPath)
put(Option.S, options.sourcesOutputDir.canonicalPath)
put(Option.D, options.classesOutputDir.canonicalPath)
put(Option.ENCODING, "UTF-8")
}
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
if (isJava9OrLater()) {
for (option in Option.getJavacFileManagerOptions()) {
val value = options.get(option) ?: continue
val value = javacOptions.get(option) ?: continue
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
import org.jetbrains.kotlin.base.kapt3.KaptOptions
import org.jetbrains.kotlin.kapt3.base.util.KaptLogger
import org.jetbrains.kotlin.kapt3.base.util.info
import java.io.Closeable
@@ -16,23 +17,19 @@ import javax.annotation.processing.Processor
class LoadedProcessors(val processors: List<Processor>, val classLoader: ClassLoader)
open class ProcessorLoader(
private val paths: KaptPaths,
private val annotationProcessorFqNames: List<String>,
private val logger: KaptLogger
) : Closeable {
open class ProcessorLoader(private val options: KaptOptions, private val logger: KaptLogger) : Closeable {
private var annotationProcessingClassLoader: URLClassLoader? = null
fun loadProcessors(parentClassLoader: ClassLoader = ClassLoader.getSystemClassLoader()): LoadedProcessors {
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)
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")
annotationProcessorFqNames.mapNotNull { tryLoadProcessor(it, classLoader) }
options.processors.mapNotNull { tryLoadProcessor(it, classLoader) }
} else {
logger.info("Need to discovery annotation processors in the AP classpath")
doLoadProcessors(classLoader)
@@ -6,8 +6,10 @@
package org.jetbrains.kotlin.kapt.base.test
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.KaptPaths
import org.jetbrains.kotlin.kapt3.base.doAnnotationProcessing
import org.jetbrains.kotlin.kapt3.base.util.KaptBaseError
import org.jetbrains.kotlin.kapt3.base.util.WriterBackedKaptLogger
@@ -49,22 +51,20 @@ class JavaKaptContextTest : TestCase() {
}
private fun doAnnotationProcessing(javaSourceFile: File, processor: Processor, outputDir: File) {
KaptContext(
KaptPaths(
projectBaseDir = javaSourceFile.parentFile,
compileClasspath = emptyList(),
annotationProcessingClasspath = emptyList(),
javaSourceRoots = emptyList(),
sourcesOutputDir = outputDir,
classFilesOutputDir = outputDir,
stubsOutputDir = outputDir,
incrementalDataOutputDir = outputDir
),
withJdk = true,
logger = WriterBackedKaptLogger(isVerbose = true),
mapDiagnosticLocations = true,
processorOptions = emptyMap()
).doAnnotationProcessing(listOf(javaSourceFile), listOf(processor))
val options = KaptOptions.Builder().apply {
projectBaseDir = javaSourceFile.parentFile
sourcesOutputDir = outputDir
classesOutputDir = outputDir
stubsOutputDir = outputDir
incrementalDataOutputDir = outputDir
flags.add(KaptFlag.MAP_DIAGNOSTIC_LOCATIONS)
detectMemoryLeaks = DetectMemoryLeaksMode.NONE
}.build()
val logger = WriterBackedKaptLogger(isVerbose = true)
KaptContext(options, true, logger).doAnnotationProcessing(listOf(javaSourceFile), listOf(processor))
}
@Test
+8 -11
View File
@@ -6,7 +6,8 @@
package org.jetbrains.kotlin.kapt.base.test
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 java.io.File
import java.nio.file.Files
@@ -36,16 +37,12 @@ class KaptPathsTest : TestCase() {
val javaRoots = listOf(simpleJava, symlinkToOtherJava, symlinkToNotJava, symlinkToJavaRootDir, javaRootDir)
val paths = KaptPaths(
projectBaseDir = null,
compileClasspath = emptyList(),
annotationProcessingClasspath = emptyList(),
javaSourceRoots = javaRoots,
sourcesOutputDir = outputDir,
classFilesOutputDir = outputDir,
stubsOutputDir = outputDir,
incrementalDataOutputDir = null
)
val paths = KaptOptions.Builder().apply {
javaSourceRoots.addAll(javaRoots)
sourcesOutputDir = outputDir
classesOutputDir = outputDir
stubsOutputDir = outputDir
}.build()
val javaSourceFiles = paths.collectJavaSourceFiles()