Output compilation time as teamcity statistics in jps plugin
This commit is contained in:
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user