Output compilation time as teamcity statistics in jps plugin

This commit is contained in:
Pavel V. Talanov
2015-03-25 14:50:15 +03:00
parent bb1410b20a
commit 929d4644b5
2 changed files with 173 additions and 66 deletions
@@ -16,58 +16,59 @@
package org.jetbrains.kotlin.jps.build
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.io.FileUtil
import com.intellij.util.containers.MultiMap
import gnu.trove.THashSet
import org.jetbrains.kotlin.cli.common.KotlinVersion
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.compilerRunner.CompilerEnvironment
import org.jetbrains.kotlin.config.CompilerRunnerConstants
import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl
import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.jps.JpsKotlinCompilerSettings
import org.jetbrains.kotlin.jps.incremental.*
import org.jetbrains.kotlin.load.kotlin.incremental.cache.IncrementalCacheProvider
import org.jetbrains.kotlin.utils.PathUtil
import org.jetbrains.jps.ModuleChunk
import org.jetbrains.jps.builders.DirtyFilesHolder
import org.jetbrains.jps.builders.java.JavaBuilderUtil
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor
import org.jetbrains.jps.builders.java.dependencyView.Mappings
import org.jetbrains.jps.incremental.*
import org.jetbrains.jps.incremental.ModuleLevelBuilder.ExitCode.*
import org.jetbrains.jps.incremental.fs.CompilationRound
import org.jetbrains.jps.incremental.java.JavaBuilder
import org.jetbrains.jps.incremental.messages.BuildMessage
import org.jetbrains.jps.incremental.messages.CompilerMessage
import java.io.File
import java.lang.reflect.Modifier
import java.util.*
import org.jetbrains.jps.model.JpsProject
import org.jetbrains.kotlin.cli.common.KotlinVersion
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation.NO_LOCATION
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
import org.jetbrains.kotlin.config.CompilerRunnerConstants.INTERNAL_ERROR_PREFIX
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.compilerRunner.CompilerEnvironment
import org.jetbrains.kotlin.compilerRunner.KotlinCompilerRunner.runK2JsCompiler
import org.jetbrains.kotlin.compilerRunner.KotlinCompilerRunner.runK2JvmCompiler
import org.jetbrains.kotlin.utils.keysToMap
import org.jetbrains.jps.incremental.ModuleLevelBuilder.ExitCode.*
import com.intellij.openapi.diagnostic.Logger
import org.jetbrains.org.objectweb.asm.ClassReader
import org.jetbrains.jps.builders.java.JavaBuilderUtil
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.jps.model.JpsProject
import java.io.FileFilter
import org.jetbrains.kotlin.jps.incremental.IncrementalCacheImpl.RecompilationDecision.*
import org.jetbrains.kotlin.compilerRunner.SimpleOutputItem
import org.jetbrains.kotlin.utils.LibraryUtils
import org.jetbrains.kotlin.load.kotlin.incremental.cache.IncrementalCache
import org.jetbrains.jps.incremental.fs.CompilationRound
import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl
import org.jetbrains.kotlin.config.CompilerRunnerConstants
import org.jetbrains.kotlin.config.CompilerRunnerConstants.INTERNAL_ERROR_PREFIX
import org.jetbrains.kotlin.config.IncrementalCompilation
import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.jps.JpsKotlinCompilerSettings
import org.jetbrains.kotlin.jps.incremental.*
import org.jetbrains.kotlin.jps.incremental.IncrementalCacheImpl.RecompilationDecision.DO_NOTHING
import org.jetbrains.kotlin.jps.incremental.IncrementalCacheImpl.RecompilationDecision.RECOMPILE_ALL_IN_CHUNK_AND_DEPENDANTS
import org.jetbrains.kotlin.jps.incremental.IncrementalCacheImpl.RecompilationDecision.RECOMPILE_OTHER_IN_CHUNK_AND_DEPENDANTS
import org.jetbrains.kotlin.jps.incremental.IncrementalCacheImpl.RecompilationDecision.RECOMPILE_OTHER_KOTLIN_IN_CHUNK
import org.jetbrains.kotlin.load.kotlin.PackageClassUtils
import org.jetbrains.kotlin.load.kotlin.header.isCompatiblePackageFacadeKind
import org.jetbrains.jps.builders.java.dependencyView.Mappings
import org.jetbrains.kotlin.load.kotlin.incremental.cache.IncrementalCache
import org.jetbrains.kotlin.load.kotlin.incremental.cache.IncrementalCacheProvider
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.jps.model.module.JpsModule
import org.jetbrains.kotlin.utils.LibraryUtils
import org.jetbrains.kotlin.utils.PathUtil
import org.jetbrains.kotlin.utils.keysToMap
import org.jetbrains.kotlin.utils.sure
import org.jetbrains.kotlin.jps.build.KotlinJpsCompilerArgumentsProvider
import org.jetbrains.org.objectweb.asm.ClassReader
import java.io.File
import java.util.ArrayList
import java.util.HashMap
import java.util.HashSet
import java.util.ServiceLoader
public class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
companion object {
@@ -76,6 +77,8 @@ public class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR
private val LOG = Logger.getInstance("#org.jetbrains.jps.cmdline.BuildSession")
}
private val statisticsLogger = TeamcityStatisticsLogger()
override fun getPresentableName() = KOTLIN_BUILDER_NAME
override fun getCompilableFileExtensions() = arrayListOf("kt")
@@ -127,44 +130,17 @@ public class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR
val allCompiledFiles = getAllCompiledFilesContainer(context)
val filesToCompile = KotlinSourceFileCollector.getDirtySourceFiles(dirtyFilesHolder)
val outputItemCollector = if (JpsUtils.isJsKotlinModule(chunk.representativeTarget())) {
compileToJs(chunk, commonArguments, environment, messageCollector, project)
}
else {
if (IncrementalCompilation.ENABLED) {
for (target in chunk.getTargets()) {
val cache = incrementalCaches[target]!!
val removedAndDirtyFiles = filesToCompile[target] + dirtyFilesHolder.getRemovedFiles(target).map { File(it) }
cache.markOutputClassesDirty(removedAndDirtyFiles)
}
}
val start = System.nanoTime()
val outputItemCollector = doCompileModuleChunk(allCompiledFiles, chunk, commonArguments, context, dirtyFilesHolder,
environment, filesToCompile, incrementalCaches, messageCollector, project)
val representativeTarget = chunk.representativeTarget()
fun concatenate(strings: Array<String>?, cp: List<String>) = array(*(strings ?: array<String>()), *cp.copyToArray())
for (argumentProvider in ServiceLoader.load(javaClass<KotlinJpsCompilerArgumentsProvider>())) {
// appending to pluginOptions
commonArguments.pluginOptions = concatenate(commonArguments.pluginOptions,
argumentProvider.getExtraArguments(representativeTarget, context))
// appending to classpath
commonArguments.pluginClasspaths = concatenate(commonArguments.pluginClasspaths,
argumentProvider.getClasspath(representativeTarget, context))
messageCollector.report(
INFO,
"Plugin loaded: ${argumentProvider.javaClass.getSimpleName()}",
NO_LOCATION
)
}
compileToJvm(allCompiledFiles, chunk, commonArguments, context, dirtyFilesHolder, environment, filesToCompile, messageCollector)
}
statisticsLogger.registerStatistic(chunk, System.nanoTime() - start)
if (outputItemCollector == null) {
return NOTHING_DONE
}
val compilationErrors = Utils.ERRORS_DETECTED_KEY[context, false]
val generatedFiles = getGeneratedFiles(chunk, outputItemCollector)
@@ -217,6 +193,46 @@ public class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR
return OK
}
private fun doCompileModuleChunk(
allCompiledFiles: MutableSet<File>, chunk: ModuleChunk, commonArguments: CommonCompilerArguments, context: CompileContext,
dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>, environment: CompilerEnvironment,
filesToCompile: MultiMap<ModuleBuildTarget, File>, incrementalCaches: Map<ModuleBuildTarget, IncrementalCacheImpl>,
messageCollector: MessageCollectorAdapter, project: JpsProject
): OutputItemsCollectorImpl? {
if (JpsUtils.isJsKotlinModule(chunk.representativeTarget())) {
return compileToJs(chunk, commonArguments, environment, messageCollector, project)
}
if (IncrementalCompilation.ENABLED) {
for (target in chunk.getTargets()) {
val cache = incrementalCaches[target]!!
val removedAndDirtyFiles = filesToCompile[target] + dirtyFilesHolder.getRemovedFiles(target).map { File(it) }
cache.markOutputClassesDirty(removedAndDirtyFiles)
}
}
val representativeTarget = chunk.representativeTarget()
fun concatenate(strings: Array<String>?, cp: List<String>) = array(*(strings ?: array<String>()), *cp.copyToArray())
for (argumentProvider in ServiceLoader.load(javaClass<KotlinJpsCompilerArgumentsProvider>())) {
// appending to pluginOptions
commonArguments.pluginOptions = concatenate(commonArguments.pluginOptions,
argumentProvider.getExtraArguments(representativeTarget, context))
// appending to classpath
commonArguments.pluginClasspaths = concatenate(commonArguments.pluginClasspaths,
argumentProvider.getClasspath(representativeTarget, context))
messageCollector.report(
INFO,
"Plugin loaded: ${argumentProvider.javaClass.getSimpleName()}",
NO_LOCATION
)
}
return compileToJvm(allCompiledFiles, chunk, commonArguments, context, dirtyFilesHolder, environment, filesToCompile, messageCollector)
}
private fun createCompileEnvironment(incrementalCaches: Map<ModuleBuildTarget, IncrementalCache>): CompilerEnvironment {
val compilerServices = Services.Builder()
.register(javaClass<IncrementalCacheProvider>(), IncrementalCacheProviderImpl(incrementalCaches))
@@ -490,7 +506,10 @@ public class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR
else -> throw IllegalArgumentException("Unsupported severity: " + severity)
}
}
}
override fun buildFinished(context: CompileContext?) {
statisticsLogger.reportTotal()
}
}
@@ -0,0 +1,88 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.jps.build
import org.jetbrains.jps.ModuleChunk
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicLong
class TeamcityStatisticsLogger {
private val isOnTeamcity = true
private val totalTime = AtomicLong()
//NOTE: mostly copied from TeamCityBuildInfoPrinter
private fun escapedChar(c: Char): Char {
return when (c) {
'\n' -> 'n'
'\r' -> 'r'
'\u0085' -> 'x' // next-line character
'\u2028' -> 'l' // line-separator character
'\u2029' -> 'p' // paragraph-separator character
'|' -> '|'
'\'' -> '\''
'[' -> '['
']' -> ']'
else -> 0.toChar()
}
}
private fun escape(text: String): String {
val escaped = StringBuilder()
for (c in text.toCharArray()) {
val escChar = escapedChar(c)
if (escChar == 0.toChar()) {
escaped.append(c)
}
else {
escaped.append('|').append(escChar)
}
}
return escaped.toString()
}
fun registerStatistic(moduleChunk: ModuleChunk, timeToCompileNs: Long) {
if (!isOnTeamcity) return
totalTime.addAndGet(timeToCompileNs)
printPerChunkStatistics(moduleChunk, timeToCompileNs)
}
private fun printPerChunkStatistics(moduleChunk: ModuleChunk, timeToCompileNs: Long) {
printStatisticMessage(
"${KotlinBuilder.KOTLIN_BUILDER_NAME} for ${moduleChunk.getPresentableShortName()} compilation time, ms",
timeToCompileNs.nanosToMillis().toString()
)
}
fun reportTotal() {
if (!isOnTeamcity) return
printStatisticMessage(
"${KotlinBuilder.KOTLIN_BUILDER_NAME} total compilation time, ms",
totalTime.get().nanosToMillis().toString()
)
}
private fun printStatisticMessage(key: String, value: String) {
println("##teamcity[buildStatisticValue key='${escape(key)}' value='${escape(value)}']")
}
private fun Long.nanosToMillis() = TimeUnit.NANOSECONDS.toMillis(this)
}