Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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.plugin.statistics
import org.gradle.api.Project
import org.gradle.api.Task
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.TaskFailureResult
import org.gradle.tooling.events.task.TaskFinishEvent
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.plugin.BuildEventsListenerRegistryHolder
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.StatisticsBuildFlowManager
import org.jetbrains.kotlin.gradle.plugin.internal.isConfigurationCacheEnabled
import org.jetbrains.kotlin.gradle.plugin.internal.isConfigurationCacheRequested
import org.jetbrains.kotlin.gradle.plugin.internal.isProjectIsolationEnabled
import org.jetbrains.kotlin.gradle.plugin.internal.isProjectIsolationRequested
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults
import org.jetbrains.kotlin.gradle.report.reportingSettings
import org.jetbrains.kotlin.gradle.tasks.withType
import org.jetbrains.kotlin.gradle.utils.SingleActionPerProject
import org.jetbrains.kotlin.gradle.utils.currentBuildId
import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics
import org.jetbrains.kotlin.statistics.metrics.StatisticsValuesConsumer
import org.jetbrains.kotlin.statistics.metrics.NumericalMetrics
import org.jetbrains.kotlin.statistics.metrics.StringMetrics
import java.io.Serializable
import java.util.UUID.*
internal interface UsesBuildFusService : Task {
@get:Internal
val buildFusService: Property
}
abstract class BuildFusService : BuildService, AutoCloseable, OperationCompletionListener {
private var buildFailed: Boolean = false
private val log = Logging.getLogger(this.javaClass)
private val buildId = randomUUID().toString()
init {
log.kotlinDebug("Initialize ${this.javaClass.simpleName}")
KotlinBuildStatsBeanService.recordBuildStart(buildId)
}
interface Parameters : BuildServiceParameters {
val generalConfigurationMetrics: Property
val configurationMetrics: ListProperty
val useBuildFinishFlowAction: Property
val buildStatisticsConfiguration: Property
}
private val fusMetricsConsumer = NonSynchronizedMetricsContainer()
internal fun getFusMetricsConsumer(): StatisticsValuesConsumer = fusMetricsConsumer
internal fun reportFusMetrics(reportAction: (StatisticsValuesConsumer) -> Unit) {
reportAction(fusMetricsConsumer)
}
private val projectEvaluatedTime: Long = System.currentTimeMillis()
companion object {
internal val serviceName = "${BuildFusService::class.simpleName}_${BuildFusService::class.java.classLoader.hashCode()}"
private var buildStartTime: Long = System.currentTimeMillis()
fun registerIfAbsent(project: Project, pluginVersion: String) =
registerIfAbsentImpl(project, pluginVersion).also { serviceProvider ->
SingleActionPerProject.run(project, UsesBuildFusService::class.java.name) {
project.tasks.withType().configureEach { task ->
task.buildFusService.value(serviceProvider).disallowChanges()
task.usesService(serviceProvider)
}
}
}
private fun registerIfAbsentImpl(
project: Project,
pluginVersion: String,
): Provider {
val isProjectIsolationEnabled = project.isProjectIsolationEnabled
val isConfigurationCacheRequested = project.isConfigurationCacheRequested
val isProjectIsolationRequested = project.isProjectIsolationRequested
project.gradle.sharedServices.registrations.findByName(serviceName)?.let {
@Suppress("UNCHECKED_CAST")
return (it.service as Provider)
}
//init buildStatsService
KotlinBuildStatsBeanService.initStatsService(project)
val buildReportOutputs = reportingSettings(project).buildReportOutputs
val useClasspathSnapshot = PropertiesProvider(project).useClasspathSnapshot
val gradle = project.gradle
//Workaround for known issues for Gradle 8+: https://github.com/gradle/gradle/issues/24887:
// when this OperationCompletionListener is called services can be already closed for Gradle 8,
// so there is a change that no VariantImplementationFactory will be found
return gradle.sharedServices.registerIfAbsent(serviceName, BuildFusService::class.java) { spec ->
spec.parameters.generalConfigurationMetrics.set(project.provider {
//isProjectIsolationEnabled isConfigurationCacheRequested and isProjectIsolationRequested should be calculated beforehand
// because since Gradle 8.0 provider's calculation is made in BuildFinishFlowAction
// and VariantImplementationFactories is not initialized at that moment
collectGeneralConfigurationTimeMetrics(
project,
gradle,
buildReportOutputs,
useClasspathSnapshot,
pluginVersion,
isProjectIsolationEnabled,
isProjectIsolationRequested,
isConfigurationCacheRequested
)
})
spec.parameters.useBuildFinishFlowAction.set(GradleVersion.current().baseVersion >= GradleVersion.version("8.1"))
spec.parameters.buildStatisticsConfiguration.set(KotlinBuildStatsConfiguration(project))
//init value to avoid `java.lang.IllegalStateException: GradleScopeServices has been closed` exception on close
spec.parameters.configurationMetrics.add(MetricContainer())
}.also { buildService ->
//DO NOT call buildService.get() before all parameters.configurationMetrics are set.
// buildService.get() call will cause parameters calculation and configuration cache storage.
//Gradle throws an exception when Gradle version less than 7.4 with configuration cache enabled and buildSrc,
@Suppress("DEPRECATION")
if (GradleVersion.current().baseVersion >= GradleVersion.version("7.4")
|| !project.isConfigurationCacheEnabled
|| project.currentBuildId().name != "buildSrc"
) {
BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(buildService)
}
if (GradleVersion.current().baseVersion >= GradleVersion.version("8.1")) {
StatisticsBuildFlowManager.getInstance(project).subscribeForBuildResult()
}
}
}
}
@Synchronized //access fusMetricsConsumer requires synchronisation as long as tasks are executed in parallel
override fun onFinish(event: FinishEvent?) {
if (event is TaskFinishEvent) {
if (event.result is TaskFailureResult) {
buildFailed = true
}
val taskExecutionResult = TaskExecutionResults[event.descriptor.taskPath]
taskExecutionResult?.also { KotlinTaskExecutionMetrics.collectMetrics(it, event, fusMetricsConsumer) }
}
ExecutedTaskMetrics.collectMetrics(event, fusMetricsConsumer)
}
override fun close() {
if (!parameters.useBuildFinishFlowAction.get()) {
recordBuildFinished(buildFailed)
}
KotlinBuildStatsBeanService.closeServices()
log.kotlinDebug("Close ${this.javaClass.simpleName}")
}
internal fun recordBuildFinished(buildFailed: Boolean) {
BuildFinishMetrics.collectMetrics(log, buildFailed, buildStartTime, projectEvaluatedTime, fusMetricsConsumer)
parameters.configurationMetrics.orElse(emptyList()).get().forEach { it.addToConsumer(fusMetricsConsumer) }
parameters.generalConfigurationMetrics.orNull?.addToConsumer(fusMetricsConsumer)
parameters.buildStatisticsConfiguration.orNull?.also {
val loggerService = KotlinBuildStatsLoggerService(it)
loggerService.initSessionLogger(buildId)
loggerService.reportBuildFinished(fusMetricsConsumer)
}
}
}
class MetricContainer : Serializable {
private val numericalMetrics = HashMap()
private val booleanMetrics = HashMap()
private val stringMetrics = HashMap()
fun addToConsumer(metricsConsumer: StatisticsValuesConsumer) {
for ((key, value) in numericalMetrics) {
metricsConsumer.report(key, value)
}
for ((key, value) in booleanMetrics) {
metricsConsumer.report(key, value)
}
for ((key, value) in stringMetrics) {
metricsConsumer.report(key, value)
}
}
fun put(metric: StringMetrics, value: String) = stringMetrics.put(metric, value)
fun put(metric: BooleanMetrics, value: Boolean) = booleanMetrics.put(metric, value)
fun put(metric: NumericalMetrics, value: Long) = numericalMetrics.put(metric, value)
}