Output compilation time as teamcity statistics in jps plugin
This commit is contained in:
@@ -16,58 +16,59 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.jps.build
|
package org.jetbrains.kotlin.jps.build
|
||||||
|
|
||||||
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.openapi.util.io.FileUtil
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
|
import com.intellij.util.containers.MultiMap
|
||||||
import gnu.trove.THashSet
|
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.ModuleChunk
|
||||||
import org.jetbrains.jps.builders.DirtyFilesHolder
|
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.JavaSourceRootDescriptor
|
||||||
|
import org.jetbrains.jps.builders.java.dependencyView.Mappings
|
||||||
import org.jetbrains.jps.incremental.*
|
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.java.JavaBuilder
|
||||||
import org.jetbrains.jps.incremental.messages.BuildMessage
|
import org.jetbrains.jps.incremental.messages.BuildMessage
|
||||||
import org.jetbrains.jps.incremental.messages.CompilerMessage
|
import org.jetbrains.jps.incremental.messages.CompilerMessage
|
||||||
import java.io.File
|
import org.jetbrains.jps.model.JpsProject
|
||||||
import java.lang.reflect.Modifier
|
import org.jetbrains.kotlin.cli.common.KotlinVersion
|
||||||
import java.util.*
|
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.CompilerMessageLocation.NO_LOCATION
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||||
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.runK2JsCompiler
|
||||||
import org.jetbrains.kotlin.compilerRunner.KotlinCompilerRunner.runK2JvmCompiler
|
import org.jetbrains.kotlin.compilerRunner.KotlinCompilerRunner.runK2JvmCompiler
|
||||||
import org.jetbrains.kotlin.utils.keysToMap
|
import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl
|
||||||
import org.jetbrains.jps.incremental.ModuleLevelBuilder.ExitCode.*
|
import org.jetbrains.kotlin.config.CompilerRunnerConstants
|
||||||
import com.intellij.openapi.diagnostic.Logger
|
import org.jetbrains.kotlin.config.CompilerRunnerConstants.INTERNAL_ERROR_PREFIX
|
||||||
import org.jetbrains.org.objectweb.asm.ClassReader
|
import org.jetbrains.kotlin.config.IncrementalCompilation
|
||||||
import org.jetbrains.jps.builders.java.JavaBuilderUtil
|
import org.jetbrains.kotlin.config.Services
|
||||||
import com.intellij.util.containers.MultiMap
|
import org.jetbrains.kotlin.jps.JpsKotlinCompilerSettings
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
import org.jetbrains.kotlin.jps.incremental.*
|
||||||
import org.jetbrains.jps.model.JpsProject
|
import org.jetbrains.kotlin.jps.incremental.IncrementalCacheImpl.RecompilationDecision.DO_NOTHING
|
||||||
import java.io.FileFilter
|
import org.jetbrains.kotlin.jps.incremental.IncrementalCacheImpl.RecompilationDecision.RECOMPILE_ALL_IN_CHUNK_AND_DEPENDANTS
|
||||||
import org.jetbrains.kotlin.jps.incremental.IncrementalCacheImpl.RecompilationDecision.*
|
import org.jetbrains.kotlin.jps.incremental.IncrementalCacheImpl.RecompilationDecision.RECOMPILE_OTHER_IN_CHUNK_AND_DEPENDANTS
|
||||||
import org.jetbrains.kotlin.compilerRunner.SimpleOutputItem
|
import org.jetbrains.kotlin.jps.incremental.IncrementalCacheImpl.RecompilationDecision.RECOMPILE_OTHER_KOTLIN_IN_CHUNK
|
||||||
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.load.kotlin.PackageClassUtils
|
import org.jetbrains.kotlin.load.kotlin.PackageClassUtils
|
||||||
import org.jetbrains.kotlin.load.kotlin.header.isCompatiblePackageFacadeKind
|
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.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.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) {
|
public class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
|
||||||
companion object {
|
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 LOG = Logger.getInstance("#org.jetbrains.jps.cmdline.BuildSession")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val statisticsLogger = TeamcityStatisticsLogger()
|
||||||
|
|
||||||
override fun getPresentableName() = KOTLIN_BUILDER_NAME
|
override fun getPresentableName() = KOTLIN_BUILDER_NAME
|
||||||
|
|
||||||
override fun getCompilableFileExtensions() = arrayListOf("kt")
|
override fun getCompilableFileExtensions() = arrayListOf("kt")
|
||||||
@@ -127,44 +130,17 @@ public class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR
|
|||||||
val allCompiledFiles = getAllCompiledFilesContainer(context)
|
val allCompiledFiles = getAllCompiledFilesContainer(context)
|
||||||
val filesToCompile = KotlinSourceFileCollector.getDirtySourceFiles(dirtyFilesHolder)
|
val filesToCompile = KotlinSourceFileCollector.getDirtySourceFiles(dirtyFilesHolder)
|
||||||
|
|
||||||
val outputItemCollector = if (JpsUtils.isJsKotlinModule(chunk.representativeTarget())) {
|
val start = System.nanoTime()
|
||||||
compileToJs(chunk, commonArguments, environment, messageCollector, project)
|
val outputItemCollector = doCompileModuleChunk(allCompiledFiles, chunk, commonArguments, context, dirtyFilesHolder,
|
||||||
}
|
environment, filesToCompile, incrementalCaches, 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 representativeTarget = chunk.representativeTarget()
|
statisticsLogger.registerStatistic(chunk, System.nanoTime() - start)
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outputItemCollector == null) {
|
if (outputItemCollector == null) {
|
||||||
return NOTHING_DONE
|
return NOTHING_DONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val compilationErrors = Utils.ERRORS_DETECTED_KEY[context, false]
|
val compilationErrors = Utils.ERRORS_DETECTED_KEY[context, false]
|
||||||
val generatedFiles = getGeneratedFiles(chunk, outputItemCollector)
|
val generatedFiles = getGeneratedFiles(chunk, outputItemCollector)
|
||||||
|
|
||||||
@@ -217,6 +193,46 @@ public class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR
|
|||||||
return OK
|
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 {
|
private fun createCompileEnvironment(incrementalCaches: Map<ModuleBuildTarget, IncrementalCache>): CompilerEnvironment {
|
||||||
val compilerServices = Services.Builder()
|
val compilerServices = Services.Builder()
|
||||||
.register(javaClass<IncrementalCacheProvider>(), IncrementalCacheProviderImpl(incrementalCaches))
|
.register(javaClass<IncrementalCacheProvider>(), IncrementalCacheProviderImpl(incrementalCaches))
|
||||||
@@ -490,7 +506,10 @@ public class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR
|
|||||||
else -> throw IllegalArgumentException("Unsupported severity: " + severity)
|
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