Kapt: verbose output mode

(cherry picked from commit e6067d5)
This commit is contained in:
Yan Zhulanow
2016-08-04 22:41:01 +03:00
committed by Yan Zhulanow
parent f5d58d9944
commit 399059729d
4 changed files with 64 additions and 14 deletions
@@ -34,13 +34,15 @@ import java.io.File
import java.net.URLClassLoader
import java.util.*
import javax.annotation.processing.Processor
import javax.tools.Diagnostic
class ClasspathBasedAnnotationProcessingExtension(
val annotationProcessingClasspath: List<String>,
generatedSourcesOutputDir: File,
classesOutputDir: File,
javaSourceRoots: List<File>
) : AbstractAnnotationProcessingExtension(generatedSourcesOutputDir, classesOutputDir, javaSourceRoots) {
javaSourceRoots: List<File>,
verboseOutput: Boolean
) : AbstractAnnotationProcessingExtension(generatedSourcesOutputDir, classesOutputDir, javaSourceRoots, verboseOutput) {
override fun loadAnnotationProcessors(): List<Processor> {
val classLoader = URLClassLoader(annotationProcessingClasspath.map { File(it).toURI().toURL() }.toTypedArray())
return ServiceLoader.load(Processor::class.java, classLoader).toList()
@@ -50,10 +52,16 @@ class ClasspathBasedAnnotationProcessingExtension(
abstract class AbstractAnnotationProcessingExtension(
val generatedSourcesOutputDir: File,
val classesOutputDir: File,
val javaSourceRoots: List<File>
val javaSourceRoots: List<File>,
val verboseOutput: Boolean
) : AnalysisCompletedHandlerExtension {
private var annotationProcessingComplete = false
private val messager = KotlinMessager()
private inline fun log(message: () -> String) {
if (verboseOutput) messager.printMessage(Diagnostic.Kind.OTHER, "Kapt: " + message())
}
override fun analysisCompleted(
project: Project,
module: ModuleDescriptor,
@@ -63,13 +71,20 @@ abstract class AbstractAnnotationProcessingExtension(
if (annotationProcessingComplete) {
return null
}
val processors = loadAnnotationProcessors()
if (processors.isEmpty()) return null
if (processors.isEmpty()) {
log { "No annotation processors detected, exiting" }
return null
}
log { "Analysing Kotlin files: " + files.map { it.virtualFile.path } }
val analysisContext = AnalysisContext(hashMapOf())
analysisContext.analyzeFiles(files)
log { "Analysing Java source roots: $javaSourceRoots" }
val psiManager = PsiManager.getInstance(project)
for (javaSourceRoot in javaSourceRoots) {
javaSourceRoot.walk().filter { it.isFile && it.extension == "java" }.forEach {
@@ -84,19 +99,21 @@ abstract class AbstractAnnotationProcessingExtension(
}
val options = emptyMap<String, String>()
log { "Options: $options" }
val javaPsiFacade = JavaPsiFacade.getInstance(project)
val projectScope = GlobalSearchScope.projectScope(project)
val filer = KotlinFiler(generatedSourcesOutputDir, classesOutputDir)
val messages = KotlinMessager()
val types = KotlinTypes(javaPsiFacade, PsiManager.getInstance(project), projectScope)
val elements = KotlinElements(javaPsiFacade, projectScope)
val processingEnvironment = KotlinProcessingEnvironment(elements, types, messages, options, filer)
val processingEnvironment = KotlinProcessingEnvironment(elements, types, messager, options, filer)
processors.forEach { it.init(processingEnvironment) }
// Round 1
log { "Initialized processors: " + processors.joinToString { it.javaClass.name } }
log { "Starting round 1" }
val round1Environment = KotlinRoundEnvironment(analysisContext)
for (processor in processors) {
val supportedAnnotationNames = processor.supportedAnnotationTypes
@@ -107,20 +124,31 @@ abstract class AbstractAnnotationProcessingExtension(
false -> processor.supportedAnnotationTypes.filter { it in analysisContext.annotationsMap }
}
if (applicableAnnotationNames.isEmpty()) continue
if (applicableAnnotationNames.isEmpty()) {
log { "Skipping processor " + processor.javaClass.name + ": no relevant annotations" }
continue
}
val applicableAnnotations = applicableAnnotationNames
.map { javaPsiFacade.findClass(it, projectScope)?.let { JeTypeElement(it) } }
.filterNotNullTo(hashSetOf())
log {
val annotationNames = applicableAnnotations.joinToString { it.qualifiedName.toString() }
"Processing with " + processor.javaClass.name + " (annotations: " + annotationNames + ")"
}
processor.process(applicableAnnotations, round1Environment)
}
// Round 2
log { "Starting round 2 (final)" }
val round2Environment = KotlinRoundEnvironment(AnalysisContext(hashMapOf()), isProcessingOver = true)
for (processor in processors) {
processor.process(emptySet(), round2Environment)
}
fun Int.count(noun: String) = if (this == 1) "$this $noun" else "$this ${noun}s"
log { "Annotation processing complete, ${messager.errorCount.count("error")}, ${messager.warningCount.count("warning")}" }
annotationProcessingComplete = true
return AnalysisResult.RetryWithAdditionalJavaRoots(bindingContext, module, listOf(generatedSourcesOutputDir))
@@ -37,6 +37,9 @@ object AnnotationProcessingConfigurationKeys {
val ANNOTATION_PROCESSOR_CLASSPATH: CompilerConfigurationKey<List<String>> =
CompilerConfigurationKey.create<List<String>>("annotation processor classpath")
val VERBOSE_MODE: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>("verbose mode")
}
class AnnotationProcessingCommandLineProcessor : CommandLineProcessor {
@@ -52,12 +55,15 @@ class AnnotationProcessingCommandLineProcessor : CommandLineProcessor {
val ANNOTATION_PROCESSOR_CLASSPATH_OPTION: CliOption =
CliOption("apclasspath", "<classpath>", "Annotation processor classpath",
required = false, allowMultipleOccurrences = true)
val VERBOSE_MODE_OPTION: CliOption =
CliOption("verbose", "true | false", "Enable verbose output", required = false)
}
override val pluginId: String = ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID
override val pluginOptions: Collection<CliOption> =
listOf(GENERATED_OUTPUT_DIR_OPTION, ANNOTATION_PROCESSOR_CLASSPATH_OPTION, CLASS_FILES_OUTPUT_DIR_OPTION)
listOf(GENERATED_OUTPUT_DIR_OPTION, ANNOTATION_PROCESSOR_CLASSPATH_OPTION, CLASS_FILES_OUTPUT_DIR_OPTION, VERBOSE_MODE_OPTION)
override fun processOption(option: CliOption, value: String, configuration: CompilerConfiguration) {
when (option) {
@@ -68,6 +74,7 @@ class AnnotationProcessingCommandLineProcessor : CommandLineProcessor {
}
GENERATED_OUTPUT_DIR_OPTION -> configuration.put(AnnotationProcessingConfigurationKeys.GENERATED_OUTPUT_DIR, value)
CLASS_FILES_OUTPUT_DIR_OPTION -> configuration.put(AnnotationProcessingConfigurationKeys.CLASS_FILES_OUTPUT_DIR, value)
VERBOSE_MODE_OPTION -> configuration.put(AnnotationProcessingConfigurationKeys.VERBOSE_MODE, value)
else -> throw CliOptionProcessingException("Unknown option: ${option.name}")
}
}
@@ -88,8 +95,10 @@ class AnnotationProcessingComponentRegistrar : ComponentRegistrar {
val classesOutputDir = File(configuration.get(AnnotationProcessingConfigurationKeys.CLASS_FILES_OUTPUT_DIR)
?: configuration[JVMConfigurationKeys.MODULES]!!.first().getOutputDirectory())
val verboseOutput = configuration.get(AnnotationProcessingConfigurationKeys.VERBOSE_MODE) == "true"
val annotationProcessingExtension = ClasspathBasedAnnotationProcessingExtension(
classpath, generatedOutputDirFile, classesOutputDir, javaRoots)
classpath, generatedOutputDirFile, classesOutputDir, javaRoots, verboseOutput)
AnalysisCompletedHandlerExtension.registerExtension(project, annotationProcessingExtension)
}
}
@@ -24,6 +24,12 @@ import javax.tools.Diagnostic
import javax.tools.Diagnostic.Kind
class KotlinMessager : Messager {
var errorCount: Int = 0
private set
var warningCount: Int = 0
private set
override fun printMessage(kind: Diagnostic.Kind, msg: CharSequence) = printMessage(kind, msg, null)
override fun printMessage(kind: Diagnostic.Kind, msg: CharSequence, e: Element?) = printMessage(kind, msg, e, null)
@@ -34,7 +40,14 @@ class KotlinMessager : Messager {
override fun printMessage(kind: Diagnostic.Kind, msg: CharSequence, e: Element?, a: AnnotationMirror?, v: AnnotationValue?) {
val output = when (kind) {
Kind.ERROR, Kind.WARNING, Kind.MANDATORY_WARNING -> System.err
Kind.ERROR -> {
errorCount++
System.err
}
Kind.WARNING, Kind.MANDATORY_WARNING -> {
warningCount++
System.err
}
else -> System.out
}
output.println(msg)