Add custom_values_limit property to configure build scan report

#KT-53732 Fixed
This commit is contained in:
nataliya.valtman
2022-07-28 19:31:07 +02:00
committed by Space
parent 214586c8ee
commit 30dd848b43
12 changed files with 334 additions and 311 deletions
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.gradle
import org.gradle.api.logging.LogLevel
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.internal.build.metrics.GradleBuildMetricsData
import org.jetbrains.kotlin.gradle.report.BuildReportType
@@ -99,7 +100,8 @@ class BuildReportsIT : KGPBaseTest() {
val metricsFile = projectPath.resolve("metrics.bin").toFile()
build(
"compileKotlin",
"-Pkotlin.internal.single.build.metrics.file=${metricsFile.absolutePath}"
"-Pkotlin.build.report.output=SINGLE_FILE",
"-Pkotlin.build.report.single_file=${metricsFile.absolutePath}"
)
assertTrue { metricsFile.exists() }
@@ -109,4 +111,33 @@ class BuildReportsIT : KGPBaseTest() {
}
}
}
@DisplayName("custom value limit")
@GradleTest
fun testCustomValueLimitForBuildScan(gradleVersion: GradleVersion) {
project("simpleProject", gradleVersion, buildOptions = defaultBuildOptions.copy(logLevel = LogLevel.DEBUG)) {
build(
"compileKotlin",
"-Pkotlin.build.report.output=BUILD_SCAN",
"-Pkotlin.build.report.build_scan.custom_values_limit=0",
"--scan"
) {
assertOutputContains("Can't add any more custom values into build scan")
}
}
}
@DisplayName("build scan listener lazy initialisation")
@GradleTest
fun testBuildScanListenerLazyInitialisation(gradleVersion: GradleVersion) {
project("simpleProject", gradleVersion, buildOptions = defaultBuildOptions.copy(logLevel = LogLevel.DEBUG)) {
build(
"compileKotlin",
"-Pkotlin.build.report.output=BUILD_SCAN",
"-Pkotlin.build.report.build_scan.custom_values_limit=0",
) {
assertOutputDoesNotContain("Can't add any more custom values into build scan")
}
}
}
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.gradle
import org.gradle.api.logging.LogLevel
import org.gradle.api.logging.configuration.WarningMode
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.report.BuildReportType
@@ -252,6 +253,22 @@ class ConfigurationCacheIT : AbstractConfigurationCacheIT() {
}
}
}
@JvmGradlePluginTests
@DisplayName("with build build scan report")
@GradleTest
fun testBuildScanReportSmokeTestForConfigurationCache(gradleVersion: GradleVersion) {
project("simpleProject", gradleVersion) {
val buildOptions = defaultBuildOptions.copy(buildReport = listOf(BuildReportType.BUILD_SCAN), logLevel = LogLevel.DEBUG)
build("clean", "assemble", "-Pkotlin.build.report.build_scan.custom_values_limit=0", "--scan", buildOptions = buildOptions) {
assertOutputContains("Can't add any more custom values into build scan")
}
build("clean", "assemble", "-Pkotlin.build.report.build_scan.custom_values_limit=0", "--scan", buildOptions = buildOptions) {
assertOutputContains("Can't add any more custom values into build scan")
}
}
}
}
abstract class AbstractConfigurationCacheIT : KGPBaseTest() {
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.gradle.plugin
import com.gradle.scan.plugin.BuildScanExtension
import org.gradle.api.Project
import org.gradle.api.logging.Logging
import org.gradle.api.provider.Provider
@@ -14,10 +13,6 @@ import org.gradle.api.services.BuildServiceParameters
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.plugin.statistics.BuildScanStatisticsListener
import org.jetbrains.kotlin.gradle.report.BuildReportType
import org.jetbrains.kotlin.gradle.report.ReportingSettings
import org.jetbrains.kotlin.gradle.report.reportingSettings
import java.io.File
internal abstract class KotlinGradleBuildServices : BuildService<KotlinGradleBuildServices.Parameters>, AutoCloseable {
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.internal.KOTLIN_COMPILER_EMBEDDABLE
import org.jetbrains.kotlin.gradle.internal.KOTLIN_MODULE_GROUP
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformAndroidGradlePluginCompatibilityHealthCheck.runMultiplatformAndroidGradlePluginCompatibilityHealthCheck
import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformAndroidGradlePluginCompatibilityHealthCheck.runMultiplatformAndroidGradlePluginCompatibilityHealthCheckWhenAndroidIsApplied
import org.jetbrains.kotlin.gradle.plugin.internal.*
import org.jetbrains.kotlin.gradle.plugin.internal.BasePluginConfiguration
@@ -97,6 +97,9 @@ internal class PropertiesProvider private constructor(private val project: Proje
val buildReportHttpPassword: String?
get() = this.property("kotlin.build.report.http.password")
val buildReportBuildScanCustomValuesLimit: Int
get() = property("kotlin.build.report.build_scan.custom_values_limit")?.toInt() ?: 1000
val buildReportMetrics: Boolean
get() = booleanProperty("kotlin.build.report.metrics") ?: false
@@ -1,127 +0,0 @@
/*
* Copyright 2010-2021 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.plugin.statistics
import com.gradle.scan.plugin.BuildScanExtension
import org.gradle.api.logging.Logging
import org.gradle.tooling.events.FinishEvent
import org.gradle.tooling.events.OperationCompletionListener
import org.gradle.tooling.events.task.TaskFinishEvent
import org.jetbrains.kotlin.build.report.metrics.SizeMetricType
import org.jetbrains.kotlin.gradle.plugin.stat.CompileStatisticsData
import org.jetbrains.kotlin.gradle.utils.formatSize
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.utils.addToStdlib.measureTimeMillisWithResult
import java.util.*
import kotlin.system.measureTimeMillis
class BuildScanStatisticsListener(
private val buildScan: BuildScanExtension,
val projectName: String,
val label: String?,
val kotlinVersion: String,
val buildUuid: String
) : OperationCompletionListener, AutoCloseable {
companion object {
const val lengthLimit = 100_000
const val customValuesLimit = 950 //git plugin and others can add custom values as well
}
private val tags = LinkedHashSet<String>()
private val log = Logging.getLogger(this.javaClass)
private var customValues = 0 // doesn't need to be thread-safe
override fun onFinish(event: FinishEvent?) {
if (event is TaskFinishEvent) {
val (collectDataDuration, compileStatData) = measureTimeMillisWithResult {
KotlinBuildStatListener.prepareData(event, projectName, buildUuid, label, kotlinVersion)
}
log.debug("Collect data takes $collectDataDuration: $compileStatData")
compileStatData?.also {
report(it)
}
}
}
override fun close() {
}
fun report(data: CompileStatisticsData) {
val elapsedTime = measureTimeMillis {
if (!tags.contains(buildUuid)) {
tags.add(buildUuid)
}
data.label?.takeIf { !tags.contains(it) }?.also {
buildScan.tag(it)
tags.add(it)
}
data.tags
.filter { !tags.contains(it) }
.forEach {
buildScan.tag(it)
tags.add(it)
}
if (customValues < customValuesLimit) {
readableString(data).forEach {
if (customValues < customValuesLimit) {
buildScan.value(data.taskName, it)
customValues++
} else {
log.debug("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.")
}
}
log.debug("Report statistic to build scan takes $elapsedTime ms")
}
private fun readableString(data: CompileStatisticsData): List<String> {
val readableString = StringBuilder()
if (data.nonIncrementalAttributes.isEmpty()) {
readableString.append("Incremental build; ")
data.changes.joinTo(readableString, prefix = "Changes: [", postfix = "]; ") { it.substringAfterLast(File.separator) }
} else {
data.nonIncrementalAttributes.joinTo(readableString, prefix = "Non incremental build because: [", postfix = "]; ") { it.readableString }
}
val timeData =
data.buildTimesMetrics.map { (key, value) -> "${key.readableString}: ${value}ms" } //sometimes it is better to have separate variable to be able debug
val perfData = data.performanceMetrics.map { (key, value) ->
when (key.type) {
SizeMetricType.BYTES -> "${key.readableString}: ${formatSize(value)}"
else -> "${key.readableString}: $value}"
}
}
timeData.union(perfData).joinTo(readableString, ",", "Performance: [", "]")
return splitStringIfNeed(readableString.toString(), lengthLimit)
}
private fun splitStringIfNeed(str: String, lengthLimit: Int): List<String> {
val splattedString = ArrayList<String>()
var tempStr = str
while (tempStr.length > lengthLimit) {
val subSequence = tempStr.substring(lengthLimit)
var index = subSequence.lastIndexOf(';')
if (index == -1) {
index = subSequence.lastIndexOf(',')
if (index == -1) {
index = lengthLimit
}
}
splattedString.add(tempStr.substring(index))
tempStr = tempStr.substring(index)
}
splattedString.add(tempStr)
return splattedString
}
}
@@ -1,132 +0,0 @@
/*
* Copyright 2010-2021 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.plugin.statistics
import org.gradle.tooling.events.task.TaskFailureResult
import org.gradle.tooling.events.task.TaskFinishEvent
import org.gradle.tooling.events.task.TaskSkippedResult
import org.gradle.tooling.events.task.TaskSuccessResult
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults
import org.jetbrains.kotlin.gradle.plugin.stat.CompileStatisticsData
import org.jetbrains.kotlin.gradle.plugin.stat.StatTag
import org.jetbrains.kotlin.gradle.report.TaskExecutionResult
import org.jetbrains.kotlin.incremental.ChangedFiles
import org.jetbrains.kotlin.utils.addToStdlib.ifTrue
import java.lang.management.ManagementFactory
import java.net.InetAddress
import java.util.*
enum class TaskExecutionState {
SKIPPED,
FAILED,
UNKNOWN,
SUCCESS,
FROM_CACHE,
UP_TO_DATE
;
}
class KotlinBuildStatListener {
companion object {
val hostName: String? = try {
InetAddress.getLocalHost().hostName
} catch (_: Exception) {
//do nothing
null
}
private fun availableForStat(taskPath: String): Boolean {
return taskPath.contains("Kotlin") && (TaskExecutionResults[taskPath] != null)
}
internal fun prepareData(
event: TaskFinishEvent,
projectName: String,
uuid: String,
label: String?,
kotlinVersion: String,
additionalTags: List<StatTag> = emptyList()
): CompileStatisticsData? {
val result = event.result
val taskPath = event.descriptor.taskPath
val durationMs = result.endTime - result.startTime
val taskResult = when (result) {
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
}
if (!availableForStat(taskPath)) {
return null
}
val taskExecutionResult = TaskExecutionResults[taskPath]
val buildTimesMs = taskExecutionResult?.buildMetrics?.buildTimes?.asMapMs()?.filterValues { value -> value != 0L } ?: emptyMap()
val perfData =
taskExecutionResult?.buildMetrics?.buildPerformanceMetrics?.asMap()?.filterValues { value -> value != 0L } ?: emptyMap()
val changes = when (val changedFiles = taskExecutionResult?.taskInfo?.changedFiles) {
is ChangedFiles.Known -> changedFiles.modified.map { it.absolutePath } + changedFiles.removed.map { it.absolutePath }
is ChangedFiles.Dependencies -> changedFiles.modified.map { it.absolutePath } + changedFiles.removed.map { it.absolutePath }
else -> emptyList<String>()
}
return CompileStatisticsData(
durationMs = durationMs,
taskResult = taskResult.name,
label = label,
buildTimesMetrics = buildTimesMs,
performanceMetrics = perfData,
projectName = projectName,
taskName = taskPath,
changes = changes,
tags = parseTags(taskExecutionResult, additionalTags).map { it.name },
nonIncrementalAttributes = taskExecutionResult?.buildMetrics?.buildAttributes?.asMap()?.filter { it.value > 0 }?.keys ?: emptySet(),
hostName = hostName,
kotlinVersion = kotlinVersion,
buildUuid = uuid,
finishTime = System.currentTimeMillis(),
compilerArguments = taskExecutionResult?.taskInfo?.compilerArguments?.asList() ?: emptyList()
)
}
private fun parseTags(taskExecutionResult: TaskExecutionResult?, additionalTags: List<StatTag>): List<StatTag> {
val tags = ArrayList(additionalTags)
val nonIncrementalAttributes = taskExecutionResult?.buildMetrics?.buildAttributes?.asMap() ?: emptyMap()
if (nonIncrementalAttributes.isEmpty()) {
tags.add(StatTag.INCREMENTAL)
} else {
tags.add(StatTag.NON_INCREMENTAL)
}
val taskInfo = taskExecutionResult?.taskInfo
taskInfo?.withAbiSnapshot?.ifTrue {
tags.add(StatTag.ABI_SNAPSHOT)
}
taskInfo?.withArtifactTransform?.ifTrue {
tags.add(StatTag.ARTIFACT_TRANSFORM)
}
val debugConfiguration = "-agentlib:"
if (ManagementFactory.getRuntimeMXBean().inputArguments.firstOrNull { it.startsWith(debugConfiguration) } != null) {
tags.add(StatTag.GRADLE_DEBUG)
}
return tags
}
}
}
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.gradle.report
import com.google.gson.Gson
import com.gradle.scan.plugin.BuildScanExtension
import org.gradle.api.Project
import org.gradle.api.invocation.Gradle
import org.gradle.api.logging.Logging
@@ -17,24 +16,34 @@ import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
import org.gradle.tooling.events.FinishEvent
import org.gradle.tooling.events.OperationCompletionListener
import org.gradle.tooling.events.task.TaskFailureResult
import org.gradle.tooling.events.task.TaskFinishEvent
import org.gradle.tooling.events.task.TaskSkippedResult
import org.gradle.tooling.events.task.TaskSuccessResult
import org.jetbrains.kotlin.build.report.metrics.SizeMetricType
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.stat.BuildFinishStatisticsData
import org.jetbrains.kotlin.gradle.plugin.stat.CompileStatisticsData
import org.jetbrains.kotlin.gradle.plugin.stat.GradleBuildStartParameters
import org.jetbrains.kotlin.gradle.plugin.stat.StatTag
import org.jetbrains.kotlin.gradle.plugin.statistics.BuildScanStatisticsListener
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatListener
import org.jetbrains.kotlin.gradle.report.data.BuildExecutionData
import org.jetbrains.kotlin.gradle.utils.formatSize
import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable
import org.jetbrains.kotlin.incremental.ChangedFiles
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.utils.addToStdlib.ifTrue
import org.jetbrains.kotlin.utils.addToStdlib.measureTimeMillisWithResult
import java.io.IOException
import java.lang.management.ManagementFactory
import java.net.HttpURLConnection
import java.net.InetAddress
import java.net.URL
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import kotlin.system.measureTimeMillis
abstract class BuildReportsService : BuildService<BuildReportsService.Parameters>, AutoCloseable, OperationCompletionListener {
@@ -45,8 +54,8 @@ abstract class BuildReportsService : BuildService<BuildReportsService.Parameters
private val buildUuid = UUID.randomUUID().toString()
private var executorService: ExecutorService = Executors.newSingleThreadExecutor()
private var buildMetricsService: Provider<BuildMetricsService>? = null
private val tags = LinkedHashSet<String>()
private var customValues = 0 // doesn't need to be thread-safe
init {
log.info("Build report service is registered. Unique build id: $buildUuid")
@@ -112,35 +121,11 @@ abstract class BuildReportsService : BuildService<BuildReportsService.Parameters
sendDataViaHttp(buildFinishData)
}
private fun initService(project: Project) {
addListeners(project)
}
private fun addBuildScanReport(project: Project) {
if (parameters.reportingSettings.buildReportOutputs.contains(BuildReportType.BUILD_SCAN)) {
val listenerRegistryHolder = BuildEventsListenerRegistryHolder.getInstance(project)
project.rootProject.extensions.findByName("buildScan")
?.also {
listenerRegistryHolder.listenerRegistry.onTaskCompletion(
project.provider {
BuildScanStatisticsListener(
it as BuildScanExtension,
parameters.projectName.get(),
parameters.label.orNull,
parameters.kotlinVersion.get(),
buildUuid
)
}
)
}
}
}
private fun addHttpReport(event: FinishEvent?) {
if (parameters.reportingSettings.get().httpReportSettings != null) {
if (event is TaskFinishEvent) {
val data =
KotlinBuildStatListener.prepareData(
prepareData(
event,
parameters.projectName.get(),
buildUuid,
@@ -154,10 +139,6 @@ abstract class BuildReportsService : BuildService<BuildReportsService.Parameters
}
private fun addListeners(project: Project) {
addBuildScanReport(project)
}
private var invalidUrl = false
private var requestPreviousFailed = false
private fun sendDataViaHttp(data: Any) {
@@ -213,8 +194,123 @@ abstract class BuildReportsService : BuildService<BuildReportsService.Parameters
}
}
private fun addBuildScanReport(event: FinishEvent?, buildScan: BuildScanExtensionHolder) {
val buildScanSettings = parameters.reportingSettings.orNull?.buildScanReportSettings
if (buildScanSettings != null && buildScan.buildScan != null) {
if (event is TaskFinishEvent) {
val (collectDataDuration, compileStatData) = measureTimeMillisWithResult {
prepareData(event, parameters.projectName.get(), buildUuid, parameters.label.orNull, parameters.kotlinVersion.get())
}
log.debug("Collect data takes $collectDataDuration: $compileStatData")
compileStatData?.also {
addBuildScanReport(it, buildScanSettings.customValueLimit, buildScan)
}
}
}
}
private fun addBuildScanReport(data: CompileStatisticsData, customValuesLimit: Int, buildScan: BuildScanExtensionHolder) {
val elapsedTime = measureTimeMillis {
buildScan.buildScan?.also {
if (!tags.contains(buildUuid)) {
addBuildScanTag(buildScan, buildUuid)
}
data.label?.takeIf { !tags.contains(it) }?.also {
addBuildScanTag(buildScan, it)
}
data.tags
.filter { !tags.contains(it) }
.forEach {
addBuildScanTag(buildScan, it)
}
if (customValues < customValuesLimit) {
readableString(data).forEach {
if (customValues < customValuesLimit) {
addBuildScanValue(buildScan, data, it)
} else {
log.debug(
"Can't add any more custom values into build scan." +
" 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.")
}
}
}
log.debug("Report statistic to build scan takes $elapsedTime ms")
}
private fun addBuildScanValue(
buildScan: BuildScanExtensionHolder,
data: CompileStatisticsData,
customValue: String
) {
buildScan.buildScan?.value(data.taskName, customValue)
customValues++
}
private fun addBuildScanTag(buildScan: BuildScanExtensionHolder, tag: String) {
buildScan.buildScan?.tag(tag)
tags.add(tag)
}
private fun readableString(data: CompileStatisticsData): List<String> {
val readableString = StringBuilder()
if (data.nonIncrementalAttributes.isEmpty()) {
readableString.append("Incremental build; ")
data.changes.joinTo(readableString, prefix = "Changes: [", postfix = "]; ") { it.substringAfterLast(File.separator) }
} else {
data.nonIncrementalAttributes.joinTo(
readableString,
prefix = "Non incremental build because: [",
postfix = "]; "
) { it.readableString }
}
val timeData =
data.buildTimesMetrics.map { (key, value) -> "${key.readableString}: ${value}ms" } //sometimes it is better to have separate variable to be able debug
val perfData = data.performanceMetrics.map { (key, value) ->
when (key.type) {
SizeMetricType.BYTES -> "${key.readableString}: ${formatSize(value)}"
else -> "${key.readableString}: $value}"
}
}
timeData.union(perfData).joinTo(readableString, ",", "Performance: [", "]")
return splitStringIfNeed(readableString.toString(), lengthLimit)
}
private fun splitStringIfNeed(str: String, lengthLimit: Int): List<String> {
val splattedString = ArrayList<String>()
var tempStr = str
while (tempStr.length > lengthLimit) {
val subSequence = tempStr.substring(lengthLimit)
var index = subSequence.lastIndexOf(';')
if (index == -1) {
index = subSequence.lastIndexOf(',')
if (index == -1) {
index = lengthLimit
}
}
splattedString.add(tempStr.substring(index))
tempStr = tempStr.substring(index)
}
splattedString.add(tempStr)
return splattedString
}
companion object {
private val log = Logging.getLogger(this.javaClass)
const val lengthLimit = 100_000
fun getStartParameters(project: Project) = project.gradle.startParameter.let {
GradleBuildStartParameters(
tasks = it.taskRequests.flatMap { it.args },
@@ -251,12 +347,19 @@ abstract class BuildReportsService : BuildService<BuildReportsService.Parameters
//init gradle tags for build scan and http reports
it.parameters.additionalTags.value(setupTags(gradle))
}!!.also {
val buildReportsService = it.get()
buildReportsService.initService(project)
if (buildReportsService.parameters.reportingSettings.get().buildReportOutputs.contains(BuildReportType.HTTP)) {
}.also {
if (reportingSettings.httpReportSettings != null) {
BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(it)
}
val buildScanExtension = project.rootProject.extensions.findByName("buildScan")
if (reportingSettings.buildScanReportSettings != null && buildScanExtension != null) {
BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(project.provider {
OperationCompletionListener { event ->
it.get().addBuildScanReport(event, BuildScanExtensionHolder(buildScanExtension))
}
})
}
}
}
@@ -270,6 +373,112 @@ abstract class BuildReportsService : BuildService<BuildReportsService.Parameters
}
return additionalTags
}
internal fun prepareData(
event: TaskFinishEvent,
projectName: String,
uuid: String,
label: String?,
kotlinVersion: String,
additionalTags: List<StatTag> = emptyList()
): CompileStatisticsData? {
val result = event.result
val taskPath = event.descriptor.taskPath
val durationMs = result.endTime - result.startTime
val taskResult = when (result) {
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
}
if (!availableForStat(taskPath)) {
return null
}
val taskExecutionResult = TaskExecutionResults[taskPath]
val buildTimesMs = taskExecutionResult?.buildMetrics?.buildTimes?.asMapMs()?.filterValues { value -> value != 0L } ?: emptyMap()
val perfData =
taskExecutionResult?.buildMetrics?.buildPerformanceMetrics?.asMap()?.filterValues { value -> value != 0L } ?: emptyMap()
val changes = when (val changedFiles = taskExecutionResult?.taskInfo?.changedFiles) {
is ChangedFiles.Known -> changedFiles.modified.map { it.absolutePath } + changedFiles.removed.map { it.absolutePath }
is ChangedFiles.Dependencies -> changedFiles.modified.map { it.absolutePath } + changedFiles.removed.map { it.absolutePath }
else -> emptyList<String>()
}
return CompileStatisticsData(
durationMs = durationMs,
taskResult = taskResult.name,
label = label,
buildTimesMetrics = buildTimesMs,
performanceMetrics = perfData,
projectName = projectName,
taskName = taskPath,
changes = changes,
tags = parseTags(taskExecutionResult, additionalTags).map { it.name },
nonIncrementalAttributes = taskExecutionResult?.buildMetrics?.buildAttributes?.asMap()?.filter { it.value > 0 }?.keys
?: emptySet(),
hostName = hostName,
kotlinVersion = kotlinVersion,
buildUuid = uuid,
finishTime = System.currentTimeMillis(),
compilerArguments = taskExecutionResult?.taskInfo?.compilerArguments?.asList() ?: emptyList()
)
}
val hostName: String? = try {
InetAddress.getLocalHost().hostName
} catch (_: Exception) {
//do nothing
null
}
private fun availableForStat(taskPath: String): Boolean {
return taskPath.contains("Kotlin") && (TaskExecutionResults[taskPath] != null)
}
private fun parseTags(taskExecutionResult: TaskExecutionResult?, additionalTags: List<StatTag>): List<StatTag> {
val tags = ArrayList(additionalTags)
val nonIncrementalAttributes = taskExecutionResult?.buildMetrics?.buildAttributes?.asMap() ?: emptyMap()
if (nonIncrementalAttributes.isEmpty()) {
tags.add(StatTag.INCREMENTAL)
} else {
tags.add(StatTag.NON_INCREMENTAL)
}
val taskInfo = taskExecutionResult?.taskInfo
taskInfo?.withAbiSnapshot?.ifTrue {
tags.add(StatTag.ABI_SNAPSHOT)
}
taskInfo?.withArtifactTransform?.ifTrue {
tags.add(StatTag.ARTIFACT_TRANSFORM)
}
val debugConfiguration = "-agentlib:"
if (ManagementFactory.getRuntimeMXBean().inputArguments.firstOrNull { it.startsWith(debugConfiguration) } != null) {
tags.add(StatTag.GRADLE_DEBUG)
}
return tags
}
}
}
}
enum class TaskExecutionState {
SKIPPED,
FAILED,
UNKNOWN,
SUCCESS,
FROM_CACHE,
UP_TO_DATE
;
}
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2022 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 com.gradle.scan.plugin.BuildScanExtension
class BuildScanExtensionHolder(val buildScan: BuildScanExtension?) : java.io.Serializable {
constructor(extension: Any?) : this(extension as BuildScanExtension)
}
@@ -14,6 +14,7 @@ data class ReportingSettings(
val buildReportLabel: String? = null,
val fileReportSettings: FileReportSettings? = null,
val httpReportSettings: HttpReportSettings? = null,
val buildScanReportSettings: BuildScanSettings? = null,
val singleOutputFile: File? = null
) : Serializable {
companion object {
@@ -38,4 +39,12 @@ data class HttpReportSettings(
companion object {
const val serialVersionUID: Long = 0
}
}
data class BuildScanSettings(
val customValueLimit: Int
): Serializable {
companion object {
const val serialVersionUID: Long = 0
}
}
@@ -37,8 +37,14 @@ internal fun reportingSettings(rootProject: Project): ReportingSettings {
null
}
val buildScanSettings = if (buildReportOutputTypes.contains(BuildReportType.BUILD_SCAN)) {
BuildScanSettings(properties.buildReportBuildScanCustomValuesLimit)
} else {
null
}
//temporary solution. support old property
val oldSingleBuildMetric = properties.singleBuildMetricsFile?.also { buildReportOutputTypes.add(BuildReportType.SINGLE_FILE) }
val oldSingleBuildMetric = properties.singleBuildMetricsFile?.also { buildReportOutputTypes.add(BuildReportType.SINGLE_FILE) }
val singleOutputFile = if (buildReportOutputTypes.contains(BuildReportType.SINGLE_FILE)) {
properties.buildReportSingleFile ?: oldSingleBuildMetric
@@ -49,6 +55,7 @@ internal fun reportingSettings(rootProject: Project): ReportingSettings {
buildReportLabel = properties.buildReportLabel,
fileReportSettings = fileReportSettings,
httpReportSettings = httpReportSettings,
buildScanReportSettings = buildScanSettings,
buildReportOutputs = buildReportOutputTypes,
singleOutputFile = singleOutputFile,
)
@@ -59,13 +59,13 @@ internal abstract class AbstractKotlinCompileConfig<TASK : AbstractKotlinCompile
.disallowChanges()
task.localStateDirectories.from(task.taskBuildLocalStateDirectory).disallowChanges()
val buildMetricsService = BuildMetricsService.registerIfAbsent(project)?.also {
BuildMetricsService.registerIfAbsent(project)?.also {
task.buildMetricsService.value(it)
BuildReportsService.registerIfAbsent(project, it)?.also {
task.buildReportsService.value(it)
}
}
buildMetricsService?.let { BuildReportsService.registerIfAbsent(project, it) }
?.let { task.buildReportsService.value(it) }
propertiesProvider.kotlinDaemonJvmArgs?.let { kotlinDaemonJvmArgs ->
task.kotlinDaemonJvmArguments.set(providers.provider {
kotlinDaemonJvmArgs.split("\\s+".toRegex())