diff --git a/compiler/daemon/daemon-common/src/org/jetbrains/kotlin/daemon/common/CompilationResults.kt b/compiler/daemon/daemon-common/src/org/jetbrains/kotlin/daemon/common/CompilationResults.kt index 8c5e3ed2590..8d25e81a632 100644 --- a/compiler/daemon/daemon-common/src/org/jetbrains/kotlin/daemon/common/CompilationResults.kt +++ b/compiler/daemon/daemon-common/src/org/jetbrains/kotlin/daemon/common/CompilationResults.kt @@ -26,5 +26,6 @@ interface CompilationResults : Remote { } enum class CompilationResultCategory(val code: Int) { - IC_COMPILE_ITERATION(0) + IC_COMPILE_ITERATION(0), + IC_LOG(1) } \ No newline at end of file diff --git a/compiler/daemon/src/org/jetbrains/kotlin/daemon/CompileServiceImpl.kt b/compiler/daemon/src/org/jetbrains/kotlin/daemon/CompileServiceImpl.kt index 879e9ae825f..11f124335b5 100644 --- a/compiler/daemon/src/org/jetbrains/kotlin/daemon/CompileServiceImpl.kt +++ b/compiler/daemon/src/org/jetbrains/kotlin/daemon/CompileServiceImpl.kt @@ -511,13 +511,18 @@ class CompileServiceImpl( val workingDir = incrementalCompilationOptions.workingDir val modulesApiHistory = ModulesApiHistoryJs(incrementalCompilationOptions.modulesInfo) + val compiler = IncrementalJsCompilerRunner( workingDir = workingDir, reporter = reporter, buildHistoryFile = incrementalCompilationOptions.multiModuleICSettings.buildHistoryFile, modulesApiHistory = modulesApiHistory ) - return compiler.compile(allKotlinFiles, args, compilerMessageCollector, changedFiles) + return try { + compiler.compile(allKotlinFiles, args, compilerMessageCollector, changedFiles) + } finally { + reporter.flush() + } } private fun execIncrementalCompiler( @@ -570,6 +575,8 @@ class CompileServiceImpl( val workingDir = incrementalCompilationOptions.workingDir val modulesApiHistory = incrementalCompilationOptions.run { + reporter.report { "Use module detection: ${multiModuleICSettings.useModuleDetection}" } + if (!multiModuleICSettings.useModuleDetection) { ModulesApiHistoryJvm(modulesInfo) } else { @@ -587,7 +594,11 @@ class CompileServiceImpl( modulesApiHistory = modulesApiHistory, kotlinSourceFilesExtensions = allKotlinExtensions ) - return compiler.compile(allKotlinFiles, k2jvmArgs, compilerMessageCollector, changedFiles) + return try { + compiler.compile(allKotlinFiles, k2jvmArgs, compilerMessageCollector, changedFiles) + } finally { + reporter.flush() + } } override fun leaseReplSession( diff --git a/compiler/daemon/src/org/jetbrains/kotlin/daemon/report/RemoteICReporter.kt b/compiler/daemon/src/org/jetbrains/kotlin/daemon/report/RemoteICReporter.kt index 3efae0f592e..fbae38fe125 100644 --- a/compiler/daemon/src/org/jetbrains/kotlin/daemon/report/RemoteICReporter.kt +++ b/compiler/daemon/src/org/jetbrains/kotlin/daemon/report/RemoteICReporter.kt @@ -22,26 +22,54 @@ import org.jetbrains.kotlin.incremental.ICReporter import java.io.File internal class RemoteICReporter( - private val servicesFacade: CompilerServicesFacadeBase, - private val compilationResults: CompilationResults, - compilationOptions: CompilationOptions + private val servicesFacade: CompilerServicesFacadeBase, + private val compilationResults: CompilationResults, + compilationOptions: IncrementalCompilationOptions ) : ICReporter { + + private val rootDir = compilationOptions.modulesInfo.projectRoot private val shouldReportMessages = ReportCategory.IC_MESSAGE.code in compilationOptions.reportCategories private val isVerbose = compilationOptions.reportSeverity == ReportSeverity.DEBUG.code - private val shouldReportCompileIteration = CompilationResultCategory.IC_COMPILE_ITERATION.code in compilationOptions.requestedCompilationResults + private val shouldReportCompileIteration = + CompilationResultCategory.IC_COMPILE_ITERATION.code in compilationOptions.requestedCompilationResults + private val shouldReportICLog = CompilationResultCategory.IC_LOG.code in compilationOptions.requestedCompilationResults + private val icLogLines = arrayListOf() override fun report(message: () -> String) { + val lazyMessage = lazy { message() } if (shouldReportMessages && isVerbose) { - servicesFacade.report(ReportCategory.IC_MESSAGE, ReportSeverity.DEBUG, message()) + servicesFacade.report(ReportCategory.IC_MESSAGE, ReportSeverity.DEBUG, lazyMessage.value) + } + if (shouldReportICLog) { + icLogLines.add(lazyMessage.value) } } override fun reportCompileIteration(sourceFiles: Collection, exitCode: ExitCode) { if (shouldReportCompileIteration) { - compilationResults.add(CompilationResultCategory.IC_COMPILE_ITERATION.code, - CompileIterationResult(sourceFiles, exitCode.toString()) + compilationResults.add( + CompilationResultCategory.IC_COMPILE_ITERATION.code, + CompileIterationResult(sourceFiles, exitCode.toString()) ) } + if (shouldReportICLog) { + icLogLines.add("compile iteration:") + sourceFiles.relativePaths(rootDir).forEach { + icLogLines.add(" $it") + } + } } + + fun flush() { + if (shouldReportICLog) { + compilationResults.add(CompilationResultCategory.IC_LOG.code, icLogLines) + } + } + + private fun File.relativeOrCanonical(base: File): String = + relativeToOrNull(base)?.path ?: canonicalPath + + private fun Iterable.relativePaths(base: File): List = + map { it.relativeOrCanonical(base) }.sorted() } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleCompilationResults.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleCompilationResults.kt index 996953cd009..553eb9d978e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleCompilationResults.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleCompilationResults.kt @@ -21,18 +21,27 @@ internal class GradleCompilationResults( LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory ) { + + var icLogLines: List? = null + @Throws(RemoteException::class) override fun add(compilationResultCategory: Int, value: Serializable) { - if (compilationResultCategory == CompilationResultCategory.IC_COMPILE_ITERATION.code) { - @Suppress("UNCHECKED_CAST") - val compileIterationResult = value as? CompileIterationResult - if (compileIterationResult != null) { - val sourceFiles = compileIterationResult.sourceFiles - if (sourceFiles.any()) { - log.kotlinDebug { "compile iteration: ${sourceFiles.pathsAsStringRelativeTo(projectRootFile)}" } + when (compilationResultCategory) { + CompilationResultCategory.IC_COMPILE_ITERATION.code -> { + @Suppress("UNCHECKED_CAST") + val compileIterationResult = value as? CompileIterationResult + if (compileIterationResult != null) { + val sourceFiles = compileIterationResult.sourceFiles + if (sourceFiles.any()) { + log.kotlinDebug { "compile iteration: ${sourceFiles.pathsAsStringRelativeTo(projectRootFile)}" } + } + val exitCode = compileIterationResult.exitCode + log.kotlinDebug { "compiler exit code: $exitCode" } } - val exitCode = compileIterationResult.exitCode - log.kotlinDebug { "compiler exit code: $exitCode" } + } + CompilationResultCategory.IC_LOG.code -> { + @Suppress("UNCHECKED_CAST") + icLogLines = value as? List } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleCompilerEnvironment.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleCompilerEnvironment.kt index abdbd766e7c..3aa360133eb 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleCompilerEnvironment.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleCompilerEnvironment.kt @@ -16,6 +16,7 @@ internal class GradleCompilerEnvironment( messageCollector: GradlePrintingMessageCollector, outputItemsCollector: OutputItemsCollector, val outputFiles: FileCollection, + val reportExecutionResult: Boolean, val incrementalCompilationEnvironment: IncrementalCompilationEnvironment? = null ) : CompilerEnvironment(Services.EMPTY, messageCollector, outputItemsCollector) { val toolsJar: File? by lazy { findToolsJar() } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt index e3dafc00de0..d55d52e355b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt @@ -141,7 +141,8 @@ internal open class GradleCompilerRunner(protected val task: Task) { incrementalModuleInfo = modulesInfo, buildFile = buildFile, outputFiles = environment.outputFiles.toList(), - taskPath = task.path + taskPath = task.path, + reportExecutionResult = environment.reportExecutionResult ) TaskLoggers.put(task.path, task.logger) runCompilerAsync(workArgs) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerWork.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerWork.kt index f9433003009..96926725e9e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerWork.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerWork.kt @@ -11,7 +11,9 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.config.Services import org.jetbrains.kotlin.daemon.common.* import org.jetbrains.kotlin.gradle.logging.* +import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers +import org.jetbrains.kotlin.gradle.report.TaskExecutionResult import org.jetbrains.kotlin.gradle.tasks.throwGradleExceptionIfError import org.jetbrains.kotlin.gradle.utils.stackTraceAsString import org.jetbrains.kotlin.incremental.ChangedFiles @@ -20,6 +22,7 @@ import org.slf4j.LoggerFactory import java.io.* import java.net.URLClassLoader import java.rmi.RemoteException +import java.util.* import java.util.concurrent.Callable import java.util.concurrent.Executors import javax.inject.Inject @@ -50,7 +53,8 @@ internal class GradleKotlinCompilerWorkArguments( val incrementalModuleInfo: IncrementalModuleInfo?, val buildFile: File?, val outputFiles: List, - val taskPath: String + val taskPath: String, + val reportExecutionResult: Boolean ) : Serializable { companion object { const val serialVersionUID: Long = 0 @@ -88,6 +92,7 @@ internal class GradleKotlinCompilerWork @Inject constructor( private val buildFile = config.buildFile private val outputFiles = config.outputFiles private val taskPath = config.taskPath + private val reportExecutionResult = config.reportExecutionResult private val log: KotlinLogger = TaskLoggers.get(taskPath)?.let { GradleKotlinLogger(it).apply { debug("Using '$taskPath' logger") } } @@ -226,7 +231,16 @@ internal class GradleKotlinCompilerWork @Inject constructor( requestedCompilationResults = emptyArray() ) val servicesFacade = GradleCompilerServicesFacadeImpl(log, bufferingMessageCollector) - return daemon.compile(sessionId, compilerArgs, compilationOptions, servicesFacade, compilationResults = null) + return try { + daemon.compile(sessionId, compilerArgs, compilationOptions, servicesFacade, compilationResults = null) + } finally { + reportExecutionResultIfNeeded { + TaskExecutionResult( + executionStrategy = DAEMON_EXECUTION_STRATEGY, + icLogLines = nonIcBuildLog("incremental compilation is not enabled for '$taskPath'") + ) + } + } } private fun incrementalCompilationWithDaemon( @@ -238,6 +252,11 @@ internal class GradleKotlinCompilerWork @Inject constructor( val icEnv = incrementalCompilationEnvironment ?: error("incrementalCompilationEnvironment is null!") val knownChangedFiles = icEnv.changedFiles as? ChangedFiles.Known + val requestedCompilationResults = EnumSet.of(CompilationResultCategory.IC_COMPILE_ITERATION) + if (reportExecutionResult) { + requestedCompilationResults.add(CompilationResultCategory.IC_LOG) + } + val compilationOptions = IncrementalCompilationOptions( areFileChangesKnown = knownChangedFiles != null, modifiedFiles = knownChangedFiles?.modified, @@ -245,7 +264,7 @@ internal class GradleKotlinCompilerWork @Inject constructor( workingDir = icEnv.workingDir, reportCategories = reportCategories(isVerbose), reportSeverity = reportSeverity(isVerbose), - requestedCompilationResults = arrayOf(CompilationResultCategory.IC_COMPILE_ITERATION.code), + requestedCompilationResults = requestedCompilationResults.map { it.code }.toTypedArray(), compilerMode = CompilerMode.INCREMENTAL_COMPILER, targetPlatform = targetPlatform, usePreciseJavaTracking = icEnv.usePreciseJavaTracking, @@ -257,11 +276,29 @@ internal class GradleKotlinCompilerWork @Inject constructor( log.info("Options for KOTLIN DAEMON: $compilationOptions") val servicesFacade = GradleIncrementalCompilerServicesFacadeImpl(log, bufferingMessageCollector) val compilationResults = GradleCompilationResults(log, projectRootFile) - return daemon.compile(sessionId, compilerArgs, compilationOptions, servicesFacade, compilationResults) + val result = daemon.compile(sessionId, compilerArgs, compilationOptions, servicesFacade, compilationResults) + + reportExecutionResultIfNeeded { + TaskExecutionResult( + executionStrategy = DAEMON_EXECUTION_STRATEGY, + icLogLines = compilationResults.icLogLines + ) + } + + return result } private fun compileOutOfProcess(): ExitCode = - runToolInSeparateProcess(compilerArgs, compilerClassName, compilerFullClasspath, log) + try { + runToolInSeparateProcess(compilerArgs, compilerClassName, compilerFullClasspath, log) + } finally { + reportExecutionResultIfNeeded { + TaskExecutionResult( + executionStrategy = OUT_OF_PROCESS_EXECUTION_STRATEGY, + icLogLines = nonIcBuildLog("$OUT_OF_PROCESS_EXECUTION_STRATEGY execution strategy does not support incremental compilation") + ) + } + } private fun compileInProcess(messageCollector: MessageCollector): ExitCode { // in-process compiler should always be run in a different thread @@ -276,6 +313,13 @@ internal class GradleKotlinCompilerWork @Inject constructor( } finally { bufferingMessageCollector.flush(messageCollector) threadPool.shutdown() + + reportExecutionResultIfNeeded { + TaskExecutionResult( + executionStrategy = IN_PROCESS_EXECUTION_STRATEGY, + icLogLines = nonIcBuildLog("$IN_PROCESS_EXECUTION_STRATEGY execution strategy does not support incremental compilation") + ) + } } } @@ -320,4 +364,14 @@ internal class GradleKotlinCompilerWork @Inject constructor( } else { ReportSeverity.DEBUG.code } + + private inline fun reportExecutionResultIfNeeded(fn: () -> TaskExecutionResult) { + if (reportExecutionResult) { + val result = fn() + TaskExecutionResults[taskPath] = result + } + } + + private fun nonIcBuildLog(reason: String): List = + listOf("Performing non-incremental build: $reason") } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithKotlincTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithKotlincTask.kt index 758278ec679..5dfd476f30d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithKotlincTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptWithKotlincTask.kt @@ -64,6 +64,7 @@ open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput) { @@ -54,6 +56,12 @@ internal class PropertiesProvider(private val project: Project) { val coroutines: Coroutines? get() = property("kotlin.coroutines")?.let { Coroutines.byCompilerArgument(it) } + val buildReportEnabled: Boolean + get() = booleanProperty("kotlin.build.report.enable") ?: false + + val buildReportDir: File? + get() = property("kotlin.build.report.dir")?.let { File(it) } + val incrementalJvm: Boolean? get() = booleanProperty("kotlin.incremental") diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/internal/state/TaskExecutionResults.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/internal/state/TaskExecutionResults.kt new file mode 100644 index 00000000000..c36ec341307 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/internal/state/TaskExecutionResults.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.plugin.internal.state + +import org.jetbrains.kotlin.gradle.report.TaskExecutionResult +import java.util.concurrent.ConcurrentHashMap + +internal object TaskExecutionResults { + private val results = ConcurrentHashMap() + + operator fun get(taskPath: String): TaskExecutionResult? = + results[taskPath] + + operator fun set(taskPath: String, result: TaskExecutionResult) { + results[taskPath] = result + } + + fun clear() { + results.clear() + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/report/KotlinBuildReporter.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/report/KotlinBuildReporter.kt new file mode 100644 index 00000000000..7c30d0d137d --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/report/KotlinBuildReporter.kt @@ -0,0 +1,176 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.report + +import org.gradle.BuildAdapter +import org.gradle.BuildResult +import org.gradle.api.Task +import org.gradle.api.execution.TaskExecutionListener +import org.gradle.api.invocation.Gradle +import org.gradle.api.logging.Logger +import org.gradle.api.tasks.TaskState +import org.jetbrains.kotlin.gradle.logging.kotlinDebug +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider +import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults +import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile +import java.io.File +import java.lang.StringBuilder +import java.text.SimpleDateFormat +import java.util.* +import kotlin.math.max + +internal fun configureBuildReporter(gradle: Gradle, log: Logger) { + val rootProject = gradle.rootProject + val properties = PropertiesProvider(rootProject) + + if (!properties.buildReportEnabled) return + + val perfLogDir = properties.buildReportDir + ?: rootProject.buildDir.resolve("reports/kotlin-build").apply { mkdirs() } + + if (perfLogDir.isFile) { + log.error("Kotlin build report cannot be created: '$perfLogDir' is a file") + return + } + + perfLogDir.mkdirs() + val ts = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Calendar.getInstance().time) + + val perfReportFile = perfLogDir.resolve("${gradle.rootProject.name}-build-$ts.txt") + val reporter = KotlinBuildReporter(perfReportFile) + gradle.addBuildListener(reporter) + + gradle.taskGraph.whenReady { graph -> + graph.allTasks.asSequence() + .filterIsInstance>() + .forEach { it.reportExecutionResult = true } + } + + log.kotlinDebug { "Configured Kotlin build reporter" } +} + +internal class KotlinBuildReporter(private val perfReportFile: File) : BuildAdapter(), TaskExecutionListener { + init { + val dir = perfReportFile.parentFile + check(dir.isDirectory) { "$dir does not exist or is a file" } + check(!perfReportFile.isFile) { "Build report log file $perfReportFile exists already" } + } + + private val taskStartNs = HashMap() + private val kotlinTaskTimeNs = HashMap() + private val tasksSb = StringBuilder() + + @Volatile + private var allTasksTimeNs: Long = 0L + + @Synchronized + override fun beforeExecute(task: Task) { + taskStartNs[task] = System.nanoTime() + } + + @Synchronized + override fun afterExecute(task: Task, state: TaskState) { + val startNs = taskStartNs[task] ?: return + + val endNs = System.nanoTime() + val timeNs = endNs - startNs + allTasksTimeNs += timeNs + + if (!task.javaClass.name.startsWith("org.jetbrains.kotlin")) { + return + } + + kotlinTaskTimeNs[task] = timeNs + tasksSb.appendln() + + val skipMessage = state.skipMessage + if (skipMessage != null) { + tasksSb.appendln("$task was skipped: $skipMessage") + } else { + tasksSb.appendln("$task finished in ${formatTime(timeNs)}") + } + + val path = task.path + val executionResult = TaskExecutionResults[path] + if (executionResult != null) { + tasksSb.appendln("Execution strategy: ${executionResult.executionStrategy}") + + executionResult.icLogLines?.let { lines -> + tasksSb.appendln("Compilation log for $task:") + lines.forEach { tasksSb.appendln(" $it") } + } + } + } + + @Synchronized + override fun buildFinished(result: BuildResult) { + val logger = result.gradle?.rootProject?.logger + try { + perfReportFile.writeText(taskOverview() + tasksSb.toString()) + logger?.lifecycle("Kotlin build report is written to ${perfReportFile.canonicalPath}") + } catch (e: Throwable) { + logger?.error("Could not write Kotlin build report to ${perfReportFile.canonicalPath}", e) + } + } + + private fun taskOverview(): String { + val sb = StringBuilder() + val kotlinTotalTimeNs = kotlinTaskTimeNs.values.sum() + val ktTaskPercent = (kotlinTotalTimeNs.toDouble() / allTasksTimeNs * 100).asString(1) + + sb.appendln("Total time for Kotlin tasks: ${formatTime(kotlinTotalTimeNs)} ($ktTaskPercent % of all tasks time)") + + val table = TextTable("Time", "% of Kotlin time", "Task") + kotlinTaskTimeNs.entries + .sortedByDescending { (_, timeNs) -> timeNs } + .forEach { (task, timeNs) -> + val percent = (timeNs.toDouble() / kotlinTotalTimeNs * 100).asString(1) + table.addRow(formatTime(timeNs), "$percent %", task.path) + } + table.printTo(sb) + sb.appendln() + return sb.toString() + } + + private fun formatTime(ns: Long): String { + val seconds = ns.toDouble() / 1_000_000_000 + return seconds.asString(2) + " s" + } + + private fun Double.asString(decPoints: Int): String = + String.format("%.${decPoints}f", this) + + private class TextTable(vararg columnNames: String) { + private val rows = ArrayList>() + private val columnsCount = columnNames.size + private val maxLengths = IntArray(columnsCount) { columnNames[it].length } + + init { + rows.add(columnNames.toList()) + } + + fun addRow(vararg row: String) { + check(row.size == columnsCount) { "Row size ${row.size} differs from columns count $columnsCount" } + rows.add(row.toList()) + + for ((i, col) in row.withIndex()) { + maxLengths[i] = max(maxLengths[i], col.length) + } + } + + fun printTo(sb: StringBuilder) { + for (row in rows) { + sb.appendln() + for ((i, col) in row.withIndex()) { + if (i > 0) sb.append("|") + + sb.append(col.padEnd(maxLengths[i], ' ')) + } + } + } + } +} + diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/report/TaskExecutionResult.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/report/TaskExecutionResult.kt new file mode 100644 index 00000000000..96fea0201c9 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/report/TaskExecutionResult.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.report + +internal class TaskExecutionResult( + val executionStrategy: String, + val icLogLines: List? +) \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt index aef4f586f31..696a18a600c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinCompileCommon.kt @@ -71,6 +71,7 @@ open class KotlinCompileCommon : AbstractKotlinCompile() : AbstractKo logger.kotlinDebug { "Set $this.incremental=$value" } } + @get:Internal + var reportExecutionResult: Boolean = false + @get:Internal internal val buildHistoryFile: File get() = File(taskBuildDirectory, "build-history.bin") @@ -436,6 +439,7 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl val environment = GradleCompilerEnvironment( computedCompilerClasspath, messageCollector, outputItemCollector, outputFiles = allOutputFiles(), + reportExecutionResult = reportExecutionResult, incrementalCompilationEnvironment = icEnv ) compilerRunner.runJvmCompilerAsync( @@ -593,6 +597,7 @@ open class Kotlin2JsCompile : AbstractKotlinCompile(), Ko val environment = GradleCompilerEnvironment( computedCompilerClasspath, messageCollector, outputItemCollector, outputFiles = allOutputFiles(), + reportExecutionResult = reportExecutionResult, incrementalCompilationEnvironment = icEnv ) compilerRunner.runJsCompilerAsync(sourceRoots.kotlinSourceFiles, commonSourceSet.toList(), args, environment)