Switching gradle plugin to incremental compilation based on build lib

KT-8487
This commit is contained in:
Ilya Chernikov
2016-01-29 12:11:19 +01:00
committed by Alexey Tsvetkov
parent d3d854ec7d
commit db3b6ff10b
@@ -1,7 +1,13 @@
package org.jetbrains.kotlin.gradle.tasks package org.jetbrains.kotlin.gradle.tasks
import com.intellij.ide.highlighter.JavaFileType import com.intellij.ide.highlighter.JavaFileType
import com.intellij.lang.Language
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtil
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiFileFactory
import com.intellij.psi.PsiJavaFile
import com.intellij.psi.impl.PsiFileFactoryImpl
import org.apache.commons.io.FileUtils import org.apache.commons.io.FileUtils
import org.apache.commons.io.FilenameUtils import org.apache.commons.io.FilenameUtils
import org.apache.commons.lang.StringUtils import org.apache.commons.lang.StringUtils
@@ -14,6 +20,8 @@ import org.gradle.api.plugins.ExtraPropertiesExtension
import org.gradle.api.tasks.SourceTask import org.gradle.api.tasks.SourceTask
import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.compile.AbstractCompile import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
import org.jetbrains.kotlin.build.GeneratedFile
import org.jetbrains.kotlin.cli.common.CLICompiler import org.jetbrains.kotlin.cli.common.CLICompiler
import org.jetbrains.kotlin.cli.common.ExitCode import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
@@ -22,9 +30,20 @@ import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
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.OutputMessageUtil
import org.jetbrains.kotlin.cli.js.K2JSCompiler import org.jetbrains.kotlin.cli.js.K2JSCompiler
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.compilerRunner.ArgumentUtils
import org.jetbrains.kotlin.compilerRunner.OutputItemsCollector
import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.Services import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.incremental.*
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.modules.TargetId
import org.jetbrains.kotlin.progress.CompilationCanceledStatus
import org.jetbrains.kotlin.utils.LibraryUtils import org.jetbrains.kotlin.utils.LibraryUtils
import java.io.File import java.io.File
import java.util.* import java.util.*
@@ -40,16 +59,31 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractCo
} }
abstract protected fun populateTargetSpecificArgs(args: T) abstract protected fun populateTargetSpecificArgs(args: T)
public var kotlinOptions: T = createBlankArgs() var kotlinOptions: T = createBlankArgs()
public var kotlinDestinationDir: File? = destinationDir var kotlinDestinationDir: File? = destinationDir
var compilerCalled: Boolean = false var compilerCalled: Boolean = false
private val loggerInstance = Logging.getLogger(this.javaClass) private val loggerInstance = Logging.getLogger(this.javaClass)
override fun getLogger() = loggerInstance override fun getLogger() = loggerInstance
@TaskAction
override fun compile() { override fun compile() {
assert(false, { "unexpected call to compile()" })
}
@TaskAction
fun execute(inputs: IncrementalTaskInputs): Unit {
logger.debug("Starting ${javaClass} task") logger.debug("Starting ${javaClass} task")
logger.kotlinDebug("all sources ${getSource().joinToString { it.path }}")
logger.kotlinDebug("is incremental == ${inputs.isIncremental}")
val modified = arrayListOf<File>()
val removed = arrayListOf<File>()
if (inputs.isIncremental) {
inputs.outOfDate { modified.add(it.file) }
inputs.removed { removed.add(it.file) }
}
logger.kotlinDebug("modified ${modified.joinToString { it.path }}")
logger.kotlinDebug("removed ${removed.joinToString { it.path }}")
var commonArgs = createBlankArgs()
val args = createBlankArgs() val args = createBlankArgs()
val sources = getKotlinSources() val sources = getKotlinSources()
if (sources.isEmpty()) { if (sources.isEmpty()) {
@@ -57,31 +91,45 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractCo
return return
} }
compilerCalled = true populateCommonArgs(args)
populateCommonArgs(args, sources)
populateTargetSpecificArgs(args) populateTargetSpecificArgs(args)
callCompiler(args) compilerCalled = true
val cachesDir = File(project.buildDir, "kotlin-caches")
callCompiler(args, sources, inputs.isIncremental, modified, removed, cachesDir)
afterCompileHook(args) afterCompileHook(args)
} }
private fun getKotlinSources(): List<File> = (getSource() as Iterable<File>).filter { it.isKotlinFile() } private fun getKotlinSources(): List<File> = (getSource() as Iterable<File>).filter { it.isKotlinFile() }
private fun File.isKotlinFile(): Boolean { protected fun File.isClassFile(): Boolean {
return when (FilenameUtils.getExtension(name).toLowerCase()) {
"class" -> true
else -> false
}
}
protected fun File.isKotlinFile(): Boolean {
return when (FilenameUtils.getExtension(name).toLowerCase()) { return when (FilenameUtils.getExtension(name).toLowerCase()) {
"kt", "kts" -> true "kt", "kts" -> true
else -> false else -> false
} }
} }
private fun populateCommonArgs(args: T, sources: List<File>) { private fun populateSources(args:T, sources: List<File>) {
args.freeArgs = sources.map { it.absolutePath } args.freeArgs = sources.map { it.absolutePath }
}
private fun populateCommonArgs(args: T) {
args.suppressWarnings = kotlinOptions.suppressWarnings args.suppressWarnings = kotlinOptions.suppressWarnings
args.verbose = kotlinOptions.verbose args.verbose = kotlinOptions.verbose
args.version = kotlinOptions.version args.version = kotlinOptions.version
args.noInline = kotlinOptions.noInline args.noInline = kotlinOptions.noInline
} }
private fun callCompiler(args: T) { protected open fun callCompiler(args: T, sources: List<File>, isIncremental: Boolean, modified: List<File>, removed: List<File>, cachesBaseDir: File) {
populateSources(args, sources)
val messageCollector = GradleMessageCollector(logger) val messageCollector = GradleMessageCollector(logger)
logger.debug("Calling compiler") logger.debug("Calling compiler")
val exitCode = compiler.exec(messageCollector, Services.EMPTY, args) val exitCode = compiler.exec(messageCollector, Services.EMPTY, args)
@@ -95,7 +143,7 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractCo
} }
public open class KotlinCompile() : AbstractKotlinCompile<K2JVMCompilerArguments>() { open class KotlinCompile() : AbstractKotlinCompile<K2JVMCompilerArguments>() {
override val compiler = K2JVMCompiler() override val compiler = K2JVMCompiler()
override fun createBlankArgs(): K2JVMCompilerArguments = K2JVMCompilerArguments() override fun createBlankArgs(): K2JVMCompilerArguments = K2JVMCompilerArguments()
@@ -108,9 +156,12 @@ public open class KotlinCompile() : AbstractKotlinCompile<K2JVMCompilerArguments
"compileReleaseUnitTestKotlin" to "compileReleaseKotlin" "compileReleaseUnitTestKotlin" to "compileReleaseKotlin"
) )
fun projectRelativePath(f: File) = f.toRelativeString(project.projectDir)
fun projectRelativePath(p: String) = File(p).toRelativeString(project.projectDir)
override fun populateTargetSpecificArgs(args: K2JVMCompilerArguments) { override fun populateTargetSpecificArgs(args: K2JVMCompilerArguments) {
// show kotlin compiler where to look for java source files // show kotlin compiler where to look for java source files
args.freeArgs = (args.freeArgs + getJavaSourceRoots().map { it.absolutePath }).toSet().toList() // args.freeArgs = (args.freeArgs + getJavaSourceRoots().map { it.getAbsolutePath() }).toSet().toList()
logger.kotlinDebug("args.freeArgs = ${args.freeArgs}") logger.kotlinDebug("args.freeArgs = ${args.freeArgs}")
if (StringUtils.isEmpty(kotlinOptions.classpath)) { if (StringUtils.isEmpty(kotlinOptions.classpath)) {
@@ -151,7 +202,7 @@ public open class KotlinCompile() : AbstractKotlinCompile<K2JVMCompilerArguments
if (tasks.size == 1) { if (tasks.size == 1) {
val task = tasks.firstOrNull() as? KotlinCompile val task = tasks.firstOrNull() as? KotlinCompile
if (task != null) { if (task != null) {
logger.kotlinDebug("destinantion directory for production = ${task.destinationDir}") logger.kotlinDebug("destination directory for production = ${task.destinationDir}")
args.friendPaths = arrayOf(task.destinationDir.absolutePath) args.friendPaths = arrayOf(task.destinationDir.absolutePath)
args.moduleName = task.kotlinOptions.moduleName ?: task.extensions.extraProperties.getOrNull<String>("defaultModuleName") args.moduleName = task.kotlinOptions.moduleName ?: task.extensions.extraProperties.getOrNull<String>("defaultModuleName")
} }
@@ -165,16 +216,223 @@ public open class KotlinCompile() : AbstractKotlinCompile<K2JVMCompilerArguments
logger.kotlinDebug("args.moduleName = ${args.moduleName}") logger.kotlinDebug("args.moduleName = ${args.moduleName}")
} }
override fun callCompiler(args: K2JVMCompilerArguments, sources: List<File>, isIncremental: Boolean, modified: List<File>, removed: List<File>, cachesBaseDir: File) {
// TODO: consider other ways to pass incremental flag to compiler/builder
System.setProperty("kotlin.incremental.compilation", "true")
// TODO: experimental should be removed as soon as it becomes standard
System.setProperty("kotlin.incremental.compilation.experimental", "true")
val targetType = "java-production"
val moduleName = args.moduleName
val targets = listOf(TargetId(moduleName, targetType))
val outputDir = File(args.destination)
val caches = hashMapOf<TargetId, IncrementalCacheImpl<TargetId>>()
val lookupStorage = LookupStorage(File(cachesBaseDir, "lookups"))
val lookupTracker = LookupTrackerImpl(LookupTracker.DO_NOTHING)
var currentRemoved = removed
val allGeneratedFiles = hashSetOf<GeneratedFile<TargetId>>()
fun getOrCreateIncrementalCache(target: TargetId): IncrementalCacheImpl<TargetId> {
val cacheDir = File(cachesBaseDir, "increCache.${target.name}")
cacheDir.mkdirs()
return IncrementalCacheImpl(targetDataRoot = cacheDir, targetOutputDir = outputDir, target = target)
}
fun PsiClass.findLookupSymbols(): Iterable<LookupSymbol> {
val fqn = qualifiedName.orEmpty()
return listOf(LookupSymbol(name.orEmpty(), if (fqn == name) "" else fqn.removeSuffix("." + name!!))) +
methods.map { LookupSymbol(it.name, fqn) } +
fields.map { LookupSymbol(it.name.orEmpty(), fqn) } +
innerClasses.flatMap { it.findLookupSymbols() }
}
fun dirtyKotlinSourcesFromGradle(): List<File> {
val kotlinFiles = modified.filter { it.isKotlinFile() }
val javaFiles = modified.filter { it.isJavaFile() }
if (javaFiles.any()) {
val rootDisposable = Disposer.newDisposable()
val configuration = CompilerConfiguration()
val environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
val project = environment.project
val psiFileFactory = PsiFileFactory.getInstance(project) as PsiFileFactoryImpl
val lookupSymbols = javaFiles.flatMap {
val javaFile = psiFileFactory.createFileFromText(it.nameWithoutExtension, Language.findLanguageByID("JAVA")!!, it.readText())
if (javaFile is PsiJavaFile)
javaFile.classes.flatMap { it.findLookupSymbols() }
else listOf()
}
logger.kotlinDebug("changed java symbols: ${lookupSymbols.joinToString { "${it.name}:${it.scope}" }}")
if (lookupSymbols.any()) {
return kotlinFiles + lookupSymbols.flatMap { lookupStorage.get(it) }.map { File(it) }
}
}
return kotlinFiles
}
fun isClassPathChanged(): Boolean {
// TODO: that doesn't look to wise - join it first and then split here, consider storing it somewhere in between
val classpath = args.classpath.split(File.pathSeparator).map { File(it) }.toHashSet()
val changedClasspath = modified.filter { classpath.contains(it) }
if (changedClasspath.any()) {
logger.kotlinDebug("recompiling project because of classpath changes: $changedClasspath")
return true
}
return false
}
fun cleanupOnError() {
val outputDirFile = File(args.destination!!)
assert(outputDirFile.exists())
val generatedRelPaths = allGeneratedFiles.map { it.outputFile.toRelativeString(outputDirFile) }
logger.kotlinDebug("deleting output on error: ${generatedRelPaths.joinToString()}")
allGeneratedFiles.forEach { it.outputFile.delete() }
generatedRelPaths.forEach { File(destinationDir, it).delete() }
}
fun outputRelativePath(f: File) = f.toRelativeString(outputDir)
fun outputRelativePath(p: String) = File(p).toRelativeString(outputDir)
// TODO: decide what to do if no files are considered dirty - rebuild or skip the module
// TODO: more precise will be not to rebuild unconditionally on classpath changes, but retrieve lookup info and try to find out which sources are affected by cp changes
var sourcesToCompile =
if (isIncremental && !isClassPathChanged()) dirtyKotlinSourcesFromGradle().distinct()
else sources
if (isIncremental) {
args.classpath = args.classpath + File.pathSeparator + outputDir.absolutePath
}
while (sourcesToCompile.any()) {
logger.kotlinDebug("compile iteration: ${sourcesToCompile.joinToString(", ")}")
val (exitCode, generatedFiles) = compileChanged(
targets = targets,
sourcesToCompile = sourcesToCompile,
outputDir = outputDir,
args = args,
getIncrementalCache = { caches.getOrPut(it, { getOrCreateIncrementalCache(it) }) },
lookupTracker = lookupTracker)
allGeneratedFiles.addAll(generatedFiles)
// save versions?
val changes = updateIncrementalCaches(
targets = targets,
generatedFiles = generatedFiles,
compiledWithErrors = exitCode != ExitCode.OK,
getIncrementalCache = { caches[it]!! })
lookupTracker.lookups.entrySet().forEach {
logger.kotlinDebug("lookups to ${it.key.name}:${it.key.scope} from ${it.value.joinToString { projectRelativePath(it) }}")
}
lookupStorage.update(lookupTracker, sourcesToCompile, currentRemoved)
when (exitCode) {
ExitCode.COMPILATION_ERROR -> {
cleanupOnError()
throw GradleException("Compilation error. See log for more details")
}
ExitCode.INTERNAL_ERROR -> {
cleanupOnError()
throw GradleException("Internal compiler error. See log for more details")
}
}
logger.kotlinDebug("generated ${generatedFiles.joinToString { outputRelativePath(it.outputFile) }}")
logger.kotlinDebug("changes: ${changes.changes.joinToString { "${it.fqName}: ${it.javaClass.simpleName}" }}")
logger.kotlinLazyDebug({
"known lookups:\n${lookupStorage.dump(changes.changes.flatMap {
change ->
if (change is ChangeInfo.MembersChanged)
change.names.asSequence().map { LookupSymbol(it, change.fqName.asString()) }
else
sequenceOf<LookupSymbol>()
}.toSet(), project.projectDir)}" })
if (!isIncremental) break;
// TODO: consider using some order-preserving set for sourcesToCompile instead
val compiledSourcesSet = sourcesToCompile.toHashSet()
val dirtyLookups = changes.dirtyLookups<TargetId>(caches.values.asSequence())
logger.kotlinDebug("dirty lookups: ${dirtyLookups.joinToString { "${it.name}:${it.scope}" }}")
val dirty = dirtyLookups.flatMap { lookup ->
val files = lookupStorage.get(lookup).map(::File).filter { it !in compiledSourcesSet }
if (files.any()) {
logger.kotlinDebug("changes in $lookup causes recompilation of ${files.joinToString { projectRelativePath(it) }}")
}
files
}
//val dirty = changes.dirtyFiles(lookupStorage).filter { it !in compiledSourcesSet }
sourcesToCompile = dirty.filter { it in sources }.toList()
if (currentRemoved.any()) {
currentRemoved = listOf()
}
logger.kotlinDebug("dirty: ${dirty.joinToString { projectRelativePath(it) }}")
logger.kotlinDebug("to compile: ${sourcesToCompile.joinToString { projectRelativePath(it) }}")
}
lookupStorage.flush(false)
lookupStorage.close()
caches.values.forEach { it.flush(false); it.close() }
}
private data class CompileChangedResults(val exitCode: ExitCode, val generatedFiles: List<GeneratedFile<TargetId>>)
private fun compileChanged(targets: List<TargetId>,
sourcesToCompile: List<File>,
outputDir: File,
args: K2JVMCompilerArguments,
getIncrementalCache: (TargetId) -> IncrementalCacheImpl<TargetId>,
lookupTracker: LookupTracker)
: CompileChangedResults
{
// show kotlin compiler where to look for java source files
args.freeArgs = (sourcesToCompile.map { it.absolutePath } + getJavaSourceRoots().map { it.absolutePath }).distinct()
args.destination = outputDir.absolutePath
logger.kotlinDebug("compiling with args ${ArgumentUtils.convertArgumentsToStringList(args)}")
val outputItemCollector = OutputItemsCollectorImpl()
val messageCollector = GradleMessageCollector(logger, outputItemCollector)
val incrementalCaches = makeIncrementalCachesMap(targets, { listOf<TargetId>() }, getIncrementalCache, { this })
val compilationCanceledStatus = object : CompilationCanceledStatus {
override fun checkCanceled() {}
}
logger.kotlinDebug("compiling with args ${ArgumentUtils.convertArgumentsToStringList(args)}")
val exitCode = compiler.exec(messageCollector, makeCompileServices(incrementalCaches, lookupTracker, compilationCanceledStatus), args)
return CompileChangedResults(
exitCode,
outputItemCollector.generatedFiles(
targets = targets,
representativeTarget = targets.first(),
getSources = { sourcesToCompile },
getOutputDir = { outputDir }))
}
private fun handleKaptProperties(extraProperties: ExtraPropertiesExtension, pluginOptions: MutableList<String>) { private fun handleKaptProperties(extraProperties: ExtraPropertiesExtension, pluginOptions: MutableList<String>) {
val kaptAnnotationsFile = extraProperties.getOrNull<File>("kaptAnnotationsFile") val kaptAnnotationsFile = extraProperties.getOrNull<File>("kaptAnnotationsFile")
if (kaptAnnotationsFile != null) { if (kaptAnnotationsFile != null) {
if (kaptAnnotationsFile.exists()) kaptAnnotationsFile.delete() if (kaptAnnotationsFile.exists()) kaptAnnotationsFile.delete()
pluginOptions.add("plugin:$ANNOTATIONS_PLUGIN_NAME:output=$kaptAnnotationsFile") pluginOptions.add("plugin:$ANNOTATIONS_PLUGIN_NAME:output=" + kaptAnnotationsFile)
} }
val kaptClassFileStubsDir = extraProperties.getOrNull<File>("kaptStubsDir") val kaptClassFileStubsDir = extraProperties.getOrNull<File>("kaptStubsDir")
if (kaptClassFileStubsDir != null) { if (kaptClassFileStubsDir != null) {
pluginOptions.add("plugin:$ANNOTATIONS_PLUGIN_NAME:stubs=$kaptClassFileStubsDir") pluginOptions.add("plugin:$ANNOTATIONS_PLUGIN_NAME:stubs=" + kaptClassFileStubsDir)
} }
val supportInheritedAnnotations = extraProperties.getOrNull<Boolean>("kaptInheritedAnnotations") val supportInheritedAnnotations = extraProperties.getOrNull<Boolean>("kaptInheritedAnnotations")
@@ -190,7 +448,7 @@ public open class KotlinCompile() : AbstractKotlinCompile<K2JVMCompilerArguments
.filterNotNull() .filterNotNull()
.toSet() .toSet()
private fun File.isJavaFile() = extension.equals(JavaFileType.INSTANCE.getDefaultExtension(), ignoreCase = true) private fun File.isJavaFile() = extension.equals(JavaFileType.INSTANCE.defaultExtension, ignoreCase = true)
override fun afterCompileHook(args: K2JVMCompilerArguments) { override fun afterCompileHook(args: K2JVMCompilerArguments) {
logger.debug("Copying resulting files to classes") logger.debug("Copying resulting files to classes")
@@ -246,7 +504,7 @@ public open class KotlinCompile() : AbstractKotlinCompile<K2JVMCompilerArguments
} }
} }
public open class Kotlin2JsCompile() : AbstractKotlinCompile<K2JSCompilerArguments>() { open class Kotlin2JsCompile() : AbstractKotlinCompile<K2JSCompilerArguments>() {
override val compiler = K2JSCompiler() override val compiler = K2JSCompiler()
override fun createBlankArgs(): K2JSCompilerArguments { override fun createBlankArgs(): K2JSCompilerArguments {
@@ -257,22 +515,22 @@ public open class Kotlin2JsCompile() : AbstractKotlinCompile<K2JSCompilerArgumen
return args return args
} }
public fun addLibraryFiles(vararg fs: String) { fun addLibraryFiles(vararg fs: String) {
kotlinOptions.libraryFiles += fs kotlinOptions.libraryFiles += fs
} }
public fun addLibraryFiles(vararg fs: File) { fun addLibraryFiles(vararg fs: File) {
val strs = fs.map { it.path }.toTypedArray() val strs = fs.map { it.path }.toTypedArray()
addLibraryFiles(*strs) addLibraryFiles(*strs)
} }
public val outputFile: String? val outputFile: String?
get() = kotlinOptions.outputFile get() = kotlinOptions.outputFile
public val sourceMapDestinationDir: File val sourceMapDestinationDir: File
get() = File(outputFile).let { if (it.isDirectory) it else it.parentFile!! } get() = File(outputFile).let { if (it.isDirectory) it else it.parentFile!! }
public val sourceMap: Boolean val sourceMap: Boolean
get() = kotlinOptions.sourceMap get() = kotlinOptions.sourceMap
init { init {
@@ -323,8 +581,8 @@ private fun <T: Any> ExtraPropertiesExtension.getOrNull(id: String): T? {
} }
} }
class GradleMessageCollector(val logger: Logger) : MessageCollector { class GradleMessageCollector(val logger: Logger, val outputCollector: OutputItemsCollector? = null) : MessageCollector {
public override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation) { override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation) {
val text = with(StringBuilder()) { val text = with(StringBuilder()) {
append(when (severity) { append(when (severity) {
in CompilerMessageSeverity.VERBOSE -> "v" in CompilerMessageSeverity.VERBOSE -> "v"
@@ -355,9 +613,20 @@ class GradleMessageCollector(val logger: Logger) : MessageCollector {
CompilerMessageSeverity.WARNING -> logger.warn(text) CompilerMessageSeverity.WARNING -> logger.warn(text)
else -> throw IllegalArgumentException("Unknown CompilerMessageSeverity: $severity") else -> throw IllegalArgumentException("Unknown CompilerMessageSeverity: $severity")
} }
// TODO: consider adding some other way of passing input -> output mapping from compiler, e.g. dedicated service
if (outputCollector != null && severity == CompilerMessageSeverity.OUTPUT) {
OutputMessageUtil.parseOutputMessage(message)?.let {
outputCollector.add(it.sourceFiles, it.outputFile)
}
}
} }
} }
fun Logger.kotlinDebug(message: String) { fun Logger.kotlinDebug(message: String) {
this.debug("[KOTLIN] $message") this.debug("[KOTLIN] $message")
} }
fun Logger.kotlinLazyDebug(makeMessage: () -> String) {
if (this.isInfoEnabled)
kotlinDebug(makeMessage())
}