Add minimal statistic report for JPS build

Fix build stat for gradle 8

#KT-56438 Fixed
This commit is contained in:
nataliya.valtman
2023-02-20 12:17:46 +01:00
committed by Space Team
parent f3ca465647
commit e34dd043da
59 changed files with 1176 additions and 789 deletions
+2
View File
@@ -19,12 +19,14 @@ dependencies {
compileOnly(intellijCore()) compileOnly(intellijCore())
compileOnly(commonDependency("org.jetbrains.intellij.deps:asm-all")) compileOnly(commonDependency("org.jetbrains.intellij.deps:asm-all"))
compileOnly(commonDependency("org.jetbrains.intellij.deps:trove4j")) compileOnly(commonDependency("org.jetbrains.intellij.deps:trove4j"))
compileOnly(project(":kotlin-build-statistic"))
testCompileOnly(project(":compiler:cli-common")) testCompileOnly(project(":compiler:cli-common"))
testApi(projectTests(":compiler:tests-common")) testApi(projectTests(":compiler:tests-common"))
testApi(commonDependency("junit:junit")) testApi(commonDependency("junit:junit"))
testApi(protobufFull()) testApi(protobufFull())
testApi(kotlinStdlib()) testApi(kotlinStdlib())
testImplementation(project(":kotlin-build-statistic"))
testImplementation(commonDependency("org.jetbrains.kotlin:kotlin-reflect")) { isTransitive = false } testImplementation(commonDependency("org.jetbrains.kotlin:kotlin-reflect")) { isTransitive = false }
testImplementation("org.reflections:reflections:0.10.2") testImplementation("org.reflections:reflections:0.10.2")
} }
+3 -1
View File
@@ -143,6 +143,7 @@ val commonCompilerModules = arrayOf(
":analysis:project-structure", ":analysis:project-structure",
":analysis:kt-references", ":analysis:kt-references",
":kotlin-build-common", ":kotlin-build-common",
":kotlin-build-statistic",
).also { extra["commonCompilerModules"] = it } ).also { extra["commonCompilerModules"] = it }
val firCompilerCoreModules = arrayOf( val firCompilerCoreModules = arrayOf(
@@ -262,7 +263,8 @@ extra["kotlinJpsPluginMavenDependencies"] = listOf(
":kotlin-util-io", ":kotlin-util-io",
":kotlin-util-klib", ":kotlin-util-klib",
":kotlin-util-klib-metadata", ":kotlin-util-klib-metadata",
":native:kotlin-native-utils" ":native:kotlin-native-utils",
":kotlin-build-statistic",
) )
extra["kotlinJpsPluginMavenDependenciesNonTransitiveLibs"] = listOf( extra["kotlinJpsPluginMavenDependenciesNonTransitiveLibs"] = listOf(
+1
View File
@@ -81,6 +81,7 @@ val kotlinGradlePluginAndItsRequired = arrayOf(
":native:kotlin-klib-commonizer-api", ":native:kotlin-klib-commonizer-api",
":compiler:build-tools:kotlin-build-tools-api", ":compiler:build-tools:kotlin-build-tools-api",
":compiler:build-tools:kotlin-build-tools-impl", ":compiler:build-tools:kotlin-build-tools-impl",
":compiler:kotlin-build-statistic"
) )
fun Task.dependsOnKotlinGradlePluginInstall() { fun Task.dependsOnKotlinGradlePluginInstall() {
@@ -87,7 +87,7 @@ fun <PathProvider : Any> getLibraryFromHome(
fun MessageCollector.toLogger(): Logger = fun MessageCollector.toLogger(): Logger =
object : Logger { object : Logger {
override fun error(message: String) { override fun error(message: String, throwable: Throwable?) {
report(CompilerMessageSeverity.ERROR, message) report(CompilerMessageSeverity.ERROR, message)
} }
@@ -103,5 +103,9 @@ fun MessageCollector.toLogger(): Logger =
override fun log(message: String) { override fun log(message: String) {
report(CompilerMessageSeverity.LOGGING, message) report(CompilerMessageSeverity.LOGGING, message)
} }
override fun lifecycle(message: String) {
report(CompilerMessageSeverity.INFO, message)
}
} }
@@ -18,6 +18,7 @@ dependencies {
api(project(":compiler:backend.jvm.entrypoint")) api(project(":compiler:backend.jvm.entrypoint"))
api(project(":kotlin-build-common")) api(project(":kotlin-build-common"))
api(project(":daemon-common")) api(project(":daemon-common"))
api(project(":kotlin-build-statistic"))
compileOnly(intellijCore()) compileOnly(intellijCore())
testApi(commonDependency("junit:junit")) testApi(commonDependency("junit:junit"))
@@ -28,6 +29,7 @@ dependencies {
testApi(intellijCore()) testApi(intellijCore())
testApi(commonDependency("org.jetbrains.intellij.deps:log4j")) testApi(commonDependency("org.jetbrains.intellij.deps:log4j"))
testApi(commonDependency("org.jetbrains.intellij.deps:jdom")) testApi(commonDependency("org.jetbrains.intellij.deps:jdom"))
testApi(projectTests(":kotlin-build-statistic"))
testImplementation(commonDependency("com.google.code.gson:gson")) testImplementation(commonDependency("com.google.code.gson:gson"))
testRuntimeOnly(commonDependency("org.jetbrains.kotlin:kotlin-reflect")) { isTransitive = false } testRuntimeOnly(commonDependency("org.jetbrains.kotlin:kotlin-reflect")) { isTransitive = false }
@@ -106,8 +106,9 @@ val CompilerConfiguration.resolverLogger: Logger
null -> DummyLogger null -> DummyLogger
else -> object : Logger { else -> object : Logger {
override fun log(message: String) = messageLogger.report(IrMessageLogger.Severity.INFO, message, null) override fun log(message: String) = messageLogger.report(IrMessageLogger.Severity.INFO, message, null)
override fun error(message: String) = messageLogger.report(IrMessageLogger.Severity.ERROR, message, null) override fun error(message: String, throwable: Throwable?) = messageLogger.report(IrMessageLogger.Severity.ERROR, message, null)
override fun warning(message: String) = messageLogger.report(IrMessageLogger.Severity.WARNING, message, null) override fun warning(message: String) = messageLogger.report(IrMessageLogger.Severity.WARNING, message, null)
override fun lifecycle(message: String) = messageLogger.report(IrMessageLogger.Severity.INFO, message, null)
override fun fatal(message: String): Nothing { override fun fatal(message: String): Nothing {
messageLogger.report(IrMessageLogger.Severity.ERROR, message, null) messageLogger.report(IrMessageLogger.Severity.ERROR, message, null)
@@ -175,8 +175,9 @@ object GenerationUtils {
fun messageCollectorLogger(collector: MessageCollector) = object : Logger { fun messageCollectorLogger(collector: MessageCollector) = object : Logger {
override fun warning(message: String) = collector.report(CompilerMessageSeverity.STRONG_WARNING, message) override fun warning(message: String) = collector.report(CompilerMessageSeverity.STRONG_WARNING, message)
override fun error(message: String) = collector.report(CompilerMessageSeverity.ERROR, message) override fun error(message: String, throwable: Throwable?) = collector.report(CompilerMessageSeverity.ERROR, message)
override fun log(message: String) = collector.report(CompilerMessageSeverity.LOGGING, message) override fun log(message: String) = collector.report(CompilerMessageSeverity.LOGGING, message)
override fun lifecycle(message: String) = collector.report(CompilerMessageSeverity.INFO, message)
override fun fatal(message: String): Nothing { override fun fatal(message: String): Nothing {
collector.report(CompilerMessageSeverity.ERROR, message) collector.report(CompilerMessageSeverity.ERROR, message)
(collector as? GroupingMessageCollector)?.flush() (collector as? GroupingMessageCollector)?.flush()
@@ -4,9 +4,10 @@ import kotlin.system.exitProcess
interface Logger { interface Logger {
fun log(message: String) fun log(message: String)
fun error(message: String) fun error(message: String, throwable: Throwable? = null)
fun warning(message: String) fun warning(message: String)
fun fatal(message: String): Nothing fun fatal(message: String): Nothing
fun lifecycle(message: String)
} }
interface WithLogger { interface WithLogger {
@@ -15,10 +16,18 @@ interface WithLogger {
object DummyLogger : Logger { object DummyLogger : Logger {
override fun log(message: String) = println(message) override fun log(message: String) = println(message)
override fun error(message: String) = println("e: $message") override fun error(message: String, throwable: Throwable?) {
println("e: $message")
throwable?.also {
println("${it.message}:\n${it.stackTraceToString()} ")
}
}
override fun warning(message: String) = println("w: $message") override fun warning(message: String) = println("w: $message")
override fun fatal(message: String): Nothing { override fun fatal(message: String): Nothing {
println("e: $message") println("e: $message")
exitProcess(1) exitProcess(1)
} }
override fun lifecycle(message: String) = println("i: $message")
} }
@@ -16,9 +16,10 @@ fun resolveSingleFileKlib(
libraryFile: File, libraryFile: File,
logger: Logger = object : Logger { logger: Logger = object : Logger {
override fun log(message: String) {} override fun log(message: String) {}
override fun error(message: String) = kotlin.error("e: $message") override fun error(message: String, throwable: Throwable?) = kotlin.error("e: $message")
override fun warning(message: String) {} override fun warning(message: String) {}
override fun fatal(message: String) = kotlin.error("e: $message") override fun fatal(message: String) = kotlin.error("e: $message")
override fun lifecycle(message: String) {}
}, },
strategy: SingleFileKlibResolveStrategy = CompilerSingleFileKlibResolveStrategy strategy: SingleFileKlibResolveStrategy = CompilerSingleFileKlibResolveStrategy
): KotlinLibrary = strategy.resolve(libraryFile, logger) ): KotlinLibrary = strategy.resolve(libraryFile, logger)
+1
View File
@@ -20,6 +20,7 @@
<trust group="org.jetbrains.kotlin" name="kotlin-build-tools-enum-compat" version="1.9.[0-9](-.+)?" regex="true"/> <trust group="org.jetbrains.kotlin" name="kotlin-build-tools-enum-compat" version="1.9.[0-9](-.+)?" regex="true"/>
<trust group="org.jetbrains.kotlin" name="kotlin-build-tools-api" version="1.9.[0-9](-.+)?" regex="true"/> <trust group="org.jetbrains.kotlin" name="kotlin-build-tools-api" version="1.9.[0-9](-.+)?" regex="true"/>
<trust group="org.jetbrains.kotlin" name="kotlin-build-tools-impl" version="1.9.[0-9](-.+)?" regex="true"/> <trust group="org.jetbrains.kotlin" name="kotlin-build-tools-impl" version="1.9.[0-9](-.+)?" regex="true"/>
<trust group="org.jetbrains.kotlin" name="kotlin-build-statistic" version="1.9.0.*" regex="true"/>
<trust group="org.jetbrains.kotlin" name="kotlin-compiler-embeddable" version="1.9.[0-9](-.+)?" regex="true"/> <trust group="org.jetbrains.kotlin" name="kotlin-compiler-embeddable" version="1.9.[0-9](-.+)?" regex="true"/>
<trust group="org.jetbrains.kotlin" name="kotlin-compiler-runner" version="1.9.[0-9](-.+)?" regex="true"/> <trust group="org.jetbrains.kotlin" name="kotlin-compiler-runner" version="1.9.[0-9](-.+)?" regex="true"/>
<trust group="org.jetbrains.kotlin" name="kotlin-daemon-client" version="1.9.[0-9](-.+)?" regex="true"/> <trust group="org.jetbrains.kotlin" name="kotlin-daemon-client" version="1.9.[0-9](-.+)?" regex="true"/>
@@ -42,6 +42,7 @@ import org.jetbrains.kotlin.jps.KotlinJpsBundle
import org.jetbrains.kotlin.jps.incremental.JpsIncrementalCache import org.jetbrains.kotlin.jps.incremental.JpsIncrementalCache
import org.jetbrains.kotlin.jps.incremental.JpsLookupStorageManager import org.jetbrains.kotlin.jps.incremental.JpsLookupStorageManager
import org.jetbrains.kotlin.jps.model.kotlinKind import org.jetbrains.kotlin.jps.model.kotlinKind
import org.jetbrains.kotlin.jps.statistic.KotlinBuilderReportService
import org.jetbrains.kotlin.jps.targets.KotlinJvmModuleBuildTarget import org.jetbrains.kotlin.jps.targets.KotlinJvmModuleBuildTarget
import org.jetbrains.kotlin.jps.targets.KotlinModuleBuildTarget import org.jetbrains.kotlin.jps.targets.KotlinModuleBuildTarget
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
@@ -67,6 +68,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
System.getProperty("kotlin.jps.classesToLoadByParent")?.split(',')?.map { it.trim() } ?: emptyList() System.getProperty("kotlin.jps.classesToLoadByParent")?.split(',')?.map { it.trim() } ?: emptyList()
private val classPrefixesToLoadByParentFromRegistry = private val classPrefixesToLoadByParentFromRegistry =
System.getProperty("kotlin.jps.classPrefixesToLoadByParent")?.split(',')?.map { it.trim() } ?: emptyList() System.getProperty("kotlin.jps.classPrefixesToLoadByParent")?.split(',')?.map { it.trim() } ?: emptyList()
private val reportService = KotlinBuilderReportService()
val classesToLoadByParent: ClassCondition val classesToLoadByParent: ClassCondition
get() = ClassCondition { className -> get() = ClassCondition { className ->
@@ -100,6 +102,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
override fun buildStarted(context: CompileContext) { override fun buildStarted(context: CompileContext) {
logSettings(context) logSettings(context)
reportService.buildStarted(context)
} }
private fun logSettings(context: CompileContext) { private fun logSettings(context: CompileContext) {
@@ -160,6 +163,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
override fun buildFinished(context: CompileContext) { override fun buildFinished(context: CompileContext) {
ensureKotlinContextDisposed(context) ensureKotlinContextDisposed(context)
reportService.buildFinished(context)
} }
private fun ensureKotlinContextDisposed(context: CompileContext) { private fun ensureKotlinContextDisposed(context: CompileContext) {
@@ -0,0 +1,32 @@
/*
* Copyright 2010-2023 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.jps.statistic
import com.intellij.openapi.diagnostic.Logger
class JpsLoggerAdapter(private val log: Logger) : org.jetbrains.kotlin.util.Logger {
override fun log(message: String) {
log.info(message)
}
override fun warning(message: String) {
log.warn(message)
}
override fun fatal(message: String): Nothing {
log.error(message)
kotlin.error(message)
}
override fun error(message: String, throwable: Throwable?) {
log.error(message, throwable)
}
override fun lifecycle(message: String) {
log.info(message)
}
}
@@ -0,0 +1,131 @@
/*
* Copyright 2010-2023 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.jps.statistic
import com.intellij.openapi.diagnostic.Logger
import org.jetbrains.jps.incremental.CompileContext
import org.jetbrains.kotlin.build.report.FileReportSettings
import org.jetbrains.kotlin.build.report.HttpReportSettings
import org.jetbrains.kotlin.build.report.metrics.*
import org.jetbrains.kotlin.build.report.statistic.BuildDataType
import org.jetbrains.kotlin.build.report.statistic.CompileStatisticsData
import org.jetbrains.kotlin.build.report.statistic.HttpReportServiceImpl
import org.jetbrains.kotlin.build.report.statistic.file.FileReportService
import java.io.File
import java.util.*
import java.net.InetAddress
interface JpsBuilderMetricReporter : BuildMetricsReporter {
fun flush(context: CompileContext): CompileStatisticsData
}
//single thread execution
class JpsBuilderMetricReporterImpl(private val reporter: BuildMetricsReporterImpl) : JpsBuilderMetricReporter, BuildMetricsReporter by reporter {
companion object {
private val log = Logger.getInstance("#org.jetbrains.kotlin.jps.statistic.KotlinBuilderMetricImpl")
private val hostName: String? = try {
InetAddress.getLocalHost().hostName
} catch (_: Exception) {
//do nothing
null
}
}
private val uuid = UUID.randomUUID()
private val startTime = System.currentTimeMillis()
override fun flush(context: CompileContext/*, listener: BuildListener*/): CompileStatisticsData {
val buildMetrics = reporter.getMetrics()
return CompileStatisticsData(
projectName = context.projectDescriptor.project.name,
label = "JPS build", //TODO
taskName = "JPS build",
taskResult = "Unknown",//TODO
startTimeMs = startTime,
durationMs = System.currentTimeMillis() - startTime,
tags = emptySet(),
buildUuid = uuid.toString(),
changes = emptyList(), //TODO
kotlinVersion = "kotlin_version", //TODO
hostName = hostName,
finishTime = System.currentTimeMillis(),
buildTimesMetrics = buildMetrics.buildTimes.asMapMs(),
performanceMetrics = buildMetrics.buildPerformanceMetrics.asMap(),
compilerArguments = emptyList(), //TODO
nonIncrementalAttributes = emptySet(),
type = BuildDataType.JPS_DATA.name,
fromKotlinPlugin = true,
compiledSources = emptyList(),
skipMessage = null,
icLogLines = emptyList(),
gcTimeMetrics = buildMetrics.gcMetrics.asGcTimeMap(),
gcCountMetrics = buildMetrics.gcMetrics.asGcCountMap(),
kotlinLanguageVersion = null
)
}
}
// TODO test UserDataHolder in CompileContext to store CompileStatisticsData.Build or KotlinBuilderMetric
class KotlinBuilderReportService(
private val fileReportSettings: FileReportSettings?,
private val httpReportSettings: HttpReportSettings?
) {
constructor() : this(
initFileReportSettings(),
initHttpReportSettings(),
)
companion object {
private fun initFileReportSettings(): FileReportSettings? {
return System.getProperty("kotlin.build.report.file.output_dir")?.let { FileReportSettings(File(it)) }
}
private fun initHttpReportSettings(): HttpReportSettings? {
val httpReportUrl = System.getProperty("kotlin.build.report.http.url") ?: return null
val httpReportUser = System.getProperty("kotlin.build.report.http.user")
val httpReportPassword = System.getProperty("kotlin.build.report.http.password")
val includeGitBranch = System.getProperty("kotlin.build.report.http.git_branch", "false").toBoolean()
val verboseEnvironment = System.getProperty("kotlin.build.report.http.environment.verbose", "false").toBoolean()
return HttpReportSettings(httpReportUrl, httpReportUser, httpReportPassword, verboseEnvironment, includeGitBranch)
}
}
private val contextMetrics = HashMap<CompileContext, JpsBuilderMetricReporter>()
private val log = Logger.getInstance("#org.jetbrains.kotlin.jps.statistic.KotlinBuilderReportService")
private val loggerAdapter = JpsLoggerAdapter(log)
private val httpService = httpReportSettings?.let { HttpReportServiceImpl(it.url, it.user, it.password) }
fun buildStarted(context: CompileContext) {
if (contextMetrics[context] != null) {
log.error("Service already initialized for context")
}
contextMetrics[context] = JpsBuilderMetricReporterImpl(BuildMetricsReporterImpl())
}
fun buildFinished(context: CompileContext) {
val metrics = contextMetrics.remove(context)
if (metrics == null) {
log.error("Service hasn't initialized for context")
return
}
httpService?.sendData(metrics.flush(context), loggerAdapter)
fileReportSettings?.also { FileReportService(it.buildReportDir, true, loggerAdapter) }
}
fun addMetric(context: CompileContext, metric: BuildTime, value: Long) {
val metrics = contextMetrics[context]
if (metrics == null) {
log.error("Service hasn't initialized for context")
return
}
metrics.addTimeMetricNs(metric, value)
}
}
+47
View File
@@ -0,0 +1,47 @@
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion as GradleKotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
description = "Kotlin Build Report Common"
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
compileOnly(project(":core:util.runtime"))
compileOnly(project(":compiler:util"))
compileOnly(project(":kotlin-util-io"))
compileOnly(commonDependency("org.jetbrains.kotlin:kotlin-reflect")) { isTransitive = false }
compileOnly(kotlinStdlib())
compileOnly(intellijCore())
implementation(commonDependency("com.google.code.gson:gson"))
testApi(kotlinStdlib())
}
sourceSets {
"main" { projectDefault() }
"test" { projectDefault() }
}
publish()
runtimeJar()
sourcesJar()
javadocJar()
testsJar()
projectTest(parallel = true)
projectTest("testJUnit5", jUnitMode = JUnitMode.JUnit5, parallel = true) {
useJUnitPlatform()
}
// 1.9 level breaks Kotlin Gradle plugins via changes in enums (KT-48872)
// We limit api and LV until KGP will stop using Kotlin compiler directly (KT-56574)
tasks.withType<KotlinCompilationTask<*>>().configureEach {
compilerOptions.apiVersion.value(GradleKotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
compilerOptions.languageVersion.value(GradleKotlinVersion.KOTLIN_1_8).finalizeValueOnRead()
}
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2023 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2023 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2023 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2023 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2023 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -57,6 +57,7 @@ class BuildMetricsReporterImpl : BuildMetricsReporter, Serializable {
when (metric.type) { when (metric.type) {
ValueType.NANOSECONDS -> myBuildMetrics.add(metric, System.nanoTime()) ValueType.NANOSECONDS -> myBuildMetrics.add(metric, System.nanoTime())
ValueType.MILLISECONDS -> myBuildMetrics.add(metric, System.currentTimeMillis()) ValueType.MILLISECONDS -> myBuildMetrics.add(metric, System.currentTimeMillis())
ValueType.TIME -> myBuildMetrics.add(metric, System.currentTimeMillis())
else -> error("Unable to add time metric for '${metric.type}' type") else -> error("Unable to add time metric for '${metric.type}' type")
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2023 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -41,13 +41,14 @@ enum class BuildPerformanceMetric(val parent: BuildPerformanceMetric? = null, va
LOAD_CLASSPATH_ENTRY_SNAPSHOT_CACHE_HITS(parent = LOAD_CLASSPATH_SNAPSHOT_EXECUTION_COUNT, "Number of cache hits when loading classpath entry snapshots", type = ValueType.NUMBER), LOAD_CLASSPATH_ENTRY_SNAPSHOT_CACHE_HITS(parent = LOAD_CLASSPATH_SNAPSHOT_EXECUTION_COUNT, "Number of cache hits when loading classpath entry snapshots", type = ValueType.NUMBER),
LOAD_CLASSPATH_ENTRY_SNAPSHOT_CACHE_MISSES(parent = LOAD_CLASSPATH_SNAPSHOT_EXECUTION_COUNT, "Number of cache misses when loading classpath entry snapshots", type = ValueType.NUMBER), LOAD_CLASSPATH_ENTRY_SNAPSHOT_CACHE_MISSES(parent = LOAD_CLASSPATH_SNAPSHOT_EXECUTION_COUNT, "Number of cache misses when loading classpath entry snapshots", type = ValueType.NUMBER),
//exact time //time metrics
START_TASK_ACTION_EXECUTION(readableString = "Start time of task action", type = ValueType.MILLISECONDS), START_TASK_ACTION_EXECUTION(readableString = "Start time of task action", type = ValueType.TIME),
FINISH_KOTLIN_DAEMON_EXECUTION(readableString = "Finish time of kotlin daemon execution", type = ValueType.TIME),
CALL_KOTLIN_DAEMON(readableString = "Finish gradle part of task execution", type = ValueType.NANOSECONDS), CALL_KOTLIN_DAEMON(readableString = "Finish gradle part of task execution", type = ValueType.NANOSECONDS),
CALL_WORKER(readableString = "Worker submit time", type = ValueType.NANOSECONDS), CALL_WORKER(readableString = "Worker submit time", type = ValueType.NANOSECONDS),
START_WORKER_EXECUTION(readableString = "Start time of worker execution", type = ValueType.NANOSECONDS), START_WORKER_EXECUTION(readableString = "Start time of worker execution", type = ValueType.NANOSECONDS),
START_KOTLIN_DAEMON_EXECUTION(readableString = "Start time of kotlin daemon task execution", type = ValueType.NANOSECONDS), START_KOTLIN_DAEMON_EXECUTION(readableString = "Start time of kotlin daemon task execution", type = ValueType.NANOSECONDS),
FINISH_KOTLIN_DAEMON_EXECUTION(readableString = "Finish kotlin daemon execution", type = ValueType.MILLISECONDS),
; ;
companion object { companion object {
const val serialVersionUID = 0L const val serialVersionUID = 0L
@@ -63,4 +64,5 @@ enum class ValueType {
NUMBER, NUMBER,
NANOSECONDS, NANOSECONDS,
MILLISECONDS, MILLISECONDS,
TIME
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2023 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2023 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2023 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2023 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.build.report
import java.io.File
import java.io.Serializable
data class FileReportSettings(
val buildReportDir: File,
val includeMetricsInReport: Boolean = false,
) : Serializable {
companion object {
const val serialVersionUID: Long = 0
}
}
data class HttpReportSettings(
val url: String,
val password: String?,
val user: String?,
val verboseEnvironment: Boolean,
val includeGitBranchName: Boolean
) : Serializable {
companion object {
const val serialVersionUID: Long = 0
}
}
@@ -1,31 +1,31 @@
/* /*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2023 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. * 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.stat package org.jetbrains.kotlin.build.report.statistic
import org.jetbrains.kotlin.build.report.metrics.BuildAttribute import org.jetbrains.kotlin.build.report.metrics.BuildAttribute
import org.jetbrains.kotlin.build.report.metrics.BuildPerformanceMetric import org.jetbrains.kotlin.build.report.metrics.BuildPerformanceMetric
import org.jetbrains.kotlin.build.report.metrics.BuildTime import org.jetbrains.kotlin.build.report.metrics.BuildTime
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
//Sensitive data. This object is used directly for statistic via http //Sensitive data. This object is used directly for statistic via http
private val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").also { it.timeZone = TimeZone.getTimeZone("UTC")} private val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").also { it.timeZone = TimeZone.getTimeZone("UTC")}
data class CompileStatisticsData( data class CompileStatisticsData(
val version: Int = 2, val version: Int = 3,
val projectName: String?, val projectName: String?,
val label: String?, val label: String?,
val taskName: String?, val taskName: String,
val taskResult: String, val taskResult: String?,
val startTimeMs: Long,
val durationMs: Long, val durationMs: Long,
val tags: List<StatTag>, val tags: Set<StatTag>,
val changes: List<String>, val changes: List<String>,
val buildUuid: String = "Unset", val buildUuid: String = "Unset",
val kotlinVersion: String, val kotlinVersion: String,
val kotlinLanguageVersion: KotlinVersion?, val kotlinLanguageVersion: String?,
val hostName: String? = "Unset", val hostName: String? = "Unset",
val finishTime: Long, val finishTime: Long,
val timestamp: String = formatter.format(finishTime), val timestamp: String = formatter.format(finishTime),
@@ -36,7 +36,11 @@ data class CompileStatisticsData(
val performanceMetrics: Map<BuildPerformanceMetric, Long>, val performanceMetrics: Map<BuildPerformanceMetric, Long>,
val gcTimeMetrics: Map<String, Long>?, val gcTimeMetrics: Map<String, Long>?,
val gcCountMetrics: Map<String, Long>?, val gcCountMetrics: Map<String, Long>?,
val type: String = BuildDataType.TASK_DATA.name val type: String = BuildDataType.TASK_DATA.name,
val fromKotlinPlugin: Boolean?,
val compiledSources: List<String> = emptyList(),
val skipMessage: String?,
val icLogLines: List<String>,
) )
@@ -57,7 +61,8 @@ enum class StatTag(val readableString: String) {
enum class BuildDataType { enum class BuildDataType {
TASK_DATA, TASK_DATA,
BUILD_DATA BUILD_DATA,
JPS_DATA
} }
//Sensitive data. This object is used directly for statistic via http //Sensitive data. This object is used directly for statistic via http
@@ -80,7 +85,7 @@ data class BuildFinishStatisticsData(
val finishTime: Long, val finishTime: Long,
val timestamp: String = formatter.format(finishTime), val timestamp: String = formatter.format(finishTime),
val hostName: String? = "Unset", val hostName: String? = "Unset",
val tags: List<StatTag>, val tags: Set<StatTag>,
val gitBranch: String = "Unset" val gitBranch: String = "Unset"
) )
@@ -1,12 +1,12 @@
/* /*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2023 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. * 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 package org.jetbrains.kotlin.build.report.statistic
import com.google.gson.Gson import com.google.gson.Gson
import org.gradle.api.logging.Logger import org.jetbrains.kotlin.util.Logger
import java.io.IOException import java.io.IOException
import java.io.Serializable import java.io.Serializable
import java.net.HttpURLConnection import java.net.HttpURLConnection
@@ -23,7 +23,6 @@ class HttpReportServiceImpl(
private val password: String?, private val password: String?,
private val user: String?, private val user: String?,
) : HttpReportService, Serializable { ) : HttpReportService, Serializable {
constructor(httpSettings: HttpReportSettings) : this(httpSettings.url, httpSettings.password, httpSettings.user)
private var invalidUrl = false private var invalidUrl = false
private var requestPreviousFailed = false private var requestPreviousFailed = false
@@ -33,9 +32,7 @@ class HttpReportServiceImpl(
if (isResponseBad) { if (isResponseBad) {
val message = "Failed to send statistic to ${connection.url} with ${connection.responseCode}: ${connection.responseMessage}" val message = "Failed to send statistic to ${connection.url} with ${connection.responseCode}: ${connection.responseMessage}"
if (!requestPreviousFailed) { if (!requestPreviousFailed) {
log.warn(message) log.warning(message)
} else {
log.debug(message)
} }
requestPreviousFailed = true requestPreviousFailed = true
} }
@@ -49,7 +46,7 @@ class HttpReportServiceImpl(
val connection = try { val connection = try {
URL(url).openConnection() as HttpURLConnection URL(url).openConnection() as HttpURLConnection
} catch (e: IOException) { } catch (e: IOException) {
log.warn("Unable to open connection to ${url}: ${e.message}") log.warning("Unable to open connection to ${url}: ${e.message}")
invalidUrl = true invalidUrl = true
return return
} }
@@ -70,12 +67,12 @@ class HttpReportServiceImpl(
connection.connect() connection.connect()
checkResponseAndLog(connection, log) checkResponseAndLog(connection, log)
} catch (e: Exception) { } catch (e: Exception) {
log.debug("Unexpected exception happened ${e.message}: ${e.stackTrace}") log.warning("Unexpected exception happened ${e.message}: ${e.stackTrace}")
checkResponseAndLog(connection, log) checkResponseAndLog(connection, log)
} finally { } finally {
connection.disconnect() connection.disconnect()
} }
} }
log.debug("Report statistic by http takes $elapsedTime ms") log.log("Report statistic by http takes $elapsedTime ms")
} }
} }
@@ -0,0 +1,294 @@
/*
* Copyright 2010-2023 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.build.report.statistic.file
import org.jetbrains.kotlin.build.report.metrics.*
import org.jetbrains.kotlin.build.report.statistic.asString
import org.jetbrains.kotlin.build.report.statistic.formatSize
import org.jetbrains.kotlin.build.report.statistic.formatTime
import org.jetbrains.kotlin.build.report.statistic.CompileStatisticsData
import org.jetbrains.kotlin.build.report.statistic.GradleBuildStartParameters
import org.jetbrains.kotlin.util.Logger
import java.io.File
import java.io.Serializable
import java.text.SimpleDateFormat
import java.util.*
class FileReportService(
private val outputFile: File,
private val printMetrics: Boolean,
private val logger: Logger
) : Serializable {
companion object {
private val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").also { it.timeZone = TimeZone.getTimeZone("UTC")}
fun reportBuildStatInFile(
buildReportDir: File,
projectName: String,
includeMetricsInReport: Boolean,
buildData: List<CompileStatisticsData>,
startParameters: GradleBuildStartParameters,
failureMessages: List<String>,
logger: Logger
) {
val ts = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Calendar.getInstance().time)
val reportFile = buildReportDir.resolve("$projectName-build-$ts.txt")
FileReportService(
outputFile = reportFile,
printMetrics = includeMetricsInReport,
logger = logger
).process(buildData, startParameters, failureMessages)
}
}
private lateinit var p: Printer
fun process(
statisticsData: List<CompileStatisticsData>,
startParameters: GradleBuildStartParameters,
failureMessages: List<String>
) {
val buildReportPath = outputFile.toPath().toUri().toString()
try {
outputFile.parentFile.mkdirs()
if (!(outputFile.parentFile.exists() && outputFile.parentFile.isDirectory)) {
logger.error("Kotlin build report cannot be created: '$outputFile.parentFile' is a file or do not have permissions to create")
return
}
outputFile.bufferedWriter().use { writer ->
p = Printer(writer)
printBuildReport(statisticsData, startParameters, failureMessages)
}
logger.lifecycle("Kotlin build report is written to $buildReportPath")
} catch (e: Exception) {
logger.error("Could not write Kotlin build report to $buildReportPath", e)
}
}
private fun printBuildReport(
statisticsData: List<CompileStatisticsData>,
startParameters: GradleBuildStartParameters,
failureMessages: List<String>
) {
// NOTE: BuildExecutionData / BuildOperationRecord contains data for both tasks and transforms.
// Where possible, we still use the term "tasks" because saying "tasks/transforms" is a bit verbose and "build operations" may sound
// a bit unfamiliar.
// TODO: If it is confusing, consider renaming "tasks" to "build operations" in this class.
printBuildInfo(startParameters, failureMessages)
if (printMetrics) {
printMetrics(
statisticsData.map { it.buildTimesMetrics }.reduce { agg, value ->
(agg.keys + value.keys).associateWith { (agg[it] ?: 0) + (value[it] ?: 0) }
},
statisticsData.map { it.performanceMetrics }.reduce { agg, value ->
(agg.keys + value.keys).associateWith { (agg[it] ?: 0) + (value[it] ?: 0) }
},
statisticsData.map { it.nonIncrementalAttributes.asSequence() }.reduce { agg, value -> agg + value }.toList(),
aggregatedMetric = true
)
p.println()
}
printTaskOverview(statisticsData)
printTasksLog(statisticsData)
}
private fun printBuildInfo(startParameters: GradleBuildStartParameters, failureMessages: List<String>) {
p.withIndent("Gradle start parameters:") {
startParameters.let {
p.println("tasks = ${it.tasks}")
p.println("excluded tasks = ${it.excludedTasks}")
p.println("current dir = ${it.currentDir}")
p.println("project properties args = ${it.projectProperties}")
p.println("system properties args = ${it.systemProperties}")
}
}
p.println()
if (failureMessages.isNotEmpty()) {
p.println("Build failed: ${failureMessages}")
p.println()
}
}
private fun printMetrics(
buildTimesMetrics: Map<BuildTime, Long>,
performanceMetrics: Map<BuildPerformanceMetric, Long>,
nonIncrementalAttributes: Collection<BuildAttribute>,
gcTimeMetrics: Map<String, Long>? = emptyMap(),
gcCountMetrics: Map<String, Long>? = emptyMap(),
aggregatedMetric: Boolean = false
) {
printBuildTimes(buildTimesMetrics)
if (aggregatedMetric) p.println()
printBuildPerformanceMetrics(performanceMetrics)
if (aggregatedMetric) p.println()
printBuildAttributes(nonIncrementalAttributes)
//TODO: KT-57310 Implement build GC metric in
if (!aggregatedMetric) {
printGcMetrics(gcTimeMetrics, gcCountMetrics)
}
}
private fun printGcMetrics(
gcTimeMetrics: Map<String, Long>?,
gcCountMetrics: Map<String, Long>?
) {
val keys = HashSet<String>()
gcCountMetrics?.keys?.also { keys.addAll(it) }
gcTimeMetrics?.keys?.also { keys.addAll(it) }
if (keys.isEmpty()) return
p.withIndent("GC metrics:") {
for (key in keys) {
p.println("$key:")
p.withIndent {
gcCountMetrics?.get(key)?.also { p.println("GC count: ${it}") }
gcTimeMetrics?.get(key)?.also { p.println("GC time: ${formatTime(it)}") }
}
}
}
}
private fun printBuildTimes(buildTimes: Map<BuildTime, Long>) {
if (buildTimes.isEmpty()) return
p.println("Time metrics:")
p.withIndent {
val visitedBuildTimes = HashSet<BuildTime>()
fun printBuildTime(buildTime: BuildTime) {
if (!visitedBuildTimes.add(buildTime)) return
val timeMs = buildTimes[buildTime]
if (timeMs != null) {
p.println("${buildTime.readableString}: ${formatTime(timeMs)}")
p.withIndent {
BuildTime.children[buildTime]?.forEach { printBuildTime(it) }
}
} else {
//Skip formatting if parent metric does not set
BuildTime.children[buildTime]?.forEach { printBuildTime(it) }
}
}
for (buildTime in BuildTime.values()) {
if (buildTime.parent != null) continue
printBuildTime(buildTime)
}
}
}
private fun printBuildPerformanceMetrics(buildMetrics: Map<BuildPerformanceMetric, Long>) {
if (buildMetrics.isEmpty()) return
p.withIndent("Size metrics:") {
for (metric in BuildPerformanceMetric.values()) {
buildMetrics[metric]?.let { printSizeMetric(metric, it) }
}
}
}
private fun printSizeMetric(sizeMetric: BuildPerformanceMetric, value: Long) {
fun BuildPerformanceMetric.numberOfAncestors(): Int {
var count = 0
var parent: BuildPerformanceMetric? = parent
while (parent != null) {
count++
parent = parent.parent
}
return count
}
val indentLevel = sizeMetric.numberOfAncestors()
repeat(indentLevel) { p.pushIndent() }
when (sizeMetric.type) {
ValueType.BYTES -> p.println("${sizeMetric.readableString}: ${formatSize(value)}")
ValueType.NUMBER -> p.println("${sizeMetric.readableString}: $value")
ValueType.NANOSECONDS -> p.println("${sizeMetric.readableString}: $value")
ValueType.MILLISECONDS -> p.println("${sizeMetric.readableString}: ${formatTime(value)}")
ValueType.TIME -> p.println("${sizeMetric.readableString}: ${formatter.format(value)}")
}
repeat(indentLevel) { p.popIndent() }
}
private fun printBuildAttributes(buildAttributes: Collection<BuildAttribute>) {
if (buildAttributes.isEmpty()) return
val buildAttributesMap = buildAttributes.groupingBy { it }.eachCount()
p.withIndent("Build attributes:") {
val attributesByKind = buildAttributesMap.entries.groupBy { it.key.kind }.toSortedMap()
for ((kind, attributesCounts) in attributesByKind) {
printMap(p, kind.name, attributesCounts.associate { (k, v) -> k.readableString to v })
}
}
}
private fun printTaskOverview(statisticsData: Collection<CompileStatisticsData>) {
var allTasksTimeMs = 0L
var kotlinTotalTimeMs = 0L
val kotlinTasks = ArrayList<CompileStatisticsData>()
for (task in statisticsData) {
val taskTimeMs = task.durationMs
allTasksTimeMs += taskTimeMs
if (task.fromKotlinPlugin == true) {
kotlinTotalTimeMs += taskTimeMs
kotlinTasks.add(task)
}
}
if (kotlinTasks.isEmpty()) {
p.println("No Kotlin task was run")
return
}
val ktTaskPercent = (kotlinTotalTimeMs.toDouble() / allTasksTimeMs * 100).asString(1)
p.println("Total time for Kotlin tasks: ${formatTime(kotlinTotalTimeMs)} ($ktTaskPercent % of all tasks time)")
val table = TextTable("Time", "% of Kotlin time", "Task")
for (task in kotlinTasks.sortedWith(compareBy({ -it.durationMs }, { it.startTimeMs }))) {
val timeMs = task.durationMs
val percent = (timeMs.toDouble() / kotlinTotalTimeMs * 100).asString(1)
table.addRow(formatTime(timeMs), "$percent %", task.taskName)
}
table.printTo(p)
p.println()
}
private fun printTasksLog(statisticsData: List<CompileStatisticsData>) {
for (task in statisticsData.sortedWith(compareBy({ -it.durationMs }, { it.startTimeMs }))) {
printTaskLog(task)
p.println()
}
}
private fun printTaskLog(statisticsData: CompileStatisticsData) {
val skipMessage = statisticsData.skipMessage
if (skipMessage != null) {
p.println("Task '${statisticsData.taskName}' was skipped: $skipMessage")
} else {
p.println("Task '${statisticsData.taskName}' finished in ${formatTime(statisticsData.durationMs)}")
}
if (statisticsData.icLogLines.isNotEmpty()) {
p.withIndent("Compilation log for task '${statisticsData.taskName}':") {
statisticsData.icLogLines.forEach { p.println(it) }
}
}
if (printMetrics) {
printMetrics(statisticsData.buildTimesMetrics, statisticsData.performanceMetrics, statisticsData.nonIncrementalAttributes,
statisticsData.gcTimeMetrics, statisticsData.gcCountMetrics)
}
}
}
@@ -0,0 +1,51 @@
/*
* Copyright 2010-2023 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.build.report.statistic.file
import java.util.*
import kotlin.math.max
internal 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)")
}
}
}
internal 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)
}
}
}
@@ -1,19 +1,9 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * Copyright 2010-2023 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.
* 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
package org.jetbrains.kotlin.build.report.statistic.file
import java.io.IOException import java.io.IOException
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2023 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.build.report.statistic
internal fun formatTime(ms: Long): String {
val seconds = ms.toDouble() / 1_000
return seconds.asString(2) + " s"
}
private const val kbSize = 1024
private const val mbSize = kbSize * 1024
private const val gbSize = mbSize * 1024
public fun formatSize(sizeInBytes: Long): String = when {
sizeInBytes / gbSize >= 1 -> "${(sizeInBytes.toDouble() / gbSize).asString(1)} GB"
sizeInBytes / mbSize >= 1 -> "${(sizeInBytes.toDouble() / mbSize).asString(1)} MB"
sizeInBytes / kbSize >= 1 -> "${(sizeInBytes.toDouble() / kbSize).asString(1)} KB"
else -> "$sizeInBytes B"
}
internal fun Double.asString(decPoints: Int): String = "%,.${decPoints}f".format(this)
@@ -38,12 +38,13 @@ class KonanLibrariesResolveSupport(
object : Logger { object : Logger {
private val collector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) private val collector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
override fun warning(message: String)= collector.report(CompilerMessageSeverity.STRONG_WARNING, message) override fun warning(message: String)= collector.report(CompilerMessageSeverity.STRONG_WARNING, message)
override fun error(message: String) = collector.report(CompilerMessageSeverity.ERROR, message) override fun error(message: String, throwable: Throwable?) = collector.report(CompilerMessageSeverity.ERROR, message)
override fun log(message: String) = collector.report(CompilerMessageSeverity.LOGGING, message) override fun log(message: String) = collector.report(CompilerMessageSeverity.LOGGING, message)
override fun fatal(message: String): Nothing { override fun fatal(message: String): Nothing {
collector.report(CompilerMessageSeverity.ERROR, message) collector.report(CompilerMessageSeverity.ERROR, message)
throw KonanCompilationException() throw KonanCompilationException()
} }
override fun lifecycle(message: String) =collector.report(CompilerMessageSeverity.LOGGING, message)
} }
private val resolver = defaultResolver( private val resolver = defaultResolver(
@@ -88,9 +88,10 @@ fun error(text: String): Nothing {
object KlibToolLogger : Logger { object KlibToolLogger : Logger {
override fun warning(message: String) = org.jetbrains.kotlin.cli.klib.warn(message) override fun warning(message: String) = org.jetbrains.kotlin.cli.klib.warn(message)
override fun error(message: String) = org.jetbrains.kotlin.cli.klib.warn(message) override fun error(message: String, throwable: Throwable?) = org.jetbrains.kotlin.cli.klib.warn(message)
override fun fatal(message: String) = org.jetbrains.kotlin.cli.klib.error(message) override fun fatal(message: String) = org.jetbrains.kotlin.cli.klib.error(message)
override fun log(message: String) = println(message) override fun log(message: String) = println(message)
override fun lifecycle(message: String) = println(message)
} }
val defaultRepository = File(DependencyProcessor.localKonanDir.resolve("klib").absolutePath) val defaultRepository = File(DependencyProcessor.localKonanDir.resolve("klib").absolutePath)
@@ -74,6 +74,7 @@ dependencies {
testImplementation(project(":kotlin-compiler-embeddable")) testImplementation(project(":kotlin-compiler-embeddable"))
testImplementation(commonDependency("org.jetbrains.intellij.deps:jdom")) testImplementation(commonDependency("org.jetbrains.intellij.deps:jdom"))
testImplementation(project(":compiler:cli-common")) testImplementation(project(":compiler:cli-common"))
testImplementation(project(":kotlin-build-statistic"))
// testCompileOnly dependency on non-shaded artifacts is needed for IDE support // testCompileOnly dependency on non-shaded artifacts is needed for IDE support
// testRuntimeOnly on shaded artifact is needed for running tests with shaded compiler // testRuntimeOnly on shaded artifact is needed for running tests with shaded compiler
testCompileOnly(project(":kotlin-gradle-plugin-test-utils-embeddable")) testCompileOnly(project(":kotlin-gradle-plugin-test-utils-embeddable"))
@@ -17,9 +17,8 @@ import io.ktor.server.engine.*
import io.ktor.server.netty.* import io.ktor.server.netty.*
import io.ktor.util.* import io.ktor.util.*
import io.ktor.util.collections.* import io.ktor.util.collections.*
import org.gradle.api.logging.LogLevel
import org.gradle.util.GradleVersion import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.plugin.stat.* import org.jetbrains.kotlin.build.report.statistic.*
import org.jetbrains.kotlin.gradle.report.BuildReportType import org.jetbrains.kotlin.gradle.report.BuildReportType
import org.jetbrains.kotlin.gradle.testbase.* import org.jetbrains.kotlin.gradle.testbase.*
import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.DisplayName
@@ -52,7 +52,6 @@ dependencies {
commonCompileOnly(intellijCore()) commonCompileOnly(intellijCore())
commonCompileOnly(commonDependency("org.jetbrains.teamcity:serviceMessages")) commonCompileOnly(commonDependency("org.jetbrains.teamcity:serviceMessages"))
commonCompileOnly("com.gradle:gradle-enterprise-gradle-plugin:3.12.4") commonCompileOnly("com.gradle:gradle-enterprise-gradle-plugin:3.12.4")
commonCompileOnly(commonDependency("com.google.code.gson:gson"))
commonCompileOnly(commonDependency("com.google.guava:guava")) commonCompileOnly(commonDependency("com.google.guava:guava"))
commonCompileOnly("de.undercouch:gradle-download-task:4.1.1") commonCompileOnly("de.undercouch:gradle-download-task:4.1.1")
commonCompileOnly("com.github.gundy:semver4j:0.16.4:nodeps") { commonCompileOnly("com.github.gundy:semver4j:0.16.4:nodeps") {
@@ -67,6 +66,7 @@ dependencies {
commonImplementation(project(":native:kotlin-klib-commonizer-api")) commonImplementation(project(":native:kotlin-klib-commonizer-api"))
commonImplementation(project(":kotlin-project-model")) commonImplementation(project(":kotlin-project-model"))
commonImplementation(project(":compiler:build-tools:kotlin-build-tools-api")) commonImplementation(project(":compiler:build-tools:kotlin-build-tools-api"))
commonImplementation(project(":kotlin-build-statistic"))
commonRuntimeOnly(project(":kotlin-compiler-embeddable")) commonRuntimeOnly(project(":kotlin-compiler-embeddable"))
commonRuntimeOnly(project(":kotlin-annotation-processing-embeddable")) commonRuntimeOnly(project(":kotlin-annotation-processing-embeddable"))
@@ -111,6 +111,7 @@ dependencies {
testImplementation(commonDependency("junit:junit")) testImplementation(commonDependency("junit:junit"))
testImplementation(project(":kotlin-gradle-statistics")) testImplementation(project(":kotlin-gradle-statistics"))
testImplementation(project(":kotlin-tooling-metadata")) testImplementation(project(":kotlin-tooling-metadata"))
testImplementation(projectTests(":kotlin-build-statistic"))
} }
configurations.commonCompileClasspath.get().exclude("org.jetbrains.kotlinx", "kotlinx-coroutines-core") configurations.commonCompileClasspath.get().exclude("org.jetbrains.kotlinx", "kotlinx-coroutines-core")
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.logging.* import org.jetbrains.kotlin.gradle.logging.*
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers
import org.jetbrains.kotlin.build.report.statistic.StatTag
import org.jetbrains.kotlin.gradle.report.* import org.jetbrains.kotlin.gradle.report.*
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy import org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy
import org.jetbrains.kotlin.gradle.tasks.cleanOutputsAndLocalState import org.jetbrains.kotlin.gradle.tasks.cleanOutputsAndLocalState
@@ -26,6 +27,7 @@ import org.jetbrains.kotlin.incremental.IncrementalModuleInfo
import org.jetbrains.kotlin.incremental.util.ExceptionLocation import org.jetbrains.kotlin.incremental.util.ExceptionLocation
import org.jetbrains.kotlin.incremental.util.reportException import org.jetbrains.kotlin.incremental.util.reportException
import org.jetbrains.kotlin.util.removeSuffixIfPresent import org.jetbrains.kotlin.util.removeSuffixIfPresent
import org.jetbrains.kotlin.utils.addToStdlib.ifTrue
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.* import java.io.*
import java.net.URLClassLoader import java.net.URLClassLoader
@@ -34,6 +36,7 @@ import java.util.*
import java.util.concurrent.Callable import java.util.concurrent.Callable
import java.util.concurrent.Executors import java.util.concurrent.Executors
import javax.inject.Inject import javax.inject.Inject
import kotlin.collections.HashSet
internal class ProjectFilesForCompilation( internal class ProjectFilesForCompilation(
val projectRootFile: File, val projectRootFile: File,
@@ -41,10 +44,9 @@ internal class ProjectFilesForCompilation(
val sessionFlagFile: File, val sessionFlagFile: File,
val buildDir: File val buildDir: File
) : Serializable { ) : Serializable {
//TODO constructor(logger: Logger, projectDir:File, buildDir: File, projectName: String, projectCacheDirProvider: File, sessionDir: File) : this(
constructor(logger: Logger, projectDir:File, buildDir: File, prjectName: String, projectCacheDirProvider: File, sessionDir: File) : this(
projectRootFile = projectDir, projectRootFile = projectDir,
clientIsAliveFlagFile = GradleCompilerRunner.getOrCreateClientFlagFile(logger, prjectName), clientIsAliveFlagFile = GradleCompilerRunner.getOrCreateClientFlagFile(logger, projectName),
sessionFlagFile = GradleCompilerRunner.getOrCreateSessionFlagFile(logger, sessionDir, projectCacheDirProvider), sessionFlagFile = GradleCompilerRunner.getOrCreateSessionFlagFile(logger, sessionDir, projectCacheDirProvider),
buildDir = buildDir buildDir = buildDir
) )
@@ -114,7 +116,7 @@ internal class GradleKotlinCompilerWork @Inject constructor(
TaskLoggers.get(taskPath)?.let { GradleKotlinLogger(it).apply { debug("Using '$taskPath' logger") } } TaskLoggers.get(taskPath)?.let { GradleKotlinLogger(it).apply { debug("Using '$taskPath' logger") } }
?: run { ?: run {
val logger = LoggerFactory.getLogger("GradleKotlinCompilerWork") val logger = LoggerFactory.getLogger("GradleKotlinCompilerWork")
val kotlinLogger = if (logger is org.gradle.api.logging.Logger) { val kotlinLogger = if (logger is Logger) {
GradleKotlinLogger(logger) GradleKotlinLogger(logger)
} else SL4JKotlinLogger(logger) } else SL4JKotlinLogger(logger)
@@ -144,8 +146,7 @@ internal class GradleKotlinCompilerWork @Inject constructor(
kotlinLanguageVersion = kotlinLanguageVersion, kotlinLanguageVersion = kotlinLanguageVersion,
changedFiles = incrementalCompilationEnvironment?.changedFiles, changedFiles = incrementalCompilationEnvironment?.changedFiles,
compilerArguments = if (reportingSettings.includeCompilerArguments) compilerArgs else emptyArray(), compilerArguments = if (reportingSettings.includeCompilerArguments) compilerArgs else emptyArray(),
withAbiSnapshot = incrementalCompilationEnvironment?.withAbiSnapshot, tags = collectStatTags(),
withArtifactTransform = incrementalCompilationEnvironment?.classpathChanges is ClasspathChanges.ClasspathSnapshotEnabled
) )
metrics.endMeasure(BuildTime.RUN_COMPILATION_IN_WORKER) metrics.endMeasure(BuildTime.RUN_COMPILATION_IN_WORKER)
val result = TaskExecutionResult(buildMetrics = metrics.getMetrics(), icLogLines = icLogLines, taskInfo = taskInfo) val result = TaskExecutionResult(buildMetrics = metrics.getMetrics(), icLogLines = icLogLines, taskInfo = taskInfo)
@@ -153,6 +154,15 @@ internal class GradleKotlinCompilerWork @Inject constructor(
} }
} }
private fun collectStatTags(): Set<StatTag> {
val statTags = HashSet<StatTag>()
incrementalCompilationEnvironment?.withAbiSnapshot?.ifTrue { statTags.add(StatTag.ABI_SNAPSHOT) }
if (incrementalCompilationEnvironment?.classpathChanges is ClasspathChanges.ClasspathSnapshotEnabled) {
statTags.add(StatTag.ARTIFACT_TRANSFORM)
}
return statTags
}
private fun compileWithDaemonOrFallbackImpl(messageCollector: MessageCollector): Pair<ExitCode, KotlinCompilerExecutionStrategy> { private fun compileWithDaemonOrFallbackImpl(messageCollector: MessageCollector): Pair<ExitCode, KotlinCompilerExecutionStrategy> {
with(log) { with(log) {
kotlinDebug { "Kotlin compiler class: $compilerClassName" } kotlinDebug { "Kotlin compiler class: $compilerClassName" }
@@ -12,8 +12,6 @@ import org.gradle.api.provider.Provider
import org.gradle.api.services.BuildService import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters import org.gradle.api.services.BuildServiceParameters
import org.jetbrains.kotlin.gradle.logging.kotlinDebug 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.utils.projectCacheDir import org.jetbrains.kotlin.gradle.utils.projectCacheDir
import java.io.File import java.io.File
@@ -65,9 +63,6 @@ internal abstract class KotlinGradleBuildServices : BuildService<KotlinGradleBui
override fun close() { override fun close() {
buildHandler.buildFinished(parameters.projectCacheDir) buildHandler.buildFinished(parameters.projectCacheDir)
log.kotlinDebug(DISPOSE_MESSAGE) log.kotlinDebug(DISPOSE_MESSAGE)
TaskLoggers.clear()
TaskExecutionResults.clear()
} }
companion object { companion object {
@@ -98,13 +98,7 @@ abstract class DefaultKotlinBasePlugin : KotlinBasePlugin {
kotlinGradleBuildServices.detectKotlinPluginLoadedInMultipleProjects(project, kotlinPluginVersion) kotlinGradleBuildServices.detectKotlinPluginLoadedInMultipleProjects(project, kotlinPluginVersion)
} }
BuildMetricsService.registerIfAbsent(project)?.also { buildMetricsService -> BuildMetricsService.registerIfAbsent(project)
val buildEventsListenerRegistryHolder = BuildEventsListenerRegistryHolder.getInstance(project)
buildEventsListenerRegistryHolder.listenerRegistry.onTaskCompletion(buildMetricsService)
BuildReportsService.registerIfAbsent(project, buildMetricsService).also {
buildEventsListenerRegistryHolder.listenerRegistry.onTaskCompletion(it)
}
}
} }
private fun addKotlinCompilerConfiguration(project: Project) { private fun addKotlinCompilerConfiguration(project: Project) {
@@ -7,14 +7,16 @@ package org.jetbrains.kotlin.gradle.report
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.Task import org.gradle.api.Task
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.invocation.Gradle
import org.gradle.api.logging.Logging import org.gradle.api.logging.Logging
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.gradle.api.services.BuildService import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters import org.gradle.api.services.BuildServiceParameters
import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Internal
import org.gradle.tooling.events.FailureResult import org.gradle.tooling.events.FailureResult
import org.gradle.tooling.events.FinishEvent
import org.gradle.tooling.events.OperationCompletionListener import org.gradle.tooling.events.OperationCompletionListener
import org.gradle.tooling.events.task.TaskExecutionResult import org.gradle.tooling.events.task.TaskExecutionResult
import org.gradle.tooling.events.task.TaskFailureResult import org.gradle.tooling.events.task.TaskFailureResult
@@ -24,13 +26,22 @@ import org.jetbrains.kotlin.build.report.metrics.BuildMetrics
import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporter import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporter
import org.jetbrains.kotlin.build.report.metrics.BuildPerformanceMetric import org.jetbrains.kotlin.build.report.metrics.BuildPerformanceMetric
import org.jetbrains.kotlin.build.report.metrics.BuildTime import org.jetbrains.kotlin.build.report.metrics.BuildTime
import org.jetbrains.kotlin.build.report.statistic.HttpReportService
import org.jetbrains.kotlin.build.report.statistic.HttpReportServiceImpl
import org.jetbrains.kotlin.gradle.plugin.BuildEventsListenerRegistryHolder
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults
import org.jetbrains.kotlin.build.report.statistic.GradleBuildStartParameters
import org.jetbrains.kotlin.build.report.statistic.StatTag
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
import org.jetbrains.kotlin.gradle.report.BuildReportsService.Companion.getStartParameters
import org.jetbrains.kotlin.gradle.report.data.BuildOperationRecord import org.jetbrains.kotlin.gradle.report.data.BuildOperationRecord
import org.jetbrains.kotlin.gradle.tasks.withType import org.jetbrains.kotlin.gradle.tasks.withType
import org.jetbrains.kotlin.gradle.utils.SingleActionPerProject import org.jetbrains.kotlin.gradle.utils.SingleActionPerProject
import org.jetbrains.kotlin.statistics.metrics.NumericalMetrics import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable
import org.jetbrains.kotlin.incremental.ChangedFiles
import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics
import org.jetbrains.kotlin.statistics.metrics.NumericalMetrics
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.ConcurrentLinkedQueue
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
@@ -40,13 +51,27 @@ internal interface UsesBuildMetricsService : Task {
val buildMetricsService: Property<BuildMetricsService?> val buildMetricsService: Property<BuildMetricsService?>
} }
abstract class BuildMetricsService : BuildService<BuildServiceParameters.None>, OperationCompletionListener { abstract class BuildMetricsService : BuildService<BuildMetricsService.Parameters>, AutoCloseable {
//Part of BuildReportService
interface Parameters : BuildServiceParameters {
val startParameters: Property<GradleBuildStartParameters>
val reportingSettings: Property<ReportingSettings>
val httpService: Property<HttpReportService>
val projectDir: DirectoryProperty
val label: Property<String?>
val projectName: Property<String>
val kotlinVersion: Property<String>
val buildConfigurationTags: ListProperty<StatTag>
}
private val log = Logging.getLogger(this.javaClass) private val log = Logging.getLogger(this.javaClass)
private val buildReportService = BuildReportsService()
// Tasks and transforms' records // Tasks and transforms' records
internal val buildOperationRecords = ConcurrentLinkedQueue<BuildOperationRecord>() private val buildOperationRecords = ConcurrentLinkedQueue<BuildOperationRecord>()
internal val failureMessages = ConcurrentLinkedQueue<String>() private val failureMessages = ConcurrentLinkedQueue<String>()
// Info for tasks only // Info for tasks only
private val taskPathToMetricsReporter = ConcurrentHashMap<String, BuildMetricsReporter>() private val taskPathToMetricsReporter = ConcurrentHashMap<String, BuildMetricsReporter>()
@@ -76,69 +101,87 @@ abstract class BuildMetricsService : BuildService<BuildServiceParameters.None>,
failureMessage?.let { failureMessages.add(it) } failureMessage?.let { failureMessages.add(it) }
} }
override fun onFinish(event: FinishEvent?) { private fun updateBuildOperationRecord(event: TaskFinishEvent): TaskRecord {
if (event is TaskFinishEvent) { val result = event.result
val result = event.result val taskPath = event.descriptor.taskPath
val taskPath = event.descriptor.taskPath val totalTimeMs = result.endTime - result.startTime
val totalTimeMs = result.endTime - result.startTime
val buildMetrics = BuildMetrics() val buildMetrics = BuildMetrics()
buildMetrics.buildTimes.addTimeMs(BuildTime.GRADLE_TASK, totalTimeMs) buildMetrics.buildTimes.addTimeMs(BuildTime.GRADLE_TASK, totalTimeMs)
taskPathToMetricsReporter[taskPath]?.let { taskPathToMetricsReporter[taskPath]?.let {
buildMetrics.addAll(it.getMetrics()) buildMetrics.addAll(it.getMetrics())
} }
val taskExecutionResult = TaskExecutionResults[taskPath] val taskExecutionResult = TaskExecutionResults[taskPath]
taskExecutionResult?.buildMetrics?.also { taskExecutionResult?.buildMetrics?.also {
buildMetrics.addAll(it) buildMetrics.addAll(it)
KotlinBuildStatsService.applyIfInitialised { collector -> KotlinBuildStatsService.applyIfInitialised { collector ->
collector.report(NumericalMetrics.COMPILATION_DURATION, totalTimeMs) collector.report(NumericalMetrics.COMPILATION_DURATION, totalTimeMs)
collector.report(BooleanMetrics.KOTLIN_COMPILATION_FAILED, event.result is FailureResult) collector.report(BooleanMetrics.KOTLIN_COMPILATION_FAILED, event.result is FailureResult)
val metricsMap = buildMetrics.buildPerformanceMetrics.asMap() val metricsMap = buildMetrics.buildPerformanceMetrics.asMap()
val linesOfCode = metricsMap[BuildPerformanceMetric.ANALYZED_LINES_NUMBER] val linesOfCode = metricsMap[BuildPerformanceMetric.ANALYZED_LINES_NUMBER]
if (linesOfCode != null && linesOfCode > 0 && totalTimeMs > 0) { if (linesOfCode != null && linesOfCode > 0 && totalTimeMs > 0) {
collector.report(NumericalMetrics.COMPILED_LINES_OF_CODE, linesOfCode) collector.report(NumericalMetrics.COMPILED_LINES_OF_CODE, linesOfCode)
collector.report(NumericalMetrics.COMPILATION_LINES_PER_SECOND, linesOfCode * 1000 / totalTimeMs, null, linesOfCode) collector.report(NumericalMetrics.COMPILATION_LINES_PER_SECOND, linesOfCode * 1000 / totalTimeMs, null, linesOfCode)
metricsMap[BuildPerformanceMetric.ANALYSIS_LPS]?.also { metricsMap[BuildPerformanceMetric.ANALYSIS_LPS]?.also { value ->
collector.report(NumericalMetrics.ANALYSIS_LINES_PER_SECOND, it, null, linesOfCode) collector.report(NumericalMetrics.ANALYSIS_LINES_PER_SECOND, value, null, linesOfCode)
} }
metricsMap[BuildPerformanceMetric.CODE_GENERATION_LPS]?.also { value -> metricsMap[BuildPerformanceMetric.CODE_GENERATION_LPS]?.also { value ->
collector.report(NumericalMetrics.CODE_GENERATION_LINES_PER_SECOND, value, null, linesOfCode) collector.report(NumericalMetrics.CODE_GENERATION_LINES_PER_SECOND, value, null, linesOfCode)
}
} }
collector.report(NumericalMetrics.COMPILATIONS_COUNT, 1)
collector.report(
NumericalMetrics.INCREMENTAL_COMPILATIONS_COUNT,
if (taskExecutionResult.buildMetrics.buildAttributes.asMap().isEmpty()) 1 else 0
)
} }
} collector.report(NumericalMetrics.COMPILATIONS_COUNT, 1)
collector.report(
buildOperationRecords.add( NumericalMetrics.INCREMENTAL_COMPILATIONS_COUNT,
TaskRecord( if (taskExecutionResult.buildMetrics.buildAttributes.asMap().isEmpty()) 1 else 0
path = taskPath,
classFqName = taskPathToTaskClass[taskPath] ?: "unknown",
startTimeMs = result.startTime,
totalTimeMs = totalTimeMs,
buildMetrics = buildMetrics,
didWork = result is TaskExecutionResult,
skipMessage = (result as? TaskSkippedResult)?.skipMessage,
icLogLines = taskExecutionResult?.icLogLines ?: emptyList(),
kotlinLanguageVersion = taskExecutionResult?.taskInfo?.kotlinLanguageVersion
) )
)
if (result is TaskFailureResult) {
failureMessages.addAll(result.failures.map { it.message })
} }
} }
val buildOperation = TaskRecord(
path = taskPath,
classFqName = taskPathToTaskClass[taskPath] ?: "unknown",
startTimeMs = result.startTime,
totalTimeMs = totalTimeMs,
buildMetrics = buildMetrics,
didWork = result is TaskExecutionResult,
skipMessage = (result as? TaskSkippedResult)?.skipMessage,
icLogLines = taskExecutionResult?.icLogLines ?: emptyList(),
changedFiles = taskExecutionResult?.taskInfo?.changedFiles,
compilerArguments = taskExecutionResult?.taskInfo?.compilerArguments ?: emptyArray(),
kotlinLanguageVersion = taskExecutionResult?.taskInfo?.kotlinLanguageVersion,
statTags = taskExecutionResult?.taskInfo?.tags ?: emptySet()
)
buildOperationRecords.add(buildOperation)
if (result is TaskFailureResult) {
failureMessages.addAll(result.failures.map { it.message })
}
return buildOperation
}
override fun close() {
buildReportService.close(buildOperationRecords, failureMessages.toList(), parameters.toBuildReportParameters())
} }
companion object { companion object {
private val serviceClass = BuildMetricsService::class.java private val serviceClass = BuildMetricsService::class.java
private val serviceName = "${serviceClass.name}_${serviceClass.classLoader.hashCode()}" private val serviceName = "${serviceClass.name}_${serviceClass.classLoader.hashCode()}"
private fun registerIfAbsentImpl(project: Project): Provider<BuildMetricsService>? { private fun Parameters.toBuildReportParameters() = BuildReportParameters(
startParameters = startParameters.get(),
reportingSettings = reportingSettings.get(),
httpService = httpService.orNull,
projectDir = projectDir.asFile.get(),
label = label.orNull,
projectName = projectName.get(),
kotlinVersion = kotlinVersion.get(),
additionalTags = HashSet(buildConfigurationTags.get())
)
private fun registerIfAbsentImpl(
project: Project,
): Provider<BuildMetricsService>? {
// Return early if the service was already registered to avoid the overhead of reading the reporting settings below // Return early if the service was already registered to avoid the overhead of reading the reporting settings below
project.gradle.sharedServices.registrations.findByName(serviceName)?.let { project.gradle.sharedServices.registrations.findByName(serviceName)?.let {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@@ -151,7 +194,59 @@ abstract class BuildMetricsService : BuildService<BuildServiceParameters.None>,
return null return null
} }
return project.gradle.sharedServices.registerIfAbsent(serviceName, serviceClass) {}!! val kotlinVersion = project.getKotlinPluginVersion()
return project.gradle.sharedServices.registerIfAbsent(serviceName, serviceClass) {
it.parameters.label.set(reportingSettings.buildReportLabel)
it.parameters.projectName.set(project.rootProject.name)
it.parameters.kotlinVersion.set(kotlinVersion)
it.parameters.startParameters.set(getStartParameters(project))
it.parameters.reportingSettings.set(reportingSettings)
reportingSettings.httpReportSettings?.let { httpSettings ->
it.parameters.httpService.set(
HttpReportServiceImpl(
httpSettings.url,
httpSettings.user,
httpSettings.password
)
)
}
it.parameters.projectDir.set(project.rootProject.layout.projectDirectory)
//init gradle tags for build scan and http reports
it.parameters.buildConfigurationTags.value(setupTags(project.gradle))
}.also {
subscribeForTaskEvents(project, it)
}
}
private fun subscribeForTaskEvents(project: Project, buildMetricService: Provider<BuildMetricsService>) {
// BuildScanExtension cant be parameter nor BuildService's field
val buildScanExtension = project.rootProject.extensions.findByName("buildScan")
val buildScan = buildScanExtension?.let { BuildScanExtensionHolder(it) }
val buildReportService = buildMetricService.map { it.buildReportService }
BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(project.provider {
OperationCompletionListener { event ->
if (event is TaskFinishEvent) {
val buildOperation = buildMetricService.get().updateBuildOperationRecord(event)
val buildParameters = buildMetricService.get().parameters.toBuildReportParameters()
buildReportService.get().onFinish(event, buildOperation, buildParameters, buildScan)
}
}
})
val buildScanReportSettings = buildMetricService.get().parameters.reportingSettings.orNull?.buildScanReportSettings
if (buildScanReportSettings != null) {
buildScan?.also {
buildReportService.get().initBuildScanTags(
it, buildMetricService.get().parameters.label.orNull
)
it.buildScan.buildFinished {
buildReportService.get().addCollectedTags(buildScan)
}
}
}
} }
fun registerIfAbsent(project: Project) = registerIfAbsentImpl(project)?.also { serviceProvider -> fun registerIfAbsent(project: Project) = registerIfAbsentImpl(project)?.also { serviceProvider ->
@@ -161,6 +256,17 @@ abstract class BuildMetricsService : BuildService<BuildServiceParameters.None>,
} }
} }
} }
private fun setupTags(gradle: Gradle): ArrayList<StatTag> {
val additionalTags = ArrayList<StatTag>()
if (isConfigurationCacheAvailable(gradle)) {
additionalTags.add(StatTag.CONFIGURATION_CACHE)
}
if (gradle.startParameter.isBuildCacheEnabled) {
additionalTags.add(StatTag.BUILD_CACHE)
}
return additionalTags
}
} }
} }
@@ -174,7 +280,10 @@ internal class TaskRecord(
override val didWork: Boolean, override val didWork: Boolean,
override val skipMessage: String?, override val skipMessage: String?,
override val icLogLines: List<String>, override val icLogLines: List<String>,
val kotlinLanguageVersion: KotlinVersion? val kotlinLanguageVersion: KotlinVersion?,
val changedFiles: ChangedFiles? = null,
val compilerArguments: Array<String> = emptyArray(),
val statTags: Set<StatTag> = emptySet(),
) : BuildOperationRecord { ) : BuildOperationRecord {
override val isFromKotlinPlugin: Boolean = classFqName.startsWith("org.jetbrains.kotlin") override val isFromKotlinPlugin: Boolean = classFqName.startsWith("org.jetbrains.kotlin")
} }
@@ -6,33 +6,21 @@
package org.jetbrains.kotlin.gradle.report package org.jetbrains.kotlin.gradle.report
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.invocation.Gradle
import org.gradle.api.logging.Logging import org.gradle.api.logging.Logging
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
import org.gradle.api.tasks.Internal
import org.gradle.tooling.events.FinishEvent
import org.gradle.tooling.events.OperationCompletionListener
import org.gradle.tooling.events.task.TaskFinishEvent import org.gradle.tooling.events.task.TaskFinishEvent
import org.jetbrains.kotlin.build.report.metrics.ValueType import org.jetbrains.kotlin.build.report.metrics.ValueType
import org.jetbrains.kotlin.gradle.plugin.BuildEventsListenerRegistryHolder import org.jetbrains.kotlin.build.report.statistic.HttpReportService
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion import org.jetbrains.kotlin.build.report.statistic.file.FileReportService
import org.jetbrains.kotlin.gradle.plugin.stat.BuildFinishStatisticsData import org.jetbrains.kotlin.build.report.statistic.formatSize
import org.jetbrains.kotlin.gradle.plugin.stat.CompileStatisticsData import org.jetbrains.kotlin.build.report.statistic.BuildFinishStatisticsData
import org.jetbrains.kotlin.gradle.plugin.stat.GradleBuildStartParameters import org.jetbrains.kotlin.build.report.statistic.CompileStatisticsData
import org.jetbrains.kotlin.gradle.plugin.stat.StatTag import org.jetbrains.kotlin.build.report.statistic.GradleBuildStartParameters
import org.jetbrains.kotlin.build.report.statistic.StatTag
import org.jetbrains.kotlin.gradle.report.data.BuildExecutionData import org.jetbrains.kotlin.gradle.report.data.BuildExecutionData
import org.jetbrains.kotlin.gradle.tasks.withType import org.jetbrains.kotlin.gradle.report.data.BuildOperationRecord
import org.jetbrains.kotlin.gradle.utils.SingleActionPerProject
import org.jetbrains.kotlin.gradle.utils.formatSize
import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable
import org.jetbrains.kotlin.utils.addToStdlib.measureTimeMillisWithResult import org.jetbrains.kotlin.utils.addToStdlib.measureTimeMillisWithResult
import java.io.File import java.io.File
import java.lang.management.ManagementFactory
import java.net.InetAddress import java.net.InetAddress
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@@ -41,18 +29,16 @@ import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
internal interface UsesBuildReportsService : Task { //Because of https://github.com/gradle/gradle/issues/23359 gradle issue, two build services interaction is not reliable at the end of the build
@get:Internal //Switch back to proper BuildService as soon as this issue is fixed
val buildReportsService: Property<BuildReportsService?> class BuildReportsService {
}
abstract class BuildReportsService : BuildService<BuildReportsService.Parameters>, AutoCloseable, OperationCompletionListener {
private val log = Logging.getLogger(this.javaClass) private val log = Logging.getLogger(this.javaClass)
private val loggerAdapter = GradleLoggerAdapter(log)
private val startTime = System.nanoTime() private val startTime = System.nanoTime()
private val buildUuid = UUID.randomUUID().toString() private val buildUuid = UUID.randomUUID().toString()
private var executorService: ExecutorService = Executors.newSingleThreadExecutor() private val executorService: ExecutorService = Executors.newSingleThreadExecutor()
private val tags = LinkedHashSet<StatTag>() private val tags = LinkedHashSet<StatTag>()
private var customValues = 0 // doesn't need to be thread-safe private var customValues = 0 // doesn't need to be thread-safe
@@ -61,33 +47,46 @@ abstract class BuildReportsService : BuildService<BuildReportsService.Parameters
log.info("Build report service is registered. Unique build id: $buildUuid") log.info("Build report service is registered. Unique build id: $buildUuid")
} }
interface Parameters : BuildServiceParameters { fun close(
val startParameters: Property<GradleBuildStartParameters> buildOperationRecords: Collection<BuildOperationRecord>,
val reportingSettings: Property<ReportingSettings> failureMessages: List<String>,
var buildMetricsService: Provider<BuildMetricsService> parameters: BuildReportParameters
val httpService: Property<HttpReportService> ) {
val projectDir: DirectoryProperty
val label: Property<String?>
val projectName: Property<String>
val kotlinVersion: Property<String>
val additionalTags: ListProperty<StatTag>
}
override fun close() {
val buildData = BuildExecutionData( val buildData = BuildExecutionData(
startParameters = parameters.startParameters.get(), startParameters = parameters.startParameters,
failureMessages = parameters.buildMetricsService.orNull?.failureMessages?.toList() ?: emptyList(), failureMessages = failureMessages,
buildOperationRecord = parameters.buildMetricsService.orNull?.buildOperationRecords?.sortedBy { it.startTimeMs } ?: emptyList() buildOperationRecord = buildOperationRecords.sortedBy { it.startTimeMs }
) )
val reportingSettings = parameters.reportingSettings.get() val reportingSettings = parameters.reportingSettings
reportingSettings.httpReportSettings?.also { reportingSettings.httpReportSettings?.also {
executorService.submit { reportBuildFinish() } // executorService.submit { reportBuildFinish(parameters) }
} }
reportingSettings.fileReportSettings?.also { reportingSettings.fileReportSettings?.also {
reportBuildStatInFile(it, buildData) FileReportService.reportBuildStatInFile(
it.buildReportDir,
parameters.projectName,
it.includeMetricsInReport,
buildOperationRecords.mapNotNull {
prepareData(
taskResult = null,
it.path,
it.startTimeMs,
it.totalTimeMs + it.startTimeMs,
parameters.projectName,
buildUuid,
parameters.label,
parameters.kotlinVersion,
it,
onlyKotlinTask = false,
parameters.additionalTags
)
},
parameters.startParameters,
failureMessages.filter { it.isNotEmpty() },
loggerAdapter
)
} }
reportingSettings.singleOutputFile?.also { singleOutputFile -> reportingSettings.singleOutputFile?.also { singleOutputFile ->
@@ -98,26 +97,20 @@ abstract class BuildReportsService : BuildService<BuildReportsService.Parameters
executorService.shutdown() executorService.shutdown()
} }
override fun onFinish(event: FinishEvent?) { fun onFinish(
addHttpReport(event) event: TaskFinishEvent, buildOperation: BuildOperationRecord,
parameters: BuildReportParameters, buildScan: BuildScanExtensionHolder?
) {
buildScan?.also { addBuildScanReport(event, buildOperation, parameters, it) }
addHttpReport(event, buildOperation, parameters)
} }
private fun reportBuildStatInFile(fileReportSettings: FileReportSettings, buildData: BuildExecutionData) { private fun reportBuildFinish(parameters: BuildReportParameters) {
val ts = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Calendar.getInstance().time) val httpReportSettings = parameters.reportingSettings.httpReportSettings ?: return
val reportFile = fileReportSettings.buildReportDir.resolve("${parameters.projectName.get()}-build-$ts.txt")
PlainTextBuildReportWriter(
outputFile = reportFile,
printMetrics = fileReportSettings.includeMetricsInReport
).process(buildData, log)
}
private fun reportBuildFinish() {
val httpReportSettings = parameters.reportingSettings.get().httpReportSettings ?: return
val branchName = if (httpReportSettings.includeGitBranchName) { val branchName = if (httpReportSettings.includeGitBranchName) {
val process = ProcessBuilder("git", "rev-parse", "--abbrev-ref", "HEAD") val process = ProcessBuilder("git", "rev-parse", "--abbrev-ref", "HEAD")
.directory(parameters.projectDir.asFile.get()) .directory(parameters.projectDir)
.start().also { .start().also {
it.waitFor(5, TimeUnit.SECONDS) it.waitFor(5, TimeUnit.SECONDS)
} }
@@ -125,19 +118,19 @@ abstract class BuildReportsService : BuildService<BuildReportsService.Parameters
} else "is not set" } else "is not set"
val buildFinishData = BuildFinishStatisticsData( val buildFinishData = BuildFinishStatisticsData(
projectName = parameters.projectName.get(), projectName = parameters.projectName,
startParameters = parameters.startParameters.get() startParameters = parameters.startParameters
.includeVerboseEnvironment(parameters.reportingSettings.get().httpReportSettings?.verboseEnvironment ?: false), .includeVerboseEnvironment(parameters.reportingSettings.httpReportSettings.verboseEnvironment),
buildUuid = buildUuid, buildUuid = buildUuid,
label = parameters.label.orNull, label = parameters.label,
totalTime = TimeUnit.NANOSECONDS.toMillis((System.nanoTime() - startTime)), totalTime = TimeUnit.NANOSECONDS.toMillis((System.nanoTime() - startTime)),
finishTime = System.currentTimeMillis(), finishTime = System.currentTimeMillis(),
hostName = hostName, hostName = hostName,
tags = tags.toList(), tags = tags,
gitBranch = branchName gitBranch = branchName
) )
parameters.httpService.orNull?.sendData(buildFinishData, log) parameters.httpService?.sendData(buildFinishData, loggerAdapter)
} }
private fun GradleBuildStartParameters.includeVerboseEnvironment(verboseEnvironment: Boolean): GradleBuildStartParameters { private fun GradleBuildStartParameters.includeVerboseEnvironment(verboseEnvironment: Boolean): GradleBuildStartParameters {
@@ -154,69 +147,72 @@ abstract class BuildReportsService : BuildService<BuildReportsService.Parameters
} }
} }
private fun addHttpReport(event: FinishEvent?) { private fun addHttpReport(
parameters.httpService.orNull?.also { httpService -> event: TaskFinishEvent,
if (event is TaskFinishEvent) { buildOperationRecord: BuildOperationRecord,
val data = parameters: BuildReportParameters
prepareData( ) {
event, parameters.httpService?.also { httpService ->
parameters.projectName.get(), val data =
buildUuid, prepareData(
parameters.label.orNull, event,
parameters.kotlinVersion.get(), parameters.projectName,
parameters.buildMetricsService.get().buildOperationRecords, buildUuid,
parameters.additionalTags.get() parameters.label,
) parameters.kotlinVersion,
data?.also { buildOperationRecord,
executorService.submit { onlyKotlinTask = true,
httpService.sendData(data, log) parameters.additionalTags
} )
data?.also {
executorService.submit {
httpService.sendData(data, loggerAdapter)
} }
} }
} }
} }
private fun addBuildScanReport(
event: TaskFinishEvent,
buildOperationRecord: BuildOperationRecord,
parameters: BuildReportParameters,
buildScanExtension: BuildScanExtensionHolder
) {
val buildScanSettings = parameters.reportingSettings.buildScanReportSettings ?: return
private fun addBuildScanReport(event: FinishEvent?, buildScan: BuildScanExtensionHolder) { val (collectDataDuration, compileStatData) = measureTimeMillisWithResult {
val buildScanSettings = parameters.reportingSettings.orNull?.buildScanReportSettings prepareData(
if (buildScanSettings != null && buildScan.buildScan != null) { event,
if (event is TaskFinishEvent) { parameters.projectName, buildUuid, parameters.label,
val (collectDataDuration, compileStatData) = measureTimeMillisWithResult { parameters.kotlinVersion,
prepareData( buildOperationRecord,
event, parameters.projectName.get(), buildUuid, parameters.label.orNull, metricsToShow = buildScanSettings.metrics
parameters.kotlinVersion.get(), )
parameters.buildMetricsService.get().buildOperationRecords, }
metricsToShow = buildScanSettings.metrics log.debug("Collect data takes $collectDataDuration: $compileStatData")
)
}
log.debug("Collect data takes $collectDataDuration: $compileStatData")
compileStatData?.also { compileStatData?.also {
addBuildScanReport(it, buildScanSettings.customValueLimit, buildScan) addBuildScanReport(it, buildScanSettings.customValueLimit, buildScanExtension)
}
}
} }
} }
private fun addBuildScanReport(data: CompileStatisticsData, customValuesLimit: Int, buildScan: BuildScanExtensionHolder) { private fun addBuildScanReport(data: CompileStatisticsData, customValuesLimit: Int, buildScan: BuildScanExtensionHolder) {
val elapsedTime = measureTimeMillis { val elapsedTime = measureTimeMillis {
data.tags.forEach { tags.add(it) } tags.addAll(data.tags)
buildScan.buildScan?.also { if (customValues < customValuesLimit) {
if (customValues < customValuesLimit) { readableString(data).forEach {
readableString(data).forEach { if (customValues < customValuesLimit) {
if (customValues < customValuesLimit) { addBuildScanValue(buildScan, data, it)
addBuildScanValue(buildScan, data, it) } else {
} else { log.debug(
log.debug( "Can't add any more custom values into build scan." +
"Can't add any more custom values into build scan." + " Statistic data for ${data.taskName} was cut due to custom values limit."
" Statistic data for ${data.taskName} was cut due to custom values limit." )
)
}
} }
} else {
log.debug("Can't add any more custom values into build scan.")
} }
} else {
log.debug("Can't add any more custom values into build scan.")
} }
} }
@@ -228,7 +224,7 @@ abstract class BuildReportsService : BuildService<BuildReportsService.Parameters
data: CompileStatisticsData, data: CompileStatisticsData,
customValue: String customValue: String
) { ) {
buildScan.buildScan?.value(data.taskName, customValue) buildScan.buildScan.value(data.taskName, customValue)
customValues++ customValues++
} }
@@ -245,7 +241,7 @@ abstract class BuildReportsService : BuildService<BuildReportsService.Parameters
) { it.readableString } ) { it.readableString }
} }
data.kotlinLanguageVersion?.version?.also { data.kotlinLanguageVersion?.also {
readableString.append("Kotlin language version: $it; ") readableString.append("Kotlin language version: $it; ")
} }
@@ -283,14 +279,18 @@ abstract class BuildReportsService : BuildService<BuildReportsService.Parameters
return splattedString return splattedString
} }
private fun initBuildScanTags(buildScan: BuildScanExtensionHolder) { internal fun initBuildScanTags(buildScan: BuildScanExtensionHolder, label: String?) {
buildScan.buildScan?.tag(buildUuid) buildScan.buildScan.tag(buildUuid)
parameters.label.orNull?.also { label?.also {
buildScan.buildScan?.tag(it) buildScan.buildScan.tag(it)
}
val debugConfiguration = "-agentlib:"
if (ManagementFactory.getRuntimeMXBean().inputArguments.firstOrNull { it.startsWith(debugConfiguration) } != null) {
buildScan.buildScan.tag(StatTag.GRADLE_DEBUG.readableString)
} }
} }
private fun addCollectedTags(buildScan: BuildScanExtensionHolder) { internal fun addCollectedTags(buildScan: BuildScanExtensionHolder) {
replaceWithCombinedTag( replaceWithCombinedTag(
StatTag.KOTLIN_1, StatTag.KOTLIN_1,
StatTag.KOTLIN_2, StatTag.KOTLIN_2,
@@ -303,7 +303,7 @@ abstract class BuildReportsService : BuildService<BuildReportsService.Parameters
StatTag.INCREMENTAL_AND_NON_INCREMENTAL StatTag.INCREMENTAL_AND_NON_INCREMENTAL
) )
tags.forEach { buildScan.buildScan?.tag(it.readableString) } tags.forEach { buildScan.buildScan.tag(it.readableString) }
} }
private fun replaceWithCombinedTag(firstTag: StatTag, secondTag: StatTag, combinedTag: StatTag) { private fun replaceWithCombinedTag(firstTag: StatTag, secondTag: StatTag, combinedTag: StatTag) {
@@ -331,78 +331,6 @@ abstract class BuildReportsService : BuildService<BuildReportsService.Parameters
) )
} }
private fun registerIfAbsentImpl(
project: Project,
buildMetricsService: Provider<BuildMetricsService>
): Provider<BuildReportsService>? {
val serviceClass = BuildReportsService::class.java
val serviceName = "${serviceClass.name}_${serviceClass.classLoader.hashCode()}"
val reportingSettings = reportingSettings(project)
if (reportingSettings.buildReportOutputs.isEmpty()) {
return null //no need to collect data
}
val kotlinVersion = project.getKotlinPluginVersion()
val gradle = project.gradle
project.gradle.sharedServices.registrations.findByName(serviceName)?.let {
@Suppress("UNCHECKED_CAST")
return it.service as Provider<BuildReportsService>
}
return gradle.sharedServices.registerIfAbsent(serviceName, serviceClass) {
it.parameters.label.set(reportingSettings.buildReportLabel)
it.parameters.projectName.set(project.rootProject.name)
it.parameters.kotlinVersion.set(kotlinVersion)
it.parameters.startParameters.set(getStartParameters(project))
it.parameters.reportingSettings.set(reportingSettings)
reportingSettings.httpReportSettings?.let { httpSettings -> it.parameters.httpService.set(HttpReportServiceImpl(httpSettings)) }
it.parameters.buildMetricsService = buildMetricsService
it.parameters.projectDir.set(project.rootProject.layout.projectDirectory)
//init gradle tags for build scan and http reports
it.parameters.additionalTags.value(setupTags(gradle))
}.also { buildServiceProvider ->
if (reportingSettings.httpReportSettings != null) {
BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(buildServiceProvider)
}
val buildScanExtension = project.rootProject.extensions.findByName("buildScan")
if (reportingSettings.buildScanReportSettings != null && buildScanExtension != null) {
val buildScan = BuildScanExtensionHolder(buildScanExtension)
buildServiceProvider.get().initBuildScanTags(buildScan)
BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(project.provider {
OperationCompletionListener { event ->
buildServiceProvider.get().addBuildScanReport(event, buildScan)
}
})
buildScan.buildScan?.buildFinished {
buildServiceProvider.get().addCollectedTags(buildScan)
}
}
}
}
fun registerIfAbsent(project: Project, buildMetricsService: Provider<BuildMetricsService>) =
registerIfAbsentImpl(project, buildMetricsService)?.also { serviceProvider ->
SingleActionPerProject.run(project, UsesBuildReportsService::class.java.name) {
project.tasks.withType<UsesBuildReportsService>().configureEach { task ->
task.usesService(serviceProvider)
}
}
}
private fun setupTags(gradle: Gradle): ArrayList<StatTag> {
val additionalTags = ArrayList<StatTag>()
if (isConfigurationCacheAvailable(gradle)) {
additionalTags.add(StatTag.CONFIGURATION_CACHE)
}
if (gradle.startParameter.isBuildCacheEnabled) {
additionalTags.add(StatTag.BUILD_CACHE)
}
return additionalTags
}
val hostName: String? = try { val hostName: String? = try {
InetAddress.getLocalHost().hostName InetAddress.getLocalHost().hostName
} catch (_: Exception) { } catch (_: Exception) {
@@ -422,3 +350,15 @@ enum class TaskExecutionState {
UP_TO_DATE UP_TO_DATE
; ;
} }
data class BuildReportParameters(
val startParameters: GradleBuildStartParameters,
val reportingSettings: ReportingSettings,
val httpService: HttpReportService?,
val projectDir: File,
val label: String?,
val projectName: String,
val kotlinVersion: String,
val additionalTags: Set<StatTag>
)
@@ -7,6 +7,6 @@ package org.jetbrains.kotlin.gradle.report
import com.gradle.scan.plugin.BuildScanExtension import com.gradle.scan.plugin.BuildScanExtension
class BuildScanExtensionHolder(val buildScan: BuildScanExtension?) : java.io.Serializable { class BuildScanExtensionHolder(val buildScan: BuildScanExtension) : java.io.Serializable {
constructor(extension: Any?) : this(extension as BuildScanExtension) constructor(extension: Any) : this(extension as BuildScanExtension)
} }
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2023 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
class GradleLoggerAdapter(private val log: Logger) : org.jetbrains.kotlin.util.Logger {
override fun log(message: String) {
log.info(message)
}
override fun warning(message: String) {
log.warn(message)
}
override fun fatal(message: String): Nothing {
log.error(message)
kotlin.error(message)
}
override fun error(message: String, throwable: Throwable?) {
throwable?.let { log.error(message, throwable) } ?: log.error(message)
}
override fun lifecycle(message: String) {
log.lifecycle(message)
}
}
@@ -1,284 +0,0 @@
/*
* 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.*
import org.jetbrains.kotlin.gradle.report.data.BuildExecutionData
import org.jetbrains.kotlin.gradle.report.data.BuildOperationRecord
import org.jetbrains.kotlin.gradle.utils.Printer
import org.jetbrains.kotlin.gradle.utils.asString
import org.jetbrains.kotlin.gradle.utils.formatSize
import org.jetbrains.kotlin.gradle.utils.formatTime
import java.io.File
import java.io.Serializable
import java.util.*
import kotlin.math.max
internal class PlainTextBuildReportWriter(
private val outputFile: File,
private val printMetrics: Boolean
) : Serializable {
private lateinit var p: Printer
fun process(build: BuildExecutionData, log: Logger) {
val buildReportPath = outputFile.toPath().toUri().toString()
try {
outputFile.parentFile.mkdirs()
if (!(outputFile.parentFile.exists() && outputFile.parentFile.isDirectory)) {
log.error("Kotlin build report cannot be created: '$outputFile.parentFile' is a file or do not have permissions to create")
return
}
outputFile.bufferedWriter().use { writer ->
p = Printer(writer)
printBuildReport(build)
}
log.lifecycle("Kotlin build report is written to $buildReportPath")
} catch (e: Exception) {
log.error("Could not write Kotlin build report to $buildReportPath", e)
}
}
private fun printBuildReport(build: BuildExecutionData) {
// NOTE: BuildExecutionData / BuildOperationRecord contains data for both tasks and transforms.
// Where possible, we still use the term "tasks" because saying "tasks/transforms" is a bit verbose and "build operations" may sound
// a bit unfamiliar.
// TODO: If it is confusing, consider renaming "tasks" to "build operations" in this class.
printBuildInfo(build)
if (printMetrics) {
printMetrics(build.aggregatedMetrics, aggregatedMetric = true)
p.println()
}
printTaskOverview(build)
printTasksLog(build)
}
private fun printBuildInfo(build: BuildExecutionData) {
p.withIndent("Gradle start parameters:") {
build.startParameters.let {
p.println("tasks = ${it.tasks}")
p.println("excluded tasks = ${it.excludedTasks}")
p.println("current dir = ${it.currentDir}")
p.println("project properties args = ${it.projectProperties}")
p.println(" system properties args = ${it.systemProperties}")
}
}
p.println()
if (build.failureMessages.isNotEmpty()) {
p.println("Build failed: ${build.failureMessages}")
p.println()
}
}
private fun printMetrics(buildMetrics: BuildMetrics, aggregatedMetric: Boolean = false) {
printBuildTimes(buildMetrics.buildTimes)
if (aggregatedMetric) p.println()
printBuildPerformanceMetrics(buildMetrics.buildPerformanceMetrics)
if (aggregatedMetric) p.println()
printBuildAttributes(buildMetrics.buildAttributes)
//TODO: KT-57310 Implement build GC metric in
if (!aggregatedMetric) {
printGcMetrics(buildMetrics.gcMetrics)
}
}
private fun printBuildTimes(buildTimes: BuildTimes) {
val buildTimesMs = buildTimes.asMapMs()
if (buildTimesMs.isEmpty()) return
p.println("Time metrics:")
p.withIndent {
val visitedBuildTimes = HashSet<BuildTime>()
fun printBuildTime(buildTime: BuildTime) {
if (!visitedBuildTimes.add(buildTime)) return
val timeMs = buildTimesMs[buildTime]
if (timeMs != null) {
p.println("${buildTime.readableString}: ${formatTime(timeMs)}")
p.withIndent {
BuildTime.children[buildTime]?.forEach { printBuildTime(it) }
}
} else {
//Skip formatting if parent metric does not set
BuildTime.children[buildTime]?.forEach { printBuildTime(it) }
}
}
for (buildTime in BuildTime.values()) {
if (buildTime.parent != null) continue
printBuildTime(buildTime)
}
}
}
private fun printBuildPerformanceMetrics(buildMetrics: BuildPerformanceMetrics) {
val allBuildMetrics = buildMetrics.asMap()
if (allBuildMetrics.isEmpty()) return
p.withIndent("Size metrics:") {
for (metric in BuildPerformanceMetric.values()) {
allBuildMetrics[metric]?.let { printSizeMetric(metric, it) }
}
}
}
private fun printSizeMetric(sizeMetric: BuildPerformanceMetric, value: Long) {
fun BuildPerformanceMetric.numberOfAncestors(): Int {
var count = 0
var parent: BuildPerformanceMetric? = parent
while (parent != null) {
count++
parent = parent.parent
}
return count
}
val indentLevel = sizeMetric.numberOfAncestors()
repeat(indentLevel) { p.pushIndent() }
when (sizeMetric.type) {
ValueType.BYTES -> p.println("${sizeMetric.readableString}: ${formatSize(value)}")
ValueType.NUMBER -> p.println("${sizeMetric.readableString}: $value")
}
repeat(indentLevel) { p.popIndent() }
}
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.readableString to v }.toMap())
}
}
}
private fun printGcMetrics(gcMetrics: GcMetrics) {
val allGcMetrics = gcMetrics.asMap()
if (allGcMetrics.isEmpty()) return
p.withIndent("GC metrics:") {
for (gcMetric in allGcMetrics) {
p.println("${gcMetric.key}:")
p.withIndent {
p.println("GC count: ${gcMetric.value.count}")
p.println("GC time: ${formatTime(gcMetric.value.time)}")
}
}
}
}
private fun printTaskOverview(build: BuildExecutionData) {
var allTasksTimeMs = 0L
var kotlinTotalTimeMs = 0L
val kotlinTasks = ArrayList<BuildOperationRecord>()
for (task in build.buildOperationRecord) {
val taskTimeMs = task.totalTimeMs
allTasksTimeMs += taskTimeMs
if (task.isFromKotlinPlugin) {
kotlinTotalTimeMs += taskTimeMs
kotlinTasks.add(task)
}
}
if (kotlinTasks.isEmpty()) {
p.println("No Kotlin task was run")
return
}
val ktTaskPercent = (kotlinTotalTimeMs.toDouble() / allTasksTimeMs * 100).asString(1)
p.println("Total time for Kotlin tasks: ${formatTime(kotlinTotalTimeMs)} ($ktTaskPercent % of all tasks time)")
val table = TextTable("Time", "% of Kotlin time", "Task")
for (task in kotlinTasks.sortedWith(compareBy({ -it.totalTimeMs }, { it.startTimeMs }))) {
val timeMs = task.totalTimeMs
val percent = (timeMs.toDouble() / kotlinTotalTimeMs * 100).asString(1)
table.addRow(formatTime(timeMs), "$percent %", task.path)
}
table.printTo(p)
p.println()
}
private fun printTasksLog(build: BuildExecutionData) {
for (task in build.buildOperationRecord.sortedWith(compareBy({ -it.totalTimeMs }, { it.startTimeMs }))) {
printTaskLog(task)
p.println()
}
}
private fun printTaskLog(task: BuildOperationRecord) {
val skipMessage = task.skipMessage
if (skipMessage != null) {
p.println("Task '${task.path}' was skipped: $skipMessage")
} else {
p.println("Task '${task.path}' finished in ${formatTime(task.totalTimeMs)}")
}
if (task.icLogLines.isNotEmpty()) {
p.withIndent("Compilation log for task '${task.path}':") {
task.icLogLines.forEach { p.println(it) }
}
}
if (printMetrics) {
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)
}
}
}
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.gradle.report package org.jetbrains.kotlin.gradle.report
import org.jetbrains.kotlin.build.report.FileReportSettings
import org.jetbrains.kotlin.build.report.HttpReportSettings
import java.io.File import java.io.File
import java.io.Serializable import java.io.Serializable
@@ -23,27 +25,6 @@ data class ReportingSettings(
} }
} }
data class FileReportSettings(
val buildReportDir: File,
val includeMetricsInReport: Boolean = false,
) : Serializable {
companion object {
const val serialVersionUID: Long = 0
}
}
data class HttpReportSettings(
val url: String,
val password: String?,
val user: String?,
val verboseEnvironment: Boolean,
val includeGitBranchName: Boolean
) : Serializable {
companion object {
const val serialVersionUID: Long = 0
}
}
data class BuildScanSettings( data class BuildScanSettings(
val customValueLimit: Int, val customValueLimit: Int,
val metrics: Set<String>? val metrics: Set<String>?
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.report
import org.jetbrains.kotlin.build.report.metrics.BuildMetrics import org.jetbrains.kotlin.build.report.metrics.BuildMetrics
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.build.report.statistic.StatTag
import org.jetbrains.kotlin.incremental.ChangedFiles import org.jetbrains.kotlin.incremental.ChangedFiles
internal class TaskExecutionResult( internal class TaskExecutionResult(
@@ -19,6 +20,5 @@ internal class TaskExecutionInfo(
val kotlinLanguageVersion: KotlinVersion? = null, val kotlinLanguageVersion: KotlinVersion? = null,
val changedFiles: ChangedFiles? = null, val changedFiles: ChangedFiles? = null,
val compilerArguments: Array<String> = emptyArray(), val compilerArguments: Array<String> = emptyArray(),
val withArtifactTransform: Boolean? = false, val tags: Set<StatTag> = emptySet(),
val withAbiSnapshot: Boolean? = false
) )
@@ -6,6 +6,8 @@
package org.jetbrains.kotlin.gradle.report package org.jetbrains.kotlin.gradle.report
import org.gradle.api.Project import org.gradle.api.Project
import org.jetbrains.kotlin.build.report.FileReportSettings
import org.jetbrains.kotlin.build.report.HttpReportSettings
import org.jetbrains.kotlin.build.report.metrics.BuildPerformanceMetric import org.jetbrains.kotlin.build.report.metrics.BuildPerformanceMetric
import org.jetbrains.kotlin.build.report.metrics.BuildTime import org.jetbrains.kotlin.build.report.metrics.BuildTime
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
@@ -14,7 +16,6 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLI
import org.jetbrains.kotlin.gradle.plugin.internal.isProjectIsolationEnabled import org.jetbrains.kotlin.gradle.plugin.internal.isProjectIsolationEnabled
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toUpperCaseAsciiOnly import org.jetbrains.kotlin.util.capitalizeDecapitalize.toUpperCaseAsciiOnly
private val availableMetrics = BuildTime.values().map { it.name } + BuildPerformanceMetric.values().map { it.name } private val availableMetrics = BuildTime.values().map { it.name } + BuildPerformanceMetric.values().map { it.name }
internal fun reportingSettings(project: Project): ReportingSettings { internal fun reportingSettings(project: Project): ReportingSettings {
@@ -6,7 +6,7 @@
package org.jetbrains.kotlin.gradle.report.data package org.jetbrains.kotlin.gradle.report.data
import org.jetbrains.kotlin.build.report.metrics.BuildMetrics import org.jetbrains.kotlin.build.report.metrics.BuildMetrics
import org.jetbrains.kotlin.gradle.plugin.stat.GradleBuildStartParameters import org.jetbrains.kotlin.build.report.statistic.GradleBuildStartParameters
class BuildExecutionData( class BuildExecutionData(
val startParameters: GradleBuildStartParameters, val startParameters: GradleBuildStartParameters,
@@ -10,20 +10,24 @@ import org.gradle.tooling.events.task.TaskFinishEvent
import org.gradle.tooling.events.task.TaskSkippedResult import org.gradle.tooling.events.task.TaskSkippedResult
import org.gradle.tooling.events.task.TaskSuccessResult import org.gradle.tooling.events.task.TaskSuccessResult
import org.jetbrains.kotlin.build.report.metrics.* import org.jetbrains.kotlin.build.report.metrics.*
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults import org.jetbrains.kotlin.build.report.statistic.CompileStatisticsData
import org.jetbrains.kotlin.gradle.plugin.stat.CompileStatisticsData import org.jetbrains.kotlin.build.report.statistic.StatTag
import org.jetbrains.kotlin.gradle.plugin.stat.StatTag
import org.jetbrains.kotlin.gradle.report.data.BuildOperationRecord import org.jetbrains.kotlin.gradle.report.data.BuildOperationRecord
import org.jetbrains.kotlin.incremental.ChangedFiles import org.jetbrains.kotlin.incremental.ChangedFiles
import org.jetbrains.kotlin.utils.addToStdlib.ifTrue
import java.lang.management.ManagementFactory
import java.util.ArrayList
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
private fun availableForStat(taskPath: String): Boolean { internal fun getTaskResult(event: TaskFinishEvent) = when (val result = event.result) {
return taskPath.contains("Kotlin") && (TaskExecutionResults[taskPath] != null) is TaskSuccessResult -> when {
result.isFromCache -> TaskExecutionState.FROM_CACHE
result.isUpToDate -> TaskExecutionState.UP_TO_DATE
else -> TaskExecutionState.SUCCESS
}
is TaskSkippedResult -> TaskExecutionState.SKIPPED
is TaskFailureResult -> TaskExecutionState.FAILED
else -> TaskExecutionState.UNKNOWN
} }
internal fun prepareData( internal fun prepareData(
@@ -32,95 +36,112 @@ internal fun prepareData(
uuid: String, uuid: String,
label: String?, label: String?,
kotlinVersion: String, kotlinVersion: String,
buildOperationRecords: Collection<BuildOperationRecord>, buildOperationRecord: BuildOperationRecord,
additionalTags: List<StatTag> = emptyList(), onlyKotlinTask: Boolean = true,
additionalTags: Set<StatTag> = emptySet(),
metricsToShow: Set<String>? = null metricsToShow: Set<String>? = null
): CompileStatisticsData? { ): CompileStatisticsData? {
val result = event.result val result = event.result
val taskPath = event.descriptor.taskPath val taskPath = event.descriptor.taskPath
val durationMs = result.endTime - result.startTime return prepareData(getTaskResult(event), taskPath, result.startTime, result.endTime - result.startTime, projectName, uuid,
val taskResult = when (result) { label, kotlinVersion, buildOperationRecord, onlyKotlinTask, additionalTags, metricsToShow)
is TaskSuccessResult -> when { }
result.isFromCache -> TaskExecutionState.FROM_CACHE
result.isUpToDate -> TaskExecutionState.UP_TO_DATE
else -> TaskExecutionState.SUCCESS
}
is TaskSkippedResult -> TaskExecutionState.SKIPPED internal fun prepareData(
is TaskFailureResult -> TaskExecutionState.FAILED taskResult: TaskExecutionState?,
else -> TaskExecutionState.UNKNOWN taskPath: String,
} startTime: Long,
finishTime: Long,
if (!availableForStat(taskPath)) { projectName: String,
uuid: String,
label: String?,
kotlinVersion: String,
buildOperationRecord: BuildOperationRecord,
onlyKotlinTask: Boolean = true,
additionalTags: Set<StatTag> = emptySet(),
metricsToShow: Set<String>? = null
): CompileStatisticsData? {
if (onlyKotlinTask && !(buildOperationRecord is TaskRecord && buildOperationRecord.isFromKotlinPlugin)) {
return null return null
} }
val taskExecutionResult = TaskExecutionResults[taskPath] val buildMetrics = buildOperationRecord.buildMetrics
val buildMetrics = buildOperationRecords.firstOrNull { it.path == taskPath }?.buildMetrics
val performanceMetrics = collectBuildPerformanceMetrics(taskExecutionResult, buildMetrics) val performanceMetrics = collectBuildPerformanceMetrics(buildMetrics)
val buildTimesMetrics = collectBuildMetrics( val buildTimesMetrics = collectBuildMetrics(
taskExecutionResult, buildMetrics, performanceMetrics, result.startTime, buildMetrics, startTime, System.currentTimeMillis()
System.currentTimeMillis()
) )
val buildAttributes = collectBuildAttributes(taskExecutionResult, buildMetrics) val buildAttributes = collectBuildAttributes(buildMetrics)
val changes = when (val changedFiles = taskExecutionResult?.taskInfo?.changedFiles) { val changes = if (buildOperationRecord is TaskRecord && buildOperationRecord.changedFiles is ChangedFiles.Known) {
is ChangedFiles.Known -> changedFiles.modified.map { it.absolutePath } + changedFiles.removed.map { it.absolutePath } buildOperationRecord.changedFiles.modified.map { it.absolutePath } + buildOperationRecord.changedFiles.removed.map { it.absolutePath }
else -> emptyList<String>() } else {
emptyList<String>()
} }
val kotlinLanguageVersion = if (buildOperationRecord is TaskRecord) buildOperationRecord.kotlinLanguageVersion else null
return CompileStatisticsData( return CompileStatisticsData(
durationMs = durationMs, durationMs = buildOperationRecord.totalTimeMs,
taskResult = taskResult.name, taskResult = taskResult?.name,
label = label, label = label,
buildTimesMetrics = filterMetrics(metricsToShow, buildTimesMetrics), buildTimesMetrics = filterMetrics(metricsToShow, buildTimesMetrics),
performanceMetrics = filterMetrics(metricsToShow, performanceMetrics), performanceMetrics = filterMetrics(metricsToShow, performanceMetrics),
projectName = projectName, projectName = projectName,
taskName = taskPath, taskName = taskPath,
changes = changes, changes = changes,
tags = collectTags(taskExecutionResult, buildMetrics, additionalTags), tags = collectTags(buildOperationRecord, additionalTags),
nonIncrementalAttributes = buildAttributes, nonIncrementalAttributes = buildAttributes,
hostName = BuildReportsService.hostName, hostName = BuildReportsService.hostName,
kotlinVersion = kotlinVersion, kotlinVersion = kotlinVersion,
kotlinLanguageVersion = taskExecutionResult?.taskInfo?.kotlinLanguageVersion, kotlinLanguageVersion = kotlinLanguageVersion?.version,
buildUuid = uuid, buildUuid = uuid,
finishTime = System.currentTimeMillis(), compilerArguments = collectCompilerArguments(buildOperationRecord),
compilerArguments = taskExecutionResult?.taskInfo?.compilerArguments?.asList() ?: emptyList(), gcCountMetrics = buildMetrics.gcMetrics.asGcCountMap(),
gcCountMetrics = buildMetrics?.gcMetrics?.asGcCountMap(), gcTimeMetrics = buildMetrics.gcMetrics.asGcTimeMap(),
gcTimeMetrics = buildMetrics?.gcMetrics?.asGcTimeMap() finishTime = finishTime,
startTimeMs = startTime,
fromKotlinPlugin = buildOperationRecord.isFromKotlinPlugin,
skipMessage = buildOperationRecord.skipMessage,
icLogLines = buildOperationRecord.icLogLines
) )
} }
fun collectCompilerArguments(buildOperationRecord: BuildOperationRecord?): List<String> {
return if (buildOperationRecord is TaskRecord) {
buildOperationRecord.compilerArguments.asList()
} else emptyList()
}
private fun <E : Enum<E>> filterMetrics( private fun <E : Enum<E>> filterMetrics(
expectedMetrics: Set<String>?, expectedMetrics: Set<String>?,
buildTimesMetrics: Map<E, Long> buildTimesMetrics: Map<E, Long>
): Map<E, Long> = expectedMetrics?.let { buildTimesMetrics.filterKeys { metric -> it.contains(metric.name) } } ?: buildTimesMetrics ): Map<E, Long> = expectedMetrics?.let { buildTimesMetrics.filterKeys { metric -> it.contains(metric.name) } } ?: buildTimesMetrics
private fun collectBuildAttributes(taskExecutionResult: TaskExecutionResult?, buildMetrics: BuildMetrics?): Set<BuildAttribute> { private fun collectBuildAttributes(buildMetrics: BuildMetrics?): Set<BuildAttribute> {
val attributes = HashSet<BuildAttribute>() return buildMetrics?.buildAttributes?.asMap()?.filter { it.value > 0 }?.keys ?: emptySet()
buildMetrics?.buildAttributes?.asMap()?.filter { it.value > 0 }?.keys?.also { attributes.addAll(it) }
taskExecutionResult?.buildMetrics?.buildAttributes?.asMap()?.filter { it.value > 0 }?.keys?.also { attributes.addAll(it) }
return attributes
} }
private fun collectBuildPerformanceMetrics( private fun collectBuildPerformanceMetrics(
taskExecutionResult: TaskExecutionResult?,
buildMetrics: BuildMetrics? buildMetrics: BuildMetrics?
): Map<BuildPerformanceMetric, Long> { ): Map<BuildPerformanceMetric, Long> {
val taskBuildPerformanceMetrics = HashMap<BuildPerformanceMetric, Long>() return buildMetrics?.buildPerformanceMetrics?.asMap()
taskExecutionResult?.buildMetrics?.buildPerformanceMetrics?.asMap()?.let { taskBuildPerformanceMetrics.putAll(it) } ?.filterValues { value -> value != 0L }
buildMetrics?.buildPerformanceMetrics?.asMap()?.let { taskBuildPerformanceMetrics.putAll(it) } ?.filterKeys { key ->
return taskBuildPerformanceMetrics.filterValues { value -> value != 0L } key !in listOf(
BuildPerformanceMetric.START_WORKER_EXECUTION,
BuildPerformanceMetric.CALL_WORKER,
BuildPerformanceMetric.CALL_KOTLIN_DAEMON,
BuildPerformanceMetric.START_KOTLIN_DAEMON_EXECUTION
)
}
?: emptyMap()
} }
private fun collectBuildMetrics( private fun collectBuildMetrics(
taskExecutionResult: TaskExecutionResult?,
buildMetrics: BuildMetrics?, buildMetrics: BuildMetrics?,
performanceMetrics: Map<BuildPerformanceMetric, Long>,
gradleTaskStartTime: Long? = null, gradleTaskStartTime: Long? = null,
taskFinishEventTime: Long? = null, taskFinishEventTime: Long? = null,
): Map<BuildTime, Long> { ): Map<BuildTime, Long> {
val taskBuildMetrics = HashMap<BuildTime, Long>() val taskBuildMetrics = HashMap<BuildTime, Long>(buildMetrics?.buildTimes?.asMapMs())
taskExecutionResult?.buildMetrics?.buildTimes?.asMapMs()?.let { taskBuildMetrics.putAll(it) } val performanceMetrics = buildMetrics?.buildPerformanceMetrics?.asMap() ?: emptyMap()
buildMetrics?.buildTimes?.asMapMs()?.let { taskBuildMetrics.putAll(it) }
gradleTaskStartTime?.let { startTime -> gradleTaskStartTime?.let { startTime ->
performanceMetrics[BuildPerformanceMetric.START_TASK_ACTION_EXECUTION]?.let { actionStartTime -> performanceMetrics[BuildPerformanceMetric.START_TASK_ACTION_EXECUTION]?.let { actionStartTime ->
taskBuildMetrics.put(BuildTime.GRADLE_TASK_PREPARATION, actionStartTime - startTime) taskBuildMetrics.put(BuildTime.GRADLE_TASK_PREPARATION, actionStartTime - startTime)
@@ -141,41 +162,34 @@ private fun collectBuildMetrics(
} }
private fun collectTags( private fun collectTags(
taskExecutionResult: TaskExecutionResult?, buildOperation: BuildOperationRecord?,
buildMetrics: BuildMetrics?, additionalTags: Set<StatTag>
additionalTags: List<StatTag> ): Set<StatTag> {
): List<StatTag> { val tags = HashSet(additionalTags)
val tags = collectTags(taskExecutionResult, additionalTags) if (buildOperation is TaskRecord) {
val nonIncrementalAttributes = collectBuildAttributes(taskExecutionResult, buildMetrics) tags.addAll(collectTaskRecordTags(buildOperation))
}
val nonIncrementalAttributes = collectBuildAttributes(buildOperation?.buildMetrics)
if (nonIncrementalAttributes.isEmpty()) { if (nonIncrementalAttributes.isEmpty()) {
tags.add(StatTag.INCREMENTAL) tags.add(StatTag.INCREMENTAL)
} else { } else {
tags.add(StatTag.NON_INCREMENTAL) tags.add(StatTag.NON_INCREMENTAL)
} }
return tags return tags
} }
private fun collectTags( private fun collectTaskRecordTags(
taskExecutionResult: TaskExecutionResult?, taskRecord: TaskRecord?,
additionalTags: List<StatTag>, ): Set<StatTag> {
): MutableList<StatTag> { val tags = HashSet<StatTag>()
val tags = ArrayList(additionalTags)
val taskInfo = taskExecutionResult?.taskInfo
taskInfo?.withAbiSnapshot?.ifTrue { taskRecord?.kotlinLanguageVersion?.also {
tags.add(StatTag.ABI_SNAPSHOT)
}
taskInfo?.withArtifactTransform?.ifTrue {
tags.add(StatTag.ARTIFACT_TRANSFORM)
}
taskInfo?.kotlinLanguageVersion?.also {
tags.add(getLanguageVersionTag(it)) tags.add(getLanguageVersionTag(it))
} }
val debugConfiguration = "-agentlib:" taskRecord?.statTags?.let { tags.addAll(it) }
if (ManagementFactory.getRuntimeMXBean().inputArguments.firstOrNull { it.startsWith(debugConfiguration) } != null) {
tags.add(StatTag.GRADLE_DEBUG)
}
return tags return tags
} }
@@ -36,7 +36,6 @@ import org.jetbrains.kotlin.gradle.internal.UsesClassLoadersCachingBuildService
import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
import org.jetbrains.kotlin.gradle.logging.GradleKotlinLogger import org.jetbrains.kotlin.gradle.logging.GradleKotlinLogger
import org.jetbrains.kotlin.gradle.logging.kotlinDebug import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerArgumentsProducer.CreateCompilerArgumentsContext.Companion.default
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_SUPPRESS_EXPERIMENTAL_IC_OPTIMIZATIONS_WARNING import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_SUPPRESS_EXPERIMENTAL_IC_OPTIMIZATIONS_WARNING
import org.jetbrains.kotlin.gradle.plugin.UsesBuildFinishedListenerService import org.jetbrains.kotlin.gradle.plugin.UsesBuildFinishedListenerService
import org.jetbrains.kotlin.gradle.plugin.UsesVariantImplementationFactories import org.jetbrains.kotlin.gradle.plugin.UsesVariantImplementationFactories
@@ -57,7 +56,6 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments> @Inject constr
) : AbstractKotlinCompileTool<T>(objectFactory), ) : AbstractKotlinCompileTool<T>(objectFactory),
CompileUsingKotlinDaemonWithNormalization, CompileUsingKotlinDaemonWithNormalization,
UsesBuildMetricsService, UsesBuildMetricsService,
UsesBuildReportsService,
UsesIncrementalModuleInfoBuildService, UsesIncrementalModuleInfoBuildService,
UsesCompilerSystemPropertiesService, UsesCompilerSystemPropertiesService,
UsesVariantImplementationFactories, UsesVariantImplementationFactories,
@@ -114,7 +112,7 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments> @Inject constr
@get:Internal @get:Internal
internal abstract val suppressKotlinOptionsFreeArgsModificationWarning: Property<Boolean> internal abstract val suppressKotlinOptionsFreeArgsModificationWarning: Property<Boolean>
internal fun reportingSettings() = buildReportsService.orNull?.parameters?.reportingSettings?.orNull ?: ReportingSettings() internal fun reportingSettings() = buildMetricsService.orNull?.parameters?.reportingSettings?.orNull ?: ReportingSettings()
@get:Internal @get:Internal
protected val multiModuleICSettings: MultiModuleICSettings protected val multiModuleICSettings: MultiModuleICSettings
@@ -25,7 +25,6 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.associateWithClosure
import org.jetbrains.kotlin.gradle.plugin.mpp.internal import org.jetbrains.kotlin.gradle.plugin.mpp.internal
import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToCompilerOptions import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToCompilerOptions
import org.jetbrains.kotlin.gradle.report.BuildMetricsService import org.jetbrains.kotlin.gradle.report.BuildMetricsService
import org.jetbrains.kotlin.gradle.report.BuildReportsService
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KOTLIN_BUILD_DIR_NAME import org.jetbrains.kotlin.gradle.tasks.KOTLIN_BUILD_DIR_NAME
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
@@ -64,7 +63,6 @@ internal abstract class AbstractKotlinCompileConfig<TASK : AbstractKotlinCompile
val compilerSystemPropertiesService = CompilerSystemPropertiesService.registerIfAbsent(project) val compilerSystemPropertiesService = CompilerSystemPropertiesService.registerIfAbsent(project)
val buildMetricsService = BuildMetricsService.registerIfAbsent(project) val buildMetricsService = BuildMetricsService.registerIfAbsent(project)
val buildReportsService = buildMetricsService?.let { BuildReportsService.registerIfAbsent(project, buildMetricsService) }
val incrementalModuleInfoProvider = val incrementalModuleInfoProvider =
IncrementalModuleInfoBuildService.registerIfAbsent(project, objectFactory.providerWithLazyConvention { IncrementalModuleInfoBuildService.registerIfAbsent(project, objectFactory.providerWithLazyConvention {
GradleCompilerRunner.buildModulesInfo(project.gradle) GradleCompilerRunner.buildModulesInfo(project.gradle)
@@ -84,9 +82,6 @@ internal abstract class AbstractKotlinCompileConfig<TASK : AbstractKotlinCompile
task.localStateDirectories.from(task.taskBuildLocalStateDirectory).disallowChanges() task.localStateDirectories.from(task.taskBuildLocalStateDirectory).disallowChanges()
buildMetricsService?.also { metricsService -> buildMetricsService?.also { metricsService ->
task.buildMetricsService.value(metricsService).disallowChanges() task.buildMetricsService.value(metricsService).disallowChanges()
buildReportsService?.also { reportsService ->
task.buildReportsService.value(reportsService).disallowChanges()
}
} }
task.systemPropertiesService.value(compilerSystemPropertiesService).disallowChanges() task.systemPropertiesService.value(compilerSystemPropertiesService).disallowChanges()
task.incrementalModuleInfoProvider.value(incrementalModuleInfoProvider).disallowChanges() task.incrementalModuleInfoProvider.value(incrementalModuleInfoProvider).disallowChanges()
@@ -115,8 +115,9 @@ internal fun getAllDependencies(dependency: ResolvedDependencyResult): Set<Resol
internal class GradleLoggerAdapter(private val gradleLogger: Logger) : org.jetbrains.kotlin.util.Logger { internal class GradleLoggerAdapter(private val gradleLogger: Logger) : org.jetbrains.kotlin.util.Logger {
override fun log(message: String) = gradleLogger.info(message) override fun log(message: String) = gradleLogger.info(message)
override fun warning(message: String) = gradleLogger.warn(message) override fun warning(message: String) = gradleLogger.warn(message)
override fun error(message: String) = kotlin.error(message) override fun error(message: String, throwable: Throwable?) = kotlin.error(message)
override fun fatal(message: String): Nothing = kotlin.error(message) override fun fatal(message: String): Nothing = kotlin.error(message)
override fun lifecycle(message: String) = gradleLogger.lifecycle(message)
} }
private fun libraryFilter(artifact: ResolvedArtifactResult): Boolean = artifact.file.absolutePath.endsWith(".klib") private fun libraryFilter(artifact: ResolvedArtifactResult): Boolean = artifact.file.absolutePath.endsWith(".klib")
@@ -64,21 +64,3 @@ internal fun formatContentLength(bytes: Long): String = when {
} }
} }
} }
internal fun formatTime(ms: Long): String {
val seconds = ms.toDouble() / 1_000
return seconds.asString(2) + " s"
}
private const val kbSize = 1024
private const val mbSize = kbSize * 1024
private const val gbSize = mbSize * 1024
internal fun formatSize(sizeInBytes: Long): String = when {
sizeInBytes / gbSize >= 1 -> "${(sizeInBytes.toDouble() / gbSize).asString(1)} GB"
sizeInBytes / mbSize >= 1 -> "${(sizeInBytes.toDouble() / mbSize).asString(1)} MB"
sizeInBytes / kbSize >= 1 -> "${(sizeInBytes.toDouble() / kbSize).asString(1)} KB"
else -> "$sizeInBytes B"
}
internal fun Double.asString(decPoints: Int): String = "%,.${decPoints}f".format(this)
@@ -42,7 +42,7 @@ fun Project.probeRemoteFileLength(url: String, probingTimeoutMs: Int = 0): Long?
else { else {
logger.kotlinDebug(::probeRemoteFileLength.name + "($url, $probingTimeoutMs): Failed to obtain content-length during the probing timeout.") logger.kotlinDebug(::probeRemoteFileLength.name + "($url, $probingTimeoutMs): Failed to obtain content-length during the probing timeout.")
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
null as Long? null
} }
} finally { } finally {
connection.disconnect() connection.disconnect()
@@ -11,10 +11,7 @@ import org.gradle.tooling.events.task.TaskFinishEvent
import org.gradle.tooling.events.task.TaskOperationDescriptor import org.gradle.tooling.events.task.TaskOperationDescriptor
import org.gradle.tooling.events.task.TaskOperationResult import org.gradle.tooling.events.task.TaskOperationResult
import org.jetbrains.kotlin.build.report.metrics.* import org.jetbrains.kotlin.build.report.metrics.*
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults import org.jetbrains.kotlin.build.report.statistic.StatTag
import org.jetbrains.kotlin.gradle.plugin.stat.StatTag
import org.jetbrains.kotlin.gradle.report.TaskExecutionInfo
import org.jetbrains.kotlin.gradle.report.TaskExecutionResult
import org.jetbrains.kotlin.gradle.report.TaskRecord import org.jetbrains.kotlin.gradle.report.TaskRecord
import org.jetbrains.kotlin.gradle.report.prepareData import org.jetbrains.kotlin.gradle.report.prepareData
import org.junit.Ignore import org.junit.Ignore
@@ -32,11 +29,6 @@ class ReportDataTest {
@Test @Test
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
fun testTags() { fun testTags() {
TaskExecutionResults[kotlinTaskPath] = TaskExecutionResult(
buildMetrics = BuildMetrics(buildAttributes = BuildAttributes()),
taskInfo = TaskExecutionInfo(kotlinLanguageVersion = KotlinVersion.KOTLIN_1_4),
icLogLines = emptyList(),
)
val buildOperationRecord = val buildOperationRecord =
taskRecord(BuildMetrics(buildAttributes = BuildAttributes().also { it.add(BuildAttribute.CLASSPATH_SNAPSHOT_NOT_FOUND) })) taskRecord(BuildMetrics(buildAttributes = BuildAttributes().also { it.add(BuildAttribute.CLASSPATH_SNAPSHOT_NOT_FOUND) }))
val statisticData = prepareData( val statisticData = prepareData(
@@ -45,8 +37,9 @@ class ReportDataTest {
uuid = "uuid", uuid = "uuid",
label = "label", label = "label",
kotlinVersion = "version", kotlinVersion = "version",
buildOperationRecords = listOf(buildOperationRecord), onlyKotlinTask = true,
additionalTags = listOf(StatTag.KOTLIN_DEBUG) buildOperationRecord = buildOperationRecord,
additionalTags = setOf(StatTag.KOTLIN_DEBUG)
) )
assertNotNull(statisticData) assertNotNull(statisticData)
@@ -57,41 +50,34 @@ class ReportDataTest {
private fun taskRecord(buildMetrics: BuildMetrics) = TaskRecord( private fun taskRecord(buildMetrics: BuildMetrics) = TaskRecord(
path = kotlinTaskPath, path = kotlinTaskPath,
classFqName = "class name", classFqName = "org.jetbrains.kotlin.TestTask",
startTimeMs = 10, startTimeMs = 10,
totalTimeMs = 20, totalTimeMs = 20,
buildMetrics = buildMetrics, buildMetrics = buildMetrics,
didWork = true, didWork = true,
skipMessage = null, skipMessage = null,
icLogLines = emptyList(), icLogLines = emptyList(),
kotlinLanguageVersion = KotlinVersion.KOTLIN_1_8 kotlinLanguageVersion = KotlinVersion.KOTLIN_1_8,
changedFiles = null,
compilerArguments = emptyArray(),
statTags = emptySet()
) )
@Test @Test
fun testMetricFilter() { fun testMetricFilter() {
TaskExecutionResults["testKotlin"] = TaskExecutionResult(
buildMetrics = BuildMetrics(
buildPerformanceMetrics = BuildPerformanceMetrics().also {
it.add(BuildPerformanceMetric.BUNDLE_SIZE)
it.add(BuildPerformanceMetric.CACHE_DIRECTORY_SIZE)
},
buildTimes = BuildTimes().also {
it.addTimeMs(BuildTime.RESTORE_OUTPUT_FROM_BACKUP, 10)
it.addTimeMs(BuildTime.IC_ANALYZE_JAR_FILES, 10)
}
),
taskInfo = TaskExecutionInfo(),
icLogLines = emptyList()
)
val buildOperationRecord = taskRecord( val buildOperationRecord = taskRecord(
BuildMetrics( BuildMetrics(
buildPerformanceMetrics = BuildPerformanceMetrics().also { buildPerformanceMetrics = BuildPerformanceMetrics().also {
it.add(BuildPerformanceMetric.COMPILE_ITERATION) it.add(BuildPerformanceMetric.COMPILE_ITERATION)
it.add(BuildPerformanceMetric.CLASSPATH_ENTRY_COUNT) it.add(BuildPerformanceMetric.CLASSPATH_ENTRY_COUNT)
it.add(BuildPerformanceMetric.BUNDLE_SIZE)
it.add(BuildPerformanceMetric.CACHE_DIRECTORY_SIZE)
}, },
buildTimes = BuildTimes().also { buildTimes = BuildTimes().also {
it.addTimeMs(BuildTime.STORE_BUILD_INFO, 20) it.addTimeMs(BuildTime.STORE_BUILD_INFO, 20)
it.addTimeMs(BuildTime.GRADLE_TASK_ACTION, 100) it.addTimeMs(BuildTime.GRADLE_TASK_ACTION, 100)
it.addTimeMs(BuildTime.RESTORE_OUTPUT_FROM_BACKUP, 10)
it.addTimeMs(BuildTime.IC_ANALYZE_JAR_FILES, 10)
} }
) )
) )
@@ -102,8 +88,9 @@ class ReportDataTest {
uuid = "uuid", uuid = "uuid",
label = "label", label = "label",
kotlinVersion = "version", kotlinVersion = "version",
buildOperationRecords = listOf(buildOperationRecord), buildOperationRecord = buildOperationRecord,
additionalTags = listOf(StatTag.KOTLIN_DEBUG), onlyKotlinTask = true,
additionalTags = setOf(StatTag.KOTLIN_DEBUG),
metricsToShow = setOf( metricsToShow = setOf(
BuildPerformanceMetric.BUNDLE_SIZE.name,// from TaskExecutionResult BuildPerformanceMetric.BUNDLE_SIZE.name,// from TaskExecutionResult
BuildTime.GRADLE_TASK_ACTION.name,// from buildOperationRecord BuildTime.GRADLE_TASK_ACTION.name,// from buildOperationRecord
@@ -132,19 +119,11 @@ class ReportDataTest {
val startWorker = 110L val startWorker = 110L
val finishGradleTask = System.nanoTime() val finishGradleTask = System.nanoTime()
TaskExecutionResults["testKotlin"] = TaskExecutionResult(
buildMetrics = BuildMetrics(
buildPerformanceMetrics = BuildPerformanceMetrics().also {
it.add(BuildPerformanceMetric.FINISH_KOTLIN_DAEMON_EXECUTION, System.currentTimeMillis())
it.add(BuildPerformanceMetric.START_WORKER_EXECUTION, TimeUnit.MILLISECONDS.toNanos(startWorker))
}
),
taskInfo = TaskExecutionInfo(),
icLogLines = emptyList()
)
val buildOperationRecord = taskRecord( val buildOperationRecord = taskRecord(
BuildMetrics( BuildMetrics(
buildPerformanceMetrics = BuildPerformanceMetrics().also { buildPerformanceMetrics = BuildPerformanceMetrics().also {
it.add(BuildPerformanceMetric.FINISH_KOTLIN_DAEMON_EXECUTION, System.currentTimeMillis())
it.add(BuildPerformanceMetric.START_WORKER_EXECUTION, TimeUnit.MILLISECONDS.toNanos(startWorker))
it.add(BuildPerformanceMetric.START_TASK_ACTION_EXECUTION, startTaskAction) it.add(BuildPerformanceMetric.START_TASK_ACTION_EXECUTION, startTaskAction)
it.add(BuildPerformanceMetric.CALL_WORKER, TimeUnit.MILLISECONDS.toNanos(callWorker)) it.add(BuildPerformanceMetric.CALL_WORKER, TimeUnit.MILLISECONDS.toNanos(callWorker))
} }
@@ -157,8 +136,9 @@ class ReportDataTest {
uuid = "uuid", uuid = "uuid",
label = "label", label = "label",
kotlinVersion = "version", kotlinVersion = "version",
buildOperationRecords = listOf(buildOperationRecord), buildOperationRecord = buildOperationRecord,
additionalTags = listOf(StatTag.KOTLIN_DEBUG), onlyKotlinTask = true,
additionalTags = setOf(StatTag.KOTLIN_DEBUG),
) )
assertNotNull(statisticData) assertNotNull(statisticData)
assertEquals(startTaskAction - startGradleTask, statisticData.buildTimesMetrics[BuildTime.GRADLE_TASK_PREPARATION]) assertEquals(startTaskAction - startGradleTask, statisticData.buildTimesMetrics[BuildTime.GRADLE_TASK_PREPARATION])
@@ -20,13 +20,15 @@ internal class CliLoggerAdapter(
override fun warning(message: String) = printlnIndented("Warning: $message", *CommonizerLogLevel.values()) override fun warning(message: String) = printlnIndented("Warning: $message", *CommonizerLogLevel.values())
override fun error(message: String) = fatal(message) override fun error(message: String, throwable: Throwable?) = fatal(message)
override fun fatal(message: String): Nothing { override fun fatal(message: String): Nothing {
printlnIndented("Error: $message\n", *CommonizerLogLevel.values()) printlnIndented("Error: $message\n", *CommonizerLogLevel.values())
exitProcess(1) exitProcess(1)
} }
override fun lifecycle(message: String) = log(message)
private fun printlnIndented(text: String, vararg levels: CommonizerLogLevel) { private fun printlnIndented(text: String, vararg levels: CommonizerLogLevel) {
if (level in levels) { if (level in levels) {
if (indent.isEmpty()) println(text) if (indent.isEmpty()) println(text)
+1
View File
@@ -214,6 +214,7 @@ dependencies {
fatJarContents(commonDependency("org.lz4:lz4-java")) { isTransitive = false } fatJarContents(commonDependency("org.lz4:lz4-java")) { isTransitive = false }
fatJarContents(commonDependency("org.jetbrains.intellij.deps:asm-all")) { isTransitive = false } fatJarContents(commonDependency("org.jetbrains.intellij.deps:asm-all")) { isTransitive = false }
fatJarContents(commonDependency("com.google.guava:guava")) { isTransitive = false } fatJarContents(commonDependency("com.google.guava:guava")) { isTransitive = false }
fatJarContents(commonDependency("com.google.code.gson:gson")) { isTransitive = false}
fatJarContentsStripServices(commonDependency("com.fasterxml:aalto-xml")) { isTransitive = false } fatJarContentsStripServices(commonDependency("com.fasterxml:aalto-xml")) { isTransitive = false }
fatJarContents(commonDependency("org.codehaus.woodstox:stax2-api")) { isTransitive = false } fatJarContents(commonDependency("org.codehaus.woodstox:stax2-api")) { isTransitive = false }
+2
View File
@@ -205,6 +205,7 @@ include ":kotlin-imports-dumper-compiler-plugin",
":kotlin-gradle-plugin-test-utils-embeddable", ":kotlin-gradle-plugin-test-utils-embeddable",
":kotlin-gradle-plugin-integration-tests", ":kotlin-gradle-plugin-integration-tests",
":kotlin-gradle-plugins-bom", ":kotlin-gradle-plugins-bom",
":kotlin-build-statistic",
":gradle:android-test-fixes", ":gradle:android-test-fixes",
":gradle:gradle-warnings-detector", ":gradle:gradle-warnings-detector",
":gradle:kotlin-compiler-args-properties", ":gradle:kotlin-compiler-args-properties",
@@ -807,6 +808,7 @@ project(':kotlin-scripting-ide-services-unshaded').projectDir = "$rootDir/plugin
project(':kotlin-scripting-ide-services-test').projectDir = "$rootDir/plugins/scripting/scripting-ide-services-test" as File project(':kotlin-scripting-ide-services-test').projectDir = "$rootDir/plugins/scripting/scripting-ide-services-test" as File
project(':kotlin-scripting-ide-services').projectDir = "$rootDir/plugins/scripting/scripting-ide-services-embeddable" as File project(':kotlin-scripting-ide-services').projectDir = "$rootDir/plugins/scripting/scripting-ide-services-embeddable" as File
project(':kotlin-scripting-ide-common').projectDir = "$rootDir/plugins/scripting/scripting-ide-common" as File project(':kotlin-scripting-ide-common').projectDir = "$rootDir/plugins/scripting/scripting-ide-common" as File
project(':kotlin-build-statistic').projectDir ="$rootDir/kotlin-build-statistic" as File
// Uncomment to use locally built protobuf-relocated // Uncomment to use locally built protobuf-relocated
// includeBuild("dependencies/protobuf") // includeBuild("dependencies/protobuf")