Add custom_values_limit property to configure build scan report
#KT-53732 Fixed
This commit is contained in:
+32
-1
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+17
@@ -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
@@ -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 {
|
||||
|
||||
-1
@@ -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
|
||||
|
||||
+3
@@ -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
|
||||
|
||||
|
||||
-127
@@ -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
|
||||
}
|
||||
}
|
||||
-132
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+249
-40
@@ -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
|
||||
;
|
||||
}
|
||||
|
||||
+12
@@ -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)
|
||||
}
|
||||
+9
@@ -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
|
||||
}
|
||||
}
|
||||
+8
-1
@@ -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,
|
||||
)
|
||||
|
||||
+4
-4
@@ -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())
|
||||
|
||||
Reference in New Issue
Block a user