Add IC metrics reporting
This commit is contained in:
committed by
Alexander Likhachev
parent
5bde6457b1
commit
36387d97ad
@@ -0,0 +1,24 @@
|
||||
description = "kotlin-gradle-statistics"
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(kotlinStdlib())
|
||||
|
||||
testImplementation(project(":kotlin-test:kotlin-test-junit"))
|
||||
testImplementation("junit:junit:4.12")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { projectDefault() }
|
||||
}
|
||||
|
||||
projectTest {
|
||||
workingDir = rootDir
|
||||
}
|
||||
|
||||
publish()
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.internal.build.metrics
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
class GradleBuildMetricsData : Serializable {
|
||||
val parentMetric: MutableMap<String, String?> = LinkedHashMap()
|
||||
val buildAttributeKind: MutableMap<String, String> = LinkedHashMap()
|
||||
val taskData: MutableMap<String, TaskData> = LinkedHashMap()
|
||||
|
||||
companion object {
|
||||
const val serialVersionUID = 0L
|
||||
}
|
||||
}
|
||||
|
||||
data class TaskData(
|
||||
val path: String,
|
||||
val typeFqName: String,
|
||||
val timeMetrics: Map<String, Long>,
|
||||
val buildAttributes: Map<String, Int>,
|
||||
val didWork: Boolean
|
||||
) : Serializable {
|
||||
|
||||
companion object {
|
||||
const val serialVersionUID = 0L
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,8 @@ dependencies {
|
||||
compileOnly(project(":kotlin-scripting-compiler"))
|
||||
compileOnly(project(":kotlin-gradle-statistics"))
|
||||
embedded(project(":kotlin-gradle-statistics"))
|
||||
compileOnly(project(":kotlin-gradle-build-metrics"))
|
||||
embedded(project(":kotlin-gradle-build-metrics"))
|
||||
|
||||
compile("com.google.code.gson:gson:${rootProject.extra["versions.jar.gson"]}")
|
||||
compile("de.undercouch:gradle-download-task:4.0.2")
|
||||
|
||||
+10
-2
@@ -1,5 +1,7 @@
|
||||
package org.jetbrains.kotlin.compilerRunner
|
||||
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildMetrics
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporterImpl
|
||||
import org.jetbrains.kotlin.daemon.common.CompilationResultCategory
|
||||
import org.jetbrains.kotlin.daemon.common.CompilationResults
|
||||
import org.jetbrains.kotlin.daemon.common.LoopbackNetworkInterface
|
||||
@@ -22,7 +24,10 @@ internal class GradleCompilationResults(
|
||||
LoopbackNetworkInterface.serverLoopbackSocketFactory
|
||||
) {
|
||||
|
||||
var icLogLines: List<String>? = null
|
||||
var icLogLines: List<String> = emptyList()
|
||||
private val buildMetricsReporter = BuildMetricsReporterImpl()
|
||||
val buildMetrics: BuildMetrics
|
||||
get() = buildMetricsReporter.getMetrics()
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
override fun add(compilationResultCategory: Int, value: Serializable) {
|
||||
@@ -42,7 +47,10 @@ internal class GradleCompilationResults(
|
||||
CompilationResultCategory.BUILD_REPORT_LINES.code,
|
||||
CompilationResultCategory.VERBOSE_BUILD_REPORT_LINES.code -> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
icLogLines = value as? List<String>
|
||||
(value as? List<String>)?.let { icLogLines = it }
|
||||
}
|
||||
CompilationResultCategory.BUILD_METRICS.code -> {
|
||||
buildMetricsReporter.addMetrics(value as? BuildMetrics)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.compilerRunner
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.jetbrains.kotlin.config.Services
|
||||
import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector
|
||||
import org.jetbrains.kotlin.gradle.report.BuildReportMode
|
||||
import org.jetbrains.kotlin.gradle.report.ReportingSettings
|
||||
import org.jetbrains.kotlin.gradle.tasks.findToolsJar
|
||||
import java.io.File
|
||||
|
||||
@@ -17,7 +17,7 @@ internal class GradleCompilerEnvironment(
|
||||
messageCollector: GradlePrintingMessageCollector,
|
||||
outputItemsCollector: OutputItemsCollector,
|
||||
val outputFiles: FileCollection,
|
||||
val buildReportMode: BuildReportMode?,
|
||||
val reportingSettings: ReportingSettings,
|
||||
val incrementalCompilationEnvironment: IncrementalCompilationEnvironment? = null,
|
||||
val kotlinScriptExtensions: Array<String> = emptyArray()
|
||||
) : CompilerEnvironment(Services.EMPTY, messageCollector, outputItemsCollector) {
|
||||
|
||||
+1
-1
@@ -160,7 +160,7 @@ internal open class GradleCompilerRunner(protected val taskProvider: GradleCompi
|
||||
incrementalModuleInfo = modulesInfo,
|
||||
outputFiles = environment.outputFiles.toList(),
|
||||
taskPath = pathProvider,
|
||||
buildReportMode = environment.buildReportMode,
|
||||
reportingSettings = environment.reportingSettings,
|
||||
kotlinScriptExtensions = environment.kotlinScriptExtensions,
|
||||
allWarningsAsErrors = compilerArgs.allWarningsAsErrors
|
||||
)
|
||||
|
||||
+63
-73
@@ -7,6 +7,8 @@ package org.jetbrains.kotlin.compilerRunner
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.logging.Logger
|
||||
import org.jetbrains.kotlin.build.ExecutionStrategy
|
||||
import org.jetbrains.kotlin.build.report.metrics.*
|
||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.config.Services
|
||||
@@ -15,6 +17,7 @@ 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.BuildReportMode
|
||||
import org.jetbrains.kotlin.gradle.report.ReportingSettings
|
||||
import org.jetbrains.kotlin.gradle.report.TaskExecutionResult
|
||||
import org.jetbrains.kotlin.gradle.tasks.clearLocalState
|
||||
import org.jetbrains.kotlin.gradle.tasks.throwGradleExceptionIfError
|
||||
@@ -59,7 +62,7 @@ internal class GradleKotlinCompilerWorkArguments(
|
||||
val incrementalModuleInfo: IncrementalModuleInfo?,
|
||||
val outputFiles: List<File>,
|
||||
val taskPath: String,
|
||||
val buildReportMode: BuildReportMode?,
|
||||
val reportingSettings: ReportingSettings,
|
||||
val kotlinScriptExtensions: Array<String>,
|
||||
val allWarningsAsErrors: Boolean
|
||||
) : Serializable {
|
||||
@@ -98,10 +101,12 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
||||
private val incrementalModuleInfo = config.incrementalModuleInfo
|
||||
private val outputFiles = config.outputFiles
|
||||
private val taskPath = config.taskPath
|
||||
private val buildReportMode = config.buildReportMode
|
||||
private val reportingSettings = config.reportingSettings
|
||||
private val kotlinScriptExtensions = config.kotlinScriptExtensions
|
||||
private val allWarningsAsErrors = config.allWarningsAsErrors
|
||||
private val buildDir = config.projectFiles.buildDir
|
||||
private val metrics = if (reportingSettings.reportMetrics) BuildMetricsReporterImpl() else DoNothingBuildMetricsReporter
|
||||
private var icLogLines: List<String> = emptyList()
|
||||
|
||||
private val log: KotlinLogger =
|
||||
TaskLoggers.get(taskPath)?.let { GradleKotlinLogger(it).apply { debug("Using '$taskPath' logger") } }
|
||||
@@ -120,13 +125,18 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
||||
get() = incrementalCompilationEnvironment != null
|
||||
|
||||
override fun run() {
|
||||
val messageCollector = GradlePrintingMessageCollector(log, allWarningsAsErrors)
|
||||
val exitCode = compileWithDaemonOrFallbackImpl(messageCollector)
|
||||
if (incrementalCompilationEnvironment?.disableMultiModuleIC == true) {
|
||||
incrementalCompilationEnvironment.multiModuleICSettings.buildHistoryFile.delete()
|
||||
}
|
||||
try {
|
||||
val messageCollector = GradlePrintingMessageCollector(log, allWarningsAsErrors)
|
||||
val exitCode = compileWithDaemonOrFallbackImpl(messageCollector)
|
||||
if (incrementalCompilationEnvironment?.disableMultiModuleIC == true) {
|
||||
incrementalCompilationEnvironment.multiModuleICSettings.buildHistoryFile.delete()
|
||||
}
|
||||
|
||||
throwGradleExceptionIfError(exitCode)
|
||||
throwGradleExceptionIfError(exitCode)
|
||||
} finally {
|
||||
val result = TaskExecutionResult(buildMetrics = metrics.getMetrics(), icLogLines = icLogLines)
|
||||
TaskExecutionResults[taskPath] = result
|
||||
}
|
||||
}
|
||||
|
||||
private fun compileWithDaemonOrFallbackImpl(messageCollector: MessageCollector): ExitCode {
|
||||
@@ -159,20 +169,21 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
||||
val isDebugEnabled = log.isDebugEnabled || System.getProperty("kotlin.daemon.debug.log")?.toBoolean() ?: true
|
||||
val daemonMessageCollector =
|
||||
if (isDebugEnabled) messageCollector else MessageCollector.NONE
|
||||
|
||||
val connection =
|
||||
try {
|
||||
GradleCompilerRunner.getDaemonConnectionImpl(
|
||||
clientIsAliveFlagFile,
|
||||
sessionFlagFile,
|
||||
compilerFullClasspath,
|
||||
daemonMessageCollector,
|
||||
isDebugEnabled = isDebugEnabled
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
log.error("Caught an exception trying to connect to Kotlin Daemon:")
|
||||
log.error(e.stackTraceAsString())
|
||||
null
|
||||
metrics.measure(BuildTime.CONNECT_TO_DAEMON) {
|
||||
try {
|
||||
GradleCompilerRunner.getDaemonConnectionImpl(
|
||||
clientIsAliveFlagFile,
|
||||
sessionFlagFile,
|
||||
compilerFullClasspath,
|
||||
daemonMessageCollector,
|
||||
isDebugEnabled = isDebugEnabled
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
log.error("Caught an exception trying to connect to Kotlin Daemon:")
|
||||
log.error(e.stackTraceAsString())
|
||||
null
|
||||
}
|
||||
}
|
||||
if (connection == null) {
|
||||
if (isIncremental) {
|
||||
@@ -215,7 +226,9 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
||||
// often source of the NoSuchObjectException and UnmarshalException, probably caused by the failed/crashed/exited daemon
|
||||
// TODO: implement a proper logic to avoid remote calls in such cases
|
||||
try {
|
||||
daemon.clearJarCache()
|
||||
metrics.measure(BuildTime.CLEAR_JAR_CACHE) {
|
||||
daemon.clearJarCache()
|
||||
}
|
||||
} catch (e: RemoteException) {
|
||||
log.warn("Unable to clear jar cache after compilation, maybe daemon is already down: $e")
|
||||
}
|
||||
@@ -229,6 +242,7 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
||||
targetPlatform: CompileService.TargetPlatform,
|
||||
bufferingMessageCollector: GradleBufferingMessageCollector
|
||||
): CompileService.CallResult<Int> {
|
||||
metrics.addAttribute(BuildAttribute.IC_IS_NOT_ENABLED)
|
||||
val compilationOptions = CompilationOptions(
|
||||
compilerMode = CompilerMode.NON_INCREMENTAL_COMPILER,
|
||||
targetPlatform = targetPlatform,
|
||||
@@ -238,15 +252,8 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
||||
kotlinScriptExtensions = kotlinScriptExtensions
|
||||
)
|
||||
val servicesFacade = GradleCompilerServicesFacadeImpl(log, bufferingMessageCollector)
|
||||
return try {
|
||||
return metrics.measure(BuildTime.NON_INCREMENTAL_COMPILATION_DAEMON) {
|
||||
daemon.compile(sessionId, compilerArgs, compilationOptions, servicesFacade, compilationResults = null)
|
||||
} finally {
|
||||
reportExecutionResultIfNeeded {
|
||||
TaskExecutionResult(
|
||||
executionStrategy = DAEMON_EXECUTION_STRATEGY,
|
||||
icLogLines = nonIcBuildLog("incremental compilation is not enabled for '$taskPath'")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,14 +265,7 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
||||
): CompileService.CallResult<Int> {
|
||||
val icEnv = incrementalCompilationEnvironment ?: error("incrementalCompilationEnvironment is null!")
|
||||
val knownChangedFiles = icEnv.changedFiles as? ChangedFiles.Known
|
||||
|
||||
val requestedCompilationResults = EnumSet.of(CompilationResultCategory.IC_COMPILE_ITERATION)
|
||||
when (buildReportMode) {
|
||||
BuildReportMode.SIMPLE -> CompilationResultCategory.BUILD_REPORT_LINES
|
||||
BuildReportMode.VERBOSE -> CompilationResultCategory.VERBOSE_BUILD_REPORT_LINES
|
||||
null -> null
|
||||
}?.let { requestedCompilationResults.add(it) }
|
||||
|
||||
val requestedCompilationResults = requestedCompilationResults()
|
||||
val compilationOptions = IncrementalCompilationOptions(
|
||||
areFileChangesKnown = knownChangedFiles != null,
|
||||
modifiedFiles = knownChangedFiles?.modified,
|
||||
@@ -286,36 +286,28 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
||||
log.info("Options for KOTLIN DAEMON: $compilationOptions")
|
||||
val servicesFacade = GradleIncrementalCompilerServicesFacadeImpl(log, bufferingMessageCollector)
|
||||
val compilationResults = GradleCompilationResults(log, projectRootFile)
|
||||
val result = daemon.compile(sessionId, compilerArgs, compilationOptions, servicesFacade, compilationResults)
|
||||
|
||||
reportExecutionResultIfNeeded {
|
||||
TaskExecutionResult(
|
||||
executionStrategy = DAEMON_EXECUTION_STRATEGY,
|
||||
icLogLines = compilationResults.icLogLines
|
||||
)
|
||||
return metrics.measure(BuildTime.RUN_COMPILER) {
|
||||
daemon.compile(sessionId, compilerArgs, compilationOptions, servicesFacade, compilationResults)
|
||||
}.also {
|
||||
metrics.addMetrics(compilationResults.buildMetrics)
|
||||
icLogLines = compilationResults.icLogLines
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun compileOutOfProcess(): ExitCode {
|
||||
clearLocalState(outputFiles, log, reason = "out-of-process execution strategy is non-incremental")
|
||||
metrics.addAttribute(BuildAttribute.OUT_OF_PROCESS_EXECUTION)
|
||||
clearLocalState(outputFiles, log, metrics, reason = "out-of-process execution strategy is non-incremental")
|
||||
|
||||
return try {
|
||||
return metrics.measure(BuildTime.NON_INCREMENTAL_COMPILATION_OUT_OF_PROCESS) {
|
||||
runToolInSeparateProcess(compilerArgs, compilerClassName, compilerFullClasspath, log, buildDir)
|
||||
} 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 {
|
||||
clearLocalState(outputFiles, log, reason = "in-process execution strategy is non-incremental")
|
||||
metrics.addAttribute(BuildAttribute.IN_PROCESS_EXECUTION)
|
||||
clearLocalState(outputFiles, log, metrics, reason = "in-process execution strategy is non-incremental")
|
||||
|
||||
metrics.startMeasure(BuildTime.NON_INCREMENTAL_COMPILATION_IN_PROCESS, System.nanoTime())
|
||||
// in-process compiler should always be run in a different thread
|
||||
// to avoid leaking thread locals from compiler (see KT-28037)
|
||||
val threadPool = Executors.newSingleThreadExecutor()
|
||||
@@ -329,12 +321,7 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
||||
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")
|
||||
)
|
||||
}
|
||||
metrics.endMeasure(BuildTime.NON_INCREMENTAL_COMPILATION_IN_PROCESS, System.nanoTime())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,6 +353,19 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
||||
return exitCode
|
||||
}
|
||||
|
||||
private fun requestedCompilationResults(): EnumSet<CompilationResultCategory> {
|
||||
val requestedCompilationResults = EnumSet.of(CompilationResultCategory.IC_COMPILE_ITERATION)
|
||||
when (reportingSettings.buildReportMode) {
|
||||
BuildReportMode.NONE -> null
|
||||
BuildReportMode.SIMPLE -> CompilationResultCategory.BUILD_REPORT_LINES
|
||||
BuildReportMode.VERBOSE -> CompilationResultCategory.VERBOSE_BUILD_REPORT_LINES
|
||||
}?.let { requestedCompilationResults.add(it) }
|
||||
if (reportingSettings.reportMetrics) {
|
||||
requestedCompilationResults.add(CompilationResultCategory.BUILD_METRICS)
|
||||
}
|
||||
return requestedCompilationResults
|
||||
}
|
||||
|
||||
private fun reportCategories(verbose: Boolean): Array<Int> =
|
||||
if (!verbose) {
|
||||
arrayOf(ReportCategory.COMPILER_MESSAGE.code)
|
||||
@@ -379,14 +379,4 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
||||
} else {
|
||||
ReportSeverity.DEBUG.code
|
||||
}
|
||||
|
||||
private inline fun reportExecutionResultIfNeeded(fn: () -> TaskExecutionResult) {
|
||||
if (buildReportMode != null) {
|
||||
val result = fn()
|
||||
TaskExecutionResults[taskPath] = result
|
||||
}
|
||||
}
|
||||
|
||||
private fun nonIcBuildLog(reason: String): List<String> =
|
||||
listOf("Performing non-incremental build: $reason")
|
||||
}
|
||||
+6
@@ -5,6 +5,8 @@ import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.internal.ConventionTask
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporter
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporterImpl
|
||||
import org.jetbrains.kotlin.gradle.internal.kapt.incremental.ClasspathSnapshot
|
||||
import org.jetbrains.kotlin.gradle.internal.kapt.incremental.KaptClasspathChanges
|
||||
import org.jetbrains.kotlin.gradle.internal.kapt.incremental.KaptIncrementalChanges
|
||||
@@ -137,6 +139,10 @@ abstract class KaptTask : ConventionTask(), TaskWithLocalState {
|
||||
protected val javaSourceRoots: Set<File>
|
||||
get() = unfilteredJavaSourceRoots.filterTo(HashSet(), ::isRootAllowed)
|
||||
|
||||
@get:Internal
|
||||
override val metrics: BuildMetricsReporter =
|
||||
BuildMetricsReporterImpl()
|
||||
|
||||
private fun isRootAllowed(file: File): Boolean =
|
||||
file.exists() &&
|
||||
!isAncestor(destinationDir, file) &&
|
||||
|
||||
+2
-2
@@ -84,7 +84,7 @@ open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput<K2JV
|
||||
private var processIncrementally = false
|
||||
|
||||
private val javaPackagePrefix by project.optionalProvider { kotlinCompileTask.javaPackagePrefix }
|
||||
private val buildReportMode by project.optionalProvider { kotlinCompileTask.buildReportMode }
|
||||
private val reportingSettings by project.provider { kotlinCompileTask.reportingSettings }
|
||||
|
||||
@TaskAction
|
||||
fun compile(inputs: IncrementalTaskInputs) {
|
||||
@@ -104,7 +104,7 @@ open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput<K2JV
|
||||
val outputItemCollector = OutputItemsCollectorImpl()
|
||||
val environment = GradleCompilerEnvironment(
|
||||
compilerClasspath, messageCollector, outputItemCollector,
|
||||
buildReportMode = buildReportMode,
|
||||
reportingSettings = reportingSettings,
|
||||
outputFiles = allOutputFiles()
|
||||
)
|
||||
if (environment.toolsJar == null && !isAtLeastJava9) {
|
||||
|
||||
+6
@@ -7,9 +7,15 @@ package org.jetbrains.kotlin.gradle.internal.tasks
|
||||
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporter
|
||||
import org.jetbrains.kotlin.gradle.utils.outputsCompatible
|
||||
|
||||
internal interface TaskWithLocalState : Task {
|
||||
fun localStateDirectories(): FileCollection
|
||||
|
||||
@get:Internal
|
||||
val metrics: BuildMetricsReporter
|
||||
}
|
||||
|
||||
internal fun TaskWithLocalState.allOutputFiles(): FileCollection =
|
||||
|
||||
+2
-3
@@ -13,10 +13,9 @@ import org.gradle.api.logging.Logging
|
||||
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
|
||||
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults
|
||||
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers
|
||||
import org.jetbrains.kotlin.gradle.report.configureBuildReporter
|
||||
import org.jetbrains.kotlin.gradle.report.configureReporting
|
||||
import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable
|
||||
|
||||
|
||||
//Support Gradle 6 and less. Move to
|
||||
internal class KotlinGradleBuildServices private constructor(
|
||||
private val gradle: Gradle
|
||||
@@ -99,7 +98,7 @@ internal class KotlinGradleBuildServices private constructor(
|
||||
TaskLoggers.clear()
|
||||
TaskExecutionResults.clear()
|
||||
|
||||
configureBuildReporter(gradle, log)
|
||||
configureReporting(gradle)
|
||||
}
|
||||
|
||||
override fun buildFinished(result: BuildResult) {
|
||||
|
||||
+6
@@ -57,9 +57,15 @@ internal class PropertiesProvider private constructor(private val project: Proje
|
||||
val coroutines: Coroutines?
|
||||
get() = property("kotlin.coroutines")?.let { Coroutines.byCompilerArgument(it) }
|
||||
|
||||
val singleBuildMetricsFile: File?
|
||||
get() = property("kotlin.internal.single.build.metrics.file")?.let { File(it) }
|
||||
|
||||
val buildReportEnabled: Boolean
|
||||
get() = booleanProperty("kotlin.build.report.enable") ?: false
|
||||
|
||||
val buildReportMetrics: Boolean
|
||||
get() = booleanProperty("kotlin.build.report.metrics") ?: false
|
||||
|
||||
val buildReportVerbose: Boolean
|
||||
get() = booleanProperty("kotlin.build.report.verbose") ?: false
|
||||
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.tasks.TaskState
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildMetrics
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildTime
|
||||
import org.jetbrains.kotlin.gradle.internal.tasks.TaskWithLocalState
|
||||
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults
|
||||
import org.jetbrains.kotlin.gradle.report.data.BuildExecutionData
|
||||
import org.jetbrains.kotlin.gradle.report.data.BuildExecutionDataProcessor
|
||||
import org.jetbrains.kotlin.gradle.report.data.TaskExecutionData
|
||||
import java.util.*
|
||||
|
||||
internal class BuildDataRecorder(
|
||||
private val gradle: Gradle,
|
||||
private val buildDataProcessors: Iterable<BuildExecutionDataProcessor>
|
||||
) : BuildAdapter(), TaskExecutionListener {
|
||||
private val taskRecords = HashMap<Task, TaskRecord>()
|
||||
|
||||
@Synchronized
|
||||
override fun beforeExecute(task: Task) {
|
||||
val startNs = System.nanoTime()
|
||||
taskRecords[task] = TaskRecord(task, startNs)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun afterExecute(task: Task, state: TaskState) {
|
||||
taskRecords[task]?.apply { processResult(state, TaskExecutionResults[task.path]) }
|
||||
}
|
||||
|
||||
override fun buildFinished(result: BuildResult) {
|
||||
val startParams = arrayListOf<String>()
|
||||
gradle.startParameter.apply {
|
||||
startParams.add("tasks = ${taskRequests.joinToString { it.args.toString() }}")
|
||||
startParams.add("excluded tasks = $excludedTaskNames")
|
||||
startParams.add("current dir = $currentDir")
|
||||
startParams.add("project properties args = $projectProperties")
|
||||
startParams.add("system properties args = $systemPropertiesArgs")
|
||||
}
|
||||
val buildData = BuildExecutionData(
|
||||
startParameters = startParams,
|
||||
failure = result.failure,
|
||||
taskExecutionData = taskRecords.values.sortedBy { it.startNs }
|
||||
)
|
||||
|
||||
buildDataProcessors.forEach { it.process(buildData) }
|
||||
}
|
||||
}
|
||||
|
||||
private class TaskRecord(
|
||||
override val task: Task,
|
||||
override val startNs: Long
|
||||
) : TaskExecutionData {
|
||||
private var myEndNs: Long = 0
|
||||
private lateinit var myTaskState: TaskState
|
||||
private var myIcLogLines: List<String> = emptyList()
|
||||
private val myBuildMetrics = BuildMetrics()
|
||||
|
||||
override val isKotlinTask by lazy {
|
||||
task.javaClass.name.startsWith("org.jetbrains.kotlin")
|
||||
}
|
||||
|
||||
override val endNs: Long
|
||||
get() = myEndNs
|
||||
|
||||
override val totalTimeNs: Long
|
||||
get() = endNs - startNs
|
||||
|
||||
override val resultState: TaskState
|
||||
get() = myTaskState
|
||||
|
||||
override val icLogLines: List<String>
|
||||
get() = myIcLogLines
|
||||
|
||||
override val buildMetrics: BuildMetrics
|
||||
get() = myBuildMetrics
|
||||
|
||||
fun processResult(state: TaskState, executionResult: TaskExecutionResult?) {
|
||||
myEndNs = System.nanoTime()
|
||||
myTaskState = state
|
||||
myBuildMetrics.buildTimes.add(BuildTime.GRADLE_TASK, totalTimeNs)
|
||||
|
||||
if (executionResult != null) {
|
||||
myBuildMetrics.addAll(executionResult.buildMetrics)
|
||||
myIcLogLines = executionResult.icLogLines
|
||||
}
|
||||
|
||||
if (task is TaskWithLocalState) {
|
||||
myBuildMetrics.addAll(task.metrics.getMetrics())
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-3
@@ -5,7 +5,14 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.report
|
||||
|
||||
enum class BuildReportMode {
|
||||
import java.io.Serializable
|
||||
|
||||
enum class BuildReportMode : Serializable {
|
||||
NONE,
|
||||
SIMPLE,
|
||||
VERBOSE
|
||||
}
|
||||
VERBOSE;
|
||||
|
||||
companion object {
|
||||
const val serialVersionUID: Long = 0
|
||||
}
|
||||
}
|
||||
|
||||
-120
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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 org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
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")
|
||||
|
||||
if (!isConfigurationCacheAvailable(gradle)) {
|
||||
val reporter = KotlinBuildReporter(gradle, perfReportFile)
|
||||
gradle.addBuildListener(reporter)
|
||||
}
|
||||
|
||||
val buildReportMode = if (properties.buildReportVerbose) BuildReportMode.VERBOSE else BuildReportMode.SIMPLE
|
||||
|
||||
gradle.taskGraph.whenReady { graph ->
|
||||
graph.allTasks.asSequence()
|
||||
.filterIsInstance<AbstractKotlinCompile<*>>()
|
||||
.forEach { it.buildReportMode = buildReportMode }
|
||||
}
|
||||
|
||||
log.kotlinDebug { "Configured Kotlin build reporter" }
|
||||
}
|
||||
|
||||
internal class KotlinBuildReporter(
|
||||
private val gradle: Gradle,
|
||||
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<Task, Long>()
|
||||
private val kotlinTaskTimeNs = HashMap<Task, Long>()
|
||||
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) {
|
||||
KotlinBuildReporterHandler().buildFinished(gradle, perfReportFile, kotlinTaskTimeNs.mapKeys{it.key.path}, allTasksTimeNs, result.failure)
|
||||
}
|
||||
}
|
||||
|
||||
-1
@@ -5,7 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.report
|
||||
|
||||
import com.intellij.util.text.DateFormatUtil.formatTime
|
||||
import org.gradle.api.invocation.Gradle
|
||||
import org.gradle.tooling.events.FinishEvent
|
||||
import org.gradle.tooling.events.OperationCompletionListener
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.api.logging.Logger
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildAttribute
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildTime
|
||||
import org.jetbrains.kotlin.gradle.internal.build.metrics.GradleBuildMetricsData
|
||||
import org.jetbrains.kotlin.gradle.internal.build.metrics.TaskData
|
||||
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
|
||||
import org.jetbrains.kotlin.gradle.report.data.BuildExecutionData
|
||||
import org.jetbrains.kotlin.gradle.report.data.BuildExecutionDataProcessor
|
||||
import java.io.File
|
||||
import java.io.ObjectOutputStream
|
||||
|
||||
internal class MetricsWriter(
|
||||
private val outputFile: File,
|
||||
private val log: Logger
|
||||
) : BuildExecutionDataProcessor {
|
||||
override fun process(build: BuildExecutionData) {
|
||||
if (build.failure != null) return
|
||||
|
||||
try {
|
||||
outputFile.parentFile?.apply { mkdirs() }
|
||||
|
||||
val buildMetricsData = GradleBuildMetricsData()
|
||||
for (metric in BuildTime.values()) {
|
||||
buildMetricsData.parentMetric[metric.name] = metric.parent?.name
|
||||
}
|
||||
for (attr in BuildAttribute.values()) {
|
||||
buildMetricsData.buildAttributeKind[attr.name] = attr.kind.name
|
||||
}
|
||||
|
||||
for (data in build.taskExecutionData) {
|
||||
val path = data.task.path
|
||||
val type = data.task::class.java.canonicalName
|
||||
val buildTimes = data.buildMetrics.buildTimes.asMap().mapKeys { (k, _) -> k.name }
|
||||
val buildAttributes = data.buildMetrics.buildAttributes.asMap().mapKeys { (k, _) -> k.name }
|
||||
buildMetricsData.taskData[path] =
|
||||
TaskData(
|
||||
path = path,
|
||||
typeFqName = type,
|
||||
timeMetrics = buildTimes,
|
||||
buildAttributes = buildAttributes,
|
||||
didWork = data.task.didWork
|
||||
)
|
||||
}
|
||||
|
||||
ObjectOutputStream(outputFile.outputStream().buffered()).use { out ->
|
||||
out.writeObject(buildMetricsData)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
log.kotlinDebug { "Could not write metrics to $outputFile: $e" }
|
||||
}
|
||||
}
|
||||
}
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.api.logging.Logger
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildAttributes
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildMetrics
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildTime
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildTimes
|
||||
import org.jetbrains.kotlin.gradle.report.data.BuildExecutionData
|
||||
import org.jetbrains.kotlin.gradle.report.data.BuildExecutionDataProcessor
|
||||
import org.jetbrains.kotlin.gradle.report.data.TaskExecutionData
|
||||
import org.jetbrains.kotlin.gradle.utils.Printer
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import kotlin.math.max
|
||||
|
||||
internal class PlainTextBuildReportWriter(
|
||||
private val outputFile: File,
|
||||
private val printMetrics: Boolean,
|
||||
private val log: Logger
|
||||
) : BuildExecutionDataProcessor {
|
||||
|
||||
private lateinit var p: Printer
|
||||
|
||||
override fun process(build: BuildExecutionData) {
|
||||
try {
|
||||
outputFile.bufferedWriter().use { writer ->
|
||||
p = Printer(writer)
|
||||
printBuildReport(build)
|
||||
}
|
||||
|
||||
log.lifecycle("Kotlin build report is written to ${outputFile.canonicalPath}")
|
||||
} catch (e: Exception) {
|
||||
log.error("Could not write Kotlin build report to ${outputFile.canonicalPath}", e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun printBuildReport(build: BuildExecutionData) {
|
||||
printBuildInfo(build)
|
||||
printMetrics(build.aggregatedMetrics)
|
||||
printTaskOverview(build)
|
||||
printTasksLog(build)
|
||||
}
|
||||
|
||||
private fun printBuildInfo(build: BuildExecutionData) {
|
||||
p.withIndent("Gradle start parameters:") {
|
||||
build.startParameters.forEach { p.println(it) }
|
||||
}
|
||||
|
||||
p.println()
|
||||
if (build.failure != null) {
|
||||
p.println("Build failed: ${build.failure}")
|
||||
}
|
||||
p.println()
|
||||
}
|
||||
|
||||
private fun printMetrics(buildMetrics: BuildMetrics) {
|
||||
if (!printMetrics) return
|
||||
|
||||
printBuildTimes(buildMetrics.buildTimes)
|
||||
printBuildAttributes(buildMetrics.buildAttributes)
|
||||
}
|
||||
|
||||
private fun printBuildTimes(buildTimes: BuildTimes) {
|
||||
val collectedBuildTimes = buildTimes.asMap()
|
||||
|
||||
if (collectedBuildTimes.isEmpty()) return
|
||||
|
||||
p.println("Time metrics:")
|
||||
p.withIndent {
|
||||
val visitedBuildTimes = HashSet<BuildTime>()
|
||||
fun printBuildTime(buildTime: BuildTime) {
|
||||
if (!visitedBuildTimes.add(buildTime)) return
|
||||
|
||||
val timeNs = collectedBuildTimes[buildTime] ?: return
|
||||
p.println("${buildTime.name}: ${formatTime(timeNs)}")
|
||||
p.withIndent {
|
||||
BuildTime.children[buildTime]?.forEach { printBuildTime(it) }
|
||||
}
|
||||
}
|
||||
|
||||
for (buildTime in BuildTime.values()) {
|
||||
if (buildTime.parent != null) continue
|
||||
|
||||
printBuildTime(buildTime)
|
||||
}
|
||||
}
|
||||
p.println()
|
||||
}
|
||||
|
||||
private fun printBuildAttributes(buildAttributes: BuildAttributes) {
|
||||
val allAttributes = buildAttributes.asMap()
|
||||
if (allAttributes.isEmpty()) return
|
||||
|
||||
p.withIndent("Build attributes:") {
|
||||
val attributesByKind = allAttributes.entries.groupBy { it.key.kind }.toSortedMap()
|
||||
for ((kind, attributesCounts) in attributesByKind) {
|
||||
printMap(p, kind.name, attributesCounts.map { (k, v) -> k.name to v }.toMap())
|
||||
}
|
||||
}
|
||||
p.println()
|
||||
}
|
||||
|
||||
private fun printTaskOverview(build: BuildExecutionData) {
|
||||
var allTasksTimeNs = 0L
|
||||
var kotlinTotalTimeNs = 0L
|
||||
val kotlinTasks = ArrayList<TaskExecutionData>()
|
||||
|
||||
for (task in build.taskExecutionData) {
|
||||
val taskTimeNs = task.totalTimeNs
|
||||
allTasksTimeNs += taskTimeNs
|
||||
|
||||
if (task.isKotlinTask) {
|
||||
kotlinTotalTimeNs += taskTimeNs
|
||||
kotlinTasks.add(task)
|
||||
}
|
||||
}
|
||||
|
||||
if (kotlinTasks.isEmpty()) {
|
||||
p.println("No Kotlin task was run")
|
||||
return
|
||||
}
|
||||
|
||||
val ktTaskPercent = (kotlinTotalTimeNs.toDouble() / allTasksTimeNs * 100).asString(1)
|
||||
p.println("Total time for Kotlin tasks: ${formatTime(kotlinTotalTimeNs)} ($ktTaskPercent % of all tasks time)")
|
||||
|
||||
val table = TextTable("Time", "% of Kotlin time", "Task")
|
||||
for (task in kotlinTasks.sortedByDescending { it.totalTimeNs }) {
|
||||
val timeNs = task.totalTimeNs
|
||||
val percent = (timeNs.toDouble() / kotlinTotalTimeNs * 100).asString(1)
|
||||
table.addRow(formatTime(timeNs), "$percent %", task.task.path)
|
||||
}
|
||||
table.printTo(p)
|
||||
p.println()
|
||||
}
|
||||
|
||||
private fun printTasksLog(build: BuildExecutionData) {
|
||||
for (task in build.taskExecutionData) {
|
||||
printTaskLog(task)
|
||||
p.println()
|
||||
}
|
||||
}
|
||||
|
||||
private fun printTaskLog(task: TaskExecutionData) {
|
||||
val skipMessage = task.resultState.skipMessage
|
||||
if (skipMessage != null) {
|
||||
p.println("$task was skipped: $skipMessage")
|
||||
} else {
|
||||
p.println("$task finished in ${formatTime(task.totalTimeNs)}")
|
||||
}
|
||||
|
||||
if (task.icLogLines.isNotEmpty()) {
|
||||
p.withIndent("Compilation log for $task:") {
|
||||
task.icLogLines.forEach { p.println(it) }
|
||||
}
|
||||
}
|
||||
|
||||
printMetrics(task.buildMetrics)
|
||||
}
|
||||
}
|
||||
|
||||
private fun printMap(p: Printer, name: String, mapping: Map<String, Int>) {
|
||||
if (mapping.isEmpty()) return
|
||||
|
||||
if (mapping.size == 1) {
|
||||
p.println("$name: ${mapping.keys.single()}")
|
||||
return
|
||||
}
|
||||
|
||||
p.withIndent("$name:") {
|
||||
val sortedEnumMap = mapping.toSortedMap()
|
||||
for ((k, v) in sortedEnumMap) {
|
||||
p.println("$k($v)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TextTable(vararg columnNames: String) {
|
||||
private val rows = ArrayList<List<String>>()
|
||||
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(p: Printer) {
|
||||
for (row in rows) {
|
||||
val rowStr = row.withIndex().joinToString("|") { (i, col) -> col.padEnd(maxLengths[i], ' ') }
|
||||
p.println(rowStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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 java.io.File
|
||||
import java.io.Serializable
|
||||
|
||||
data class ReportingSettings(
|
||||
val metricsOutputFile: File? = null,
|
||||
val buildReportDir: File? = null,
|
||||
val reportMetrics: Boolean = false,
|
||||
val includeMetricsInReport: Boolean = false,
|
||||
val buildReportMode: BuildReportMode = BuildReportMode.NONE
|
||||
) : Serializable {
|
||||
companion object {
|
||||
const val serialVersionUID: Long = 0
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -5,7 +5,9 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.report
|
||||
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildMetrics
|
||||
|
||||
internal class TaskExecutionResult(
|
||||
val executionStrategy: String,
|
||||
val icLogLines: List<String>?
|
||||
val buildMetrics: BuildMetrics,
|
||||
val icLogLines: List<String> = emptyList()
|
||||
)
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.api.Project
|
||||
import org.gradle.api.invocation.Gradle
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.report.data.BuildExecutionDataProcessor
|
||||
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
|
||||
import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
internal fun configureReporting(gradle: Gradle) {
|
||||
val buildDataProcessors = ArrayList<BuildExecutionDataProcessor>()
|
||||
|
||||
val rootProject = gradle.rootProject
|
||||
val reportingSettings = reportingSettings(rootProject)
|
||||
gradle.taskGraph.whenReady { graph ->
|
||||
graph.allTasks.asSequence()
|
||||
.filterIsInstance<AbstractKotlinCompile<*>>()
|
||||
.forEach { it.reportingSettings = reportingSettings }
|
||||
}
|
||||
|
||||
if (reportingSettings.buildReportMode != BuildReportMode.NONE && reportingSettings.buildReportDir != null) {
|
||||
configurePlainTextReportWriter(gradle, reportingSettings)?.let {
|
||||
buildDataProcessors.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
if (reportingSettings.metricsOutputFile != null) {
|
||||
buildDataProcessors.add(MetricsWriter(reportingSettings.metricsOutputFile.absoluteFile, rootProject.logger))
|
||||
}
|
||||
|
||||
if (buildDataProcessors.isNotEmpty() && !isConfigurationCacheAvailable(gradle)) {
|
||||
val listener = BuildDataRecorder(gradle, buildDataProcessors)
|
||||
gradle.addBuildListener(listener)
|
||||
}
|
||||
}
|
||||
|
||||
private fun reportingSettings(rootProject: Project): ReportingSettings {
|
||||
val properties = PropertiesProvider(rootProject)
|
||||
val buildReportMode =
|
||||
when {
|
||||
!properties.buildReportEnabled -> BuildReportMode.NONE
|
||||
properties.buildReportVerbose -> BuildReportMode.VERBOSE
|
||||
else -> BuildReportMode.SIMPLE
|
||||
}
|
||||
val metricsOutputFile = properties.singleBuildMetricsFile
|
||||
val buildReportDir = properties.buildReportDir ?: rootProject.buildDir.resolve("reports/kotlin-build")
|
||||
val includeMetricsInReport = properties.buildReportMetrics || buildReportMode == BuildReportMode.VERBOSE
|
||||
return ReportingSettings(
|
||||
metricsOutputFile = metricsOutputFile,
|
||||
buildReportDir = buildReportDir,
|
||||
reportMetrics = metricsOutputFile != null || includeMetricsInReport,
|
||||
includeMetricsInReport = includeMetricsInReport,
|
||||
buildReportMode = buildReportMode
|
||||
)
|
||||
}
|
||||
|
||||
private fun configurePlainTextReportWriter(
|
||||
gradle: Gradle,
|
||||
reportingSettings: ReportingSettings
|
||||
): BuildExecutionDataProcessor? {
|
||||
val log = gradle.rootProject.logger
|
||||
val reportDir = reportingSettings.buildReportDir!!.apply { mkdirs() }
|
||||
if (reportDir.isFile) {
|
||||
log.error("Kotlin build report cannot be created: '$reportDir' is a file")
|
||||
return null
|
||||
}
|
||||
reportDir.mkdirs()
|
||||
val ts = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Calendar.getInstance().time)
|
||||
val reportFile = reportDir.resolve("${gradle.rootProject.name}-build-$ts.txt")
|
||||
|
||||
return PlainTextBuildReportWriter(
|
||||
outputFile = reportFile,
|
||||
printMetrics = reportingSettings.includeMetricsInReport,
|
||||
log = log
|
||||
)
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.data
|
||||
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildMetrics
|
||||
|
||||
internal class BuildExecutionData(
|
||||
val startParameters: Collection<String>,
|
||||
val failure: Throwable?,
|
||||
val taskExecutionData: Collection<TaskExecutionData>
|
||||
) {
|
||||
val aggregatedMetrics by lazy {
|
||||
BuildMetrics().also { acc ->
|
||||
taskExecutionData.forEach { acc.addAll(it.buildMetrics) }
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.data
|
||||
|
||||
internal interface BuildExecutionDataProcessor {
|
||||
fun process(build: BuildExecutionData)
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.data
|
||||
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.tasks.TaskState
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildMetrics
|
||||
|
||||
internal interface TaskExecutionData {
|
||||
val task: Task
|
||||
val startNs: Long
|
||||
val endNs: Long
|
||||
val totalTimeNs: Long
|
||||
val resultState: TaskState
|
||||
val icLogLines: List<String>
|
||||
val buildMetrics: BuildMetrics
|
||||
val isKotlinTask: Boolean
|
||||
}
|
||||
|
||||
+1
-1
@@ -99,7 +99,7 @@ open class KotlinCompileCommon : AbstractKotlinCompile<K2MetadataCompilerArgumen
|
||||
val compilerRunner = compilerRunner
|
||||
val environment = GradleCompilerEnvironment(
|
||||
computedCompilerClasspath, messageCollector, outputItemCollector,
|
||||
buildReportMode = buildReportMode,
|
||||
reportingSettings = reportingSettings,
|
||||
outputFiles = allOutputFiles()
|
||||
)
|
||||
compilerRunner.runMetadataCompilerAsync(sourceRoots.kotlinSourceFiles, args, environment)
|
||||
|
||||
+14
-6
@@ -16,6 +16,10 @@ import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
|
||||
import org.gradle.workers.WorkerExecutor
|
||||
import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporter
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporterImpl
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildTime
|
||||
import org.jetbrains.kotlin.build.report.metrics.measure
|
||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
||||
import org.jetbrains.kotlin.cli.common.arguments.CommonToolArguments
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
|
||||
@@ -38,7 +42,7 @@ import org.jetbrains.kotlin.gradle.plugin.PLUGIN_CLASSPATH_CONFIGURATION_NAME
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.associateWithTransitiveClosure
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.ownModuleName
|
||||
import org.jetbrains.kotlin.gradle.report.BuildReportMode
|
||||
import org.jetbrains.kotlin.gradle.report.ReportingSettings
|
||||
import org.jetbrains.kotlin.gradle.utils.*
|
||||
import org.jetbrains.kotlin.gradle.utils.isParentOf
|
||||
import org.jetbrains.kotlin.gradle.utils.pathsAsStringRelativeTo
|
||||
@@ -92,6 +96,10 @@ abstract class AbstractKotlinCompileTool<T : CommonToolArguments>
|
||||
@get:Input
|
||||
internal var useFallbackCompilerSearch: Boolean = false
|
||||
|
||||
@get:Internal
|
||||
override val metrics: BuildMetricsReporter =
|
||||
BuildMetricsReporterImpl()
|
||||
|
||||
@get:Classpath
|
||||
@get:InputFiles
|
||||
internal val computedCompilerClasspath: List<File> by lazy {
|
||||
@@ -195,7 +203,7 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
|
||||
incremental
|
||||
|
||||
@get:Internal
|
||||
internal var buildReportMode: BuildReportMode? = null
|
||||
internal var reportingSettings = ReportingSettings()
|
||||
|
||||
@get:Internal
|
||||
internal val taskData: KotlinCompileTaskData = KotlinCompileTaskData.get(project, name)
|
||||
@@ -327,7 +335,7 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
|
||||
// To prevent this, we backup outputs before incremental build and restore when exception is thrown
|
||||
val outputsBackup: TaskOutputsBackup? =
|
||||
if (isIncrementalCompilationEnabled() && inputs.isIncremental)
|
||||
kotlinLogger.logTime("Backing up outputs for incremental build") {
|
||||
metrics.measure(BuildTime.BACKUP_OUTPUT) {
|
||||
TaskOutputsBackup(allOutputFiles())
|
||||
}
|
||||
else null
|
||||
@@ -342,7 +350,7 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
|
||||
executeImpl(inputs)
|
||||
} catch (t: Throwable) {
|
||||
if (outputsBackup != null) {
|
||||
kotlinLogger.logTime("Restoring previous outputs on error") {
|
||||
metrics.measure(BuildTime.RESTORE_OUTPUT_FROM_BACKUP) {
|
||||
outputsBackup.restoreOutputs()
|
||||
}
|
||||
}
|
||||
@@ -525,7 +533,7 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
|
||||
val environment = GradleCompilerEnvironment(
|
||||
computedCompilerClasspath, messageCollector, outputItemCollector,
|
||||
outputFiles = allOutputFiles(),
|
||||
buildReportMode = buildReportMode,
|
||||
reportingSettings = reportingSettings,
|
||||
incrementalCompilationEnvironment = icEnv,
|
||||
kotlinScriptExtensions = sourceFilesExtensions.toTypedArray()
|
||||
)
|
||||
@@ -752,7 +760,7 @@ open class Kotlin2JsCompile : AbstractKotlinCompile<K2JSCompilerArguments>(), Ko
|
||||
val environment = GradleCompilerEnvironment(
|
||||
computedCompilerClasspath, messageCollector, outputItemCollector,
|
||||
outputFiles = allOutputFiles(),
|
||||
buildReportMode = buildReportMode,
|
||||
reportingSettings = reportingSettings,
|
||||
incrementalCompilationEnvironment = icEnv
|
||||
)
|
||||
compilerRunner.runJsCompilerAsync(sourceRoots.kotlinSourceFiles, commonSourceSet.toList(), args, environment)
|
||||
|
||||
+23
-13
@@ -1,6 +1,9 @@
|
||||
package org.jetbrains.kotlin.gradle.tasks
|
||||
|
||||
import org.gradle.api.GradleException
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporter
|
||||
import org.jetbrains.kotlin.build.report.metrics.BuildTime
|
||||
import org.jetbrains.kotlin.build.report.metrics.measure
|
||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||
import org.jetbrains.kotlin.compilerRunner.KotlinLogger
|
||||
import org.jetbrains.kotlin.gradle.logging.GradleKotlinLogger
|
||||
@@ -22,26 +25,33 @@ fun throwGradleExceptionIfError(exitCode: ExitCode) {
|
||||
|
||||
internal fun TaskWithLocalState.clearLocalState(reason: String? = null) {
|
||||
val log = GradleKotlinLogger(logger)
|
||||
clearLocalState(allOutputFiles(), log, reason)
|
||||
clearLocalState(allOutputFiles(), log, metrics, reason)
|
||||
}
|
||||
|
||||
internal fun clearLocalState(outputFiles: Iterable<File>, log: KotlinLogger, reason: String? = null) {
|
||||
internal fun clearLocalState(
|
||||
outputFiles: Iterable<File>,
|
||||
log: KotlinLogger,
|
||||
metrics: BuildMetricsReporter,
|
||||
reason: String? = null
|
||||
) {
|
||||
log.kotlinDebug {
|
||||
val suffix = reason?.let { " ($it)" }.orEmpty()
|
||||
"Clearing output$suffix:"
|
||||
}
|
||||
|
||||
for (file in outputFiles) {
|
||||
if (!file.exists()) continue
|
||||
when {
|
||||
file.isDirectory -> {
|
||||
log.debug("Deleting output directory: $file")
|
||||
file.deleteRecursively()
|
||||
file.mkdirs()
|
||||
}
|
||||
file.isFile -> {
|
||||
log.debug("Deleting output file: $file")
|
||||
file.delete()
|
||||
metrics.measure(BuildTime.CLEAR_OUTPUT) {
|
||||
for (file in outputFiles) {
|
||||
if (!file.exists()) continue
|
||||
when {
|
||||
file.isDirectory -> {
|
||||
log.debug("Deleting output directory: $file")
|
||||
file.deleteRecursively()
|
||||
file.mkdirs()
|
||||
}
|
||||
file.isFile -> {
|
||||
log.debug("Deleting output file: $file")
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.gradle.utils
|
||||
|
||||
import java.io.IOException
|
||||
|
||||
private val LINE_SEPARATOR = System.getProperty("line.separator")
|
||||
|
||||
internal class Printer(
|
||||
private val out: Appendable,
|
||||
private val indentUnit: String = " ",
|
||||
private var indent: String = ""
|
||||
) {
|
||||
private fun append(s: String) {
|
||||
try {
|
||||
out.append(s)
|
||||
} catch (e: IOException) { // Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
fun println(vararg strings: String) {
|
||||
this.print(*strings)
|
||||
printLineSeparator()
|
||||
}
|
||||
|
||||
private fun printLineSeparator() {
|
||||
append(LINE_SEPARATOR)
|
||||
}
|
||||
|
||||
fun print(vararg strings: String) {
|
||||
if (strings.isNotEmpty()) {
|
||||
this.printIndent()
|
||||
}
|
||||
this.printWithNoIndent(*strings)
|
||||
}
|
||||
|
||||
private fun printIndent() {
|
||||
append(indent)
|
||||
}
|
||||
|
||||
private fun printWithNoIndent(vararg strings: String) {
|
||||
for (s in strings) {
|
||||
append(s)
|
||||
}
|
||||
}
|
||||
|
||||
fun pushIndent() {
|
||||
indent += indentUnit
|
||||
}
|
||||
|
||||
fun popIndent() {
|
||||
check(indent.length >= indentUnit.length) { "No indentation to pop" }
|
||||
indent = indent.substring(indentUnit.length)
|
||||
}
|
||||
|
||||
inline fun <T> withIndent(headLine: String? = null, fn: () -> T): T {
|
||||
if (headLine != null) {
|
||||
this.println(headLine)
|
||||
}
|
||||
pushIndent()
|
||||
|
||||
return try {
|
||||
fn()
|
||||
} finally {
|
||||
popIndent()
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -20,7 +20,7 @@ import kotlin.jvm.functions.Function0;
|
||||
import org.apache.maven.plugin.logging.Log;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.cli.common.ExitCode;
|
||||
import org.jetbrains.kotlin.incremental.ICReporterBase;
|
||||
import org.jetbrains.kotlin.build.report.ICReporterBase;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
|
||||
Reference in New Issue
Block a user