Fix repeating custom values in build scan. Refactor build statistic

This commit is contained in:
nataliya.valtman
2022-07-26 16:21:47 +02:00
parent ccb4b5fd7d
commit 6e2202ad5a
14 changed files with 322 additions and 309 deletions
@@ -13,7 +13,7 @@ import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Classpath
import org.gradle.api.tasks.Internal
import org.jetbrains.kotlin.build.report.metrics.*
import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService
import org.jetbrains.kotlin.gradle.report.BuildMetricsService
import org.jetbrains.kotlin.incremental.classpathDiff.ClassSnapshotGranularity
import org.jetbrains.kotlin.incremental.classpathDiff.ClassSnapshotGranularity.CLASS_LEVEL
import org.jetbrains.kotlin.incremental.classpathDiff.ClassSnapshotGranularity.CLASS_MEMBER_LEVEL
@@ -31,7 +31,7 @@ abstract class ClasspathEntrySnapshotTransform : TransformAction<ClasspathEntryS
abstract val gradleUserHomeDir: DirectoryProperty
@get:Internal
abstract val buildMetricsReporterService: Property<BuildMetricsReporterService>
abstract val buildMetricsService: Property<BuildMetricsService>
}
@get:Classpath
@@ -44,7 +44,7 @@ abstract class ClasspathEntrySnapshotTransform : TransformAction<ClasspathEntryS
val granularity = getClassSnapshotGranularity(classpathEntryInputDirOrJar, parameters.gradleUserHomeDir.get().asFile)
val buildMetricsReporterService = parameters.buildMetricsReporterService.orNull
val buildMetricsReporterService = parameters.buildMetricsService.orNull
val metricsReporter = buildMetricsReporterService?.let { BuildMetricsReporterImpl() } ?: DoNothingBuildMetricsReporter
val startTimeMs = System.currentTimeMillis()
@@ -41,8 +41,8 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSetFactory
import org.jetbrains.kotlin.gradle.plugin.sources.kpm.FragmentMappedKotlinSourceSetFactory
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService
import org.jetbrains.kotlin.gradle.report.HttpReportService
import org.jetbrains.kotlin.gradle.report.BuildMetricsService
import org.jetbrains.kotlin.gradle.report.BuildReportsService
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsPlugin
import org.jetbrains.kotlin.gradle.targets.js.npm.addNpmDependencyExtension
@@ -92,12 +92,9 @@ abstract class DefaultKotlinBasePlugin : KotlinBasePlugin {
KotlinGradleBuildServices.registerIfAbsent(project).get()
KotlinGradleBuildServices.detectKotlinPluginLoadedInMultipleProjects(project, kotlinPluginVersion)
BuildMetricsReporterService.registerIfAbsent(project)?.also {
BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(it)
}
HttpReportService.registerIfAbsent(project, kotlinPluginVersion)?.also {
BuildMetricsService.registerIfAbsent(project)?.also {
BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(it)
BuildReportsService.registerIfAbsent(project, it)
}
}
@@ -20,27 +20,17 @@ import org.jetbrains.kotlin.build.report.metrics.BuildMetrics
import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporter
import org.jetbrains.kotlin.build.report.metrics.BuildTime
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults
import org.jetbrains.kotlin.gradle.plugin.stat.GradleBuildStartParameters
import org.jetbrains.kotlin.gradle.report.data.BuildExecutionData
import org.jetbrains.kotlin.gradle.report.data.BuildExecutionDataProcessor
import org.jetbrains.kotlin.gradle.report.data.BuildOperationRecord
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentLinkedQueue
abstract class BuildMetricsReporterService : BuildService<BuildMetricsReporterService.Parameters>,
OperationCompletionListener, AutoCloseable {
interface Parameters : BuildServiceParameters {
var buildDataProcessors: List<BuildExecutionDataProcessor>
var startParameters: GradleBuildStartParameters
var reportingSettings: ReportingSettings
}
abstract class BuildMetricsService : BuildService<BuildServiceParameters.None>, OperationCompletionListener {
private val log = Logging.getLogger(this.javaClass)
// Tasks and transforms' records
private val buildOperationRecords = ConcurrentLinkedQueue<BuildOperationRecord>()
private val failureMessages = ConcurrentLinkedQueue<String>()
internal val buildOperationRecords = ConcurrentLinkedQueue<BuildOperationRecord>()
internal val failureMessages = ConcurrentLinkedQueue<String>()
// Info for tasks only
private val taskPathToMetricsReporter = ConcurrentHashMap<String, BuildMetricsReporter>()
@@ -102,55 +92,24 @@ abstract class BuildMetricsReporterService : BuildService<BuildMetricsReporterSe
}
}
override fun close() {
val buildData = BuildExecutionData(
startParameters = parameters.startParameters,
failureMessages = failureMessages.toList(),
buildOperationRecord = buildOperationRecords.sortedBy { it.startTimeMs }
)
parameters.buildDataProcessors.forEach { it.process(buildData, log) }
}
companion object {
private val serviceClass = BuildMetricsService::class.java
private val serviceName = "${serviceClass.name}_${serviceClass.classLoader.hashCode()}"
fun getStartParameters(project: Project) = project.gradle.startParameter.let {
GradleBuildStartParameters(
tasks = it.taskRequests.flatMap { it.args },
excludedTasks = it.excludedTaskNames,
currentDir = it.currentDir.path,
projectProperties = it.projectProperties.map {(key, value) -> "$key: $value"},
systemProperties = it.systemPropertiesArgs.map {(key, value) -> "$key: $value"},
)
}
fun registerIfAbsent(project: Project): Provider<BuildMetricsReporterService>? {
val serviceClass = BuildMetricsReporterService::class.java
val serviceName = "${serviceClass.name}_${serviceClass.classLoader.hashCode()}"
fun registerIfAbsent(project: Project): Provider<BuildMetricsService>? {
// 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 {
@Suppress("UNCHECKED_CAST")
return it.service as Provider<BuildMetricsReporterService>
return it.service as Provider<BuildMetricsService>
}
//do not need to collect metrics if there aren't consumers for this data
val reportingSettings = reportingSettings(project.rootProject)
if (reportingSettings.buildReportOutputs.isEmpty()) {
return null
}
return project.gradle.sharedServices.registerIfAbsent(serviceName, serviceClass) {
val buildDataProcessors = mutableListOf<BuildExecutionDataProcessor>()
reportingSettings.fileReportSettings?.let { fileReportSettings ->
buildDataProcessors.add(PlainTextBuildReportWriterDataProcessor(fileReportSettings, project.rootProject.name))
}
reportingSettings.singleOutputFile?.let { singleOutputFile ->
buildDataProcessors.add(MetricsWriter(singleOutputFile.absoluteFile))
}
it.parameters.startParameters = getStartParameters(project)
it.parameters.buildDataProcessors = buildDataProcessors
it.parameters.reportingSettings = reportingSettings
}!!
return project.gradle.sharedServices.registerIfAbsent(serviceName, serviceClass) {}!!
}
}
@@ -0,0 +1,273 @@
/*
* 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.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
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.tooling.events.FinishEvent
import org.gradle.tooling.events.OperationCompletionListener
import org.gradle.tooling.events.task.TaskFinishEvent
import org.jetbrains.kotlin.gradle.plugin.BuildEventsListenerRegistryHolder
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
import org.jetbrains.kotlin.gradle.plugin.stat.BuildFinishStatisticsData
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.isConfigurationCacheAvailable
import java.io.IOException
import java.net.HttpURLConnection
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 {
private val log = Logging.getLogger(this.javaClass)
private val startTime = System.nanoTime()
private val buildUuid = UUID.randomUUID().toString()
private var executorService: ExecutorService = Executors.newSingleThreadExecutor()
init {
log.info("Build report service is registered. Unique build id: $buildUuid")
}
interface Parameters : BuildServiceParameters {
val startParameters: Property<GradleBuildStartParameters>
val reportingSettings: Property<ReportingSettings>
var buildMetricsService: Provider<BuildMetricsService>
val label: Property<String?>
val projectName: Property<String>
val kotlinVersion: Property<String>
val additionalTags: ListProperty<StatTag>
}
override fun close() {
val buildData = BuildExecutionData(
startParameters = parameters.startParameters.get(),
failureMessages = parameters.buildMetricsService.orNull?.failureMessages?.toList() ?: emptyList(),
buildOperationRecord = parameters.buildMetricsService.orNull?.buildOperationRecords?.sortedBy { it.startTimeMs } ?: emptyList()
)
val reportingSettings = parameters.reportingSettings.get()
reportingSettings.httpReportSettings?.also {
executorService.submit { reportBuildFinish() } //
}
reportingSettings.fileReportSettings?.also {
reportBuildStatInFile(it, buildData)
}
reportingSettings.singleOutputFile?.also { singleOutputFile ->
MetricsWriter(singleOutputFile.absoluteFile).process(buildData, log)
}
//It's expected that bad internet connection can cause a significant delay for big project
executorService.shutdownNow()
}
override fun onFinish(event: FinishEvent?) {
addHttpReport(event)
}
private fun reportBuildStatInFile(fileReportSettings: FileReportSettings, buildData: BuildExecutionData) {
val ts = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Calendar.getInstance().time)
val reportFile = fileReportSettings.buildReportDir.resolve("${parameters.projectName.get()}-build-$ts.txt")
PlainTextBuildReportWriter(
outputFile = reportFile,
printMetrics = fileReportSettings.includeMetricsInReport
).process(buildData, log)
}
private fun reportBuildFinish() {
val buildFinishData = BuildFinishStatisticsData(
projectName = parameters.projectName.get(),
startParameters = parameters.startParameters.get(),
buildUuid = buildUuid,
label = parameters.label.orNull,
totalTime = (System.nanoTime() - startTime) / 1_000_000
)
sendDataViaHttp(buildFinishData)
}
private fun initService(project: Project) {
addListeners(project)
}
private fun addBuildScanReport(project: Project) {
if (parameters.reportingSettings.get().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(
event,
parameters.projectName.get(),
buildUuid,
parameters.label.orNull,
parameters.kotlinVersion.get(),
parameters.additionalTags.get()
)
data?.also { executorService.submit { sendDataViaHttp(data) } }
}
}
}
private fun addListeners(project: Project) {
addBuildScanReport(project)
}
private var invalidUrl = false
private var requestPreviousFailed = false
private fun sendDataViaHttp(data: Any) {
val httpSettings = parameters.reportingSettings.get().httpReportSettings ?: return
val elapsedTime = measureTimeMillis {
if (invalidUrl) {
return
}
val connection = try {
URL(httpSettings.url).openConnection() as HttpURLConnection
} catch (e: IOException) {
log.warn("Unable to open connection to ${httpSettings.url}: ${e.message}")
invalidUrl = true
return
}
try {
if (httpSettings.user != null && httpSettings.password != null) {
val auth = Base64.getEncoder()
.encode("${httpSettings.user}:${httpSettings.password}".toByteArray())
.toString(Charsets.UTF_8)
connection.addRequestProperty("Authorization", "Basic $auth")
}
connection.addRequestProperty("Content-Type", "application/json")
connection.requestMethod = "POST"
connection.doOutput = true
connection.outputStream.use {
it.write(Gson().toJson(data).toByteArray())
}
connection.connect()
checkResponseAndLog(connection)
} catch (e: Exception) {
log.debug("Unexpected exception happened ${e.message}: ${e.stackTrace}")
checkResponseAndLog(connection)
} finally {
connection.disconnect()
}
}
log.debug("Report statistic by http takes $elapsedTime ms")
}
private fun checkResponseAndLog(connection: HttpURLConnection) {
val isResponseBad = connection.responseCode !in 200..299
if (isResponseBad) {
val message = "Failed to send statistic to ${connection.url} with ${connection.responseCode}: ${connection.responseMessage}"
if (!requestPreviousFailed) {
log.warn(message)
} else {
log.debug(message)
}
requestPreviousFailed = true
}
}
companion object {
fun getStartParameters(project: Project) = project.gradle.startParameter.let {
GradleBuildStartParameters(
tasks = it.taskRequests.flatMap { it.args },
excludedTasks = it.excludedTaskNames,
currentDir = it.currentDir.path,
projectProperties = it.projectProperties.map { (key, value) -> "$key: $value" },
systemProperties = it.systemPropertiesArgs.map { (key, value) -> "$key: $value" },
)
}
fun registerIfAbsent(project: Project, buildMetricsService: Provider<BuildMetricsService>): Provider<BuildReportsService>? {
val serviceClass = BuildReportsService::class.java
val serviceName = "${serviceClass.name}_${serviceClass.classLoader.hashCode()}"
val reportingSettings = reportingSettings(project.rootProject)
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)
it.parameters.buildMetricsService = buildMetricsService
//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)) {
BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(it)
}
}
}
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
}
}
}
@@ -1,194 +0,0 @@
/*
* 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.google.gson.Gson
import com.gradle.scan.plugin.BuildScanExtension
import org.gradle.api.Project
import org.gradle.api.logging.Logging
import org.gradle.api.provider.Provider
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.TaskFinishEvent
import org.jetbrains.kotlin.gradle.plugin.stat.BuildFinishStatisticsData
import org.jetbrains.kotlin.gradle.plugin.BuildEventsListenerRegistryHolder
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.Companion.prepareData
import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService.Companion.getStartParameters
import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable
import java.io.IOException
import java.net.HttpURLConnection
import java.net.URL
import java.util.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import kotlin.system.measureTimeMillis
abstract class HttpReportService : BuildService<HttpReportService.Parameters>,
OperationCompletionListener, AutoCloseable {
var executorService: ExecutorService = Executors.newSingleThreadExecutor()
val startTime = System.nanoTime()
interface Parameters : BuildServiceParameters {
var label: String?
var projectName: String
var httpSettings: HttpReportSettings
var kotlinVersion: String
var additionalTags: List<StatTag>
var startParameters: GradleBuildStartParameters
}
val log = Logging.getLogger(this.javaClass)
init {
log.info("Http report service is registered. Unique build id: $buildUuid")
}
// @Volatile for one thread executor it does not need
private var requestPreviousFailed = false
private var invalidUrl = false
override fun onFinish(event: FinishEvent?) {
if (event is TaskFinishEvent) {
val data =
prepareData(event, parameters.projectName, buildUuid, parameters.label, parameters.kotlinVersion, parameters.additionalTags)
data?.also { executorService.submit { report(data) } }
}
}
override fun close() {
executorService.submit { reportBuildFinish() }
executorService.shutdown()
}
companion object {
val buildUuid = UUID.randomUUID().toString()
fun registerIfAbsent(project: Project, kotlinVersion: String): Provider<HttpReportService>? {
val gradle = project.gradle
val rootProject = gradle.rootProject
val reportingSettings = reportingSettings(rootProject)
addListeners(project, reportingSettings, kotlinVersion)
return reportingSettings.httpReportSettings?.let { httpSettings ->
gradle.sharedServices.registerIfAbsent(
"build_http_metric_service_${HttpReportService::class.java.classLoader.hashCode()}",
HttpReportService::class.java
) {
it.parameters.label = reportingSettings.buildReportLabel
it.parameters.projectName = rootProject.name
it.parameters.httpSettings = httpSettings
it.parameters.kotlinVersion = kotlinVersion
it.parameters.startParameters = getStartParameters(project)
//init gradle tags, that present in build scan
val additionalTags = ArrayList<StatTag>()
if (isConfigurationCacheAvailable(gradle)) {
additionalTags.add(StatTag.CONFIGURATION_CACHE)
}
if (gradle.startParameter.isBuildCacheEnabled) {
additionalTags.add(StatTag.BUILD_CACHE)
}
it.parameters.additionalTags = additionalTags
}!!
}
}
fun addListeners(project: Project, reportingSettings: ReportingSettings, kotlinVersion: String) {
if (reportingSettings.buildReportOutputs.contains(BuildReportType.BUILD_SCAN)) {
project.rootProject.extensions.findByName("buildScan")
?.also {
val listenerRegistryHolder = BuildEventsListenerRegistryHolder.getInstance(project)
listenerRegistryHolder.listenerRegistry.onTaskCompletion(
project.provider {
BuildScanStatisticsListener(
it as BuildScanExtension,
project.rootProject.name,
reportingSettings.buildReportLabel,
kotlinVersion,
buildUuid
)
}
)
}
}
}
}
fun report(data: Any) {
val elapsedTime = measureTimeMillis {
if (invalidUrl) {
return
}
val connection = try {
URL(parameters.httpSettings.url).openConnection() as HttpURLConnection
} catch (e: IOException) {
log.warn("Unable to open connection to ${parameters.httpSettings.url}: ${e.message}")
invalidUrl = true
return
}
try {
if (parameters.httpSettings.user != null && parameters.httpSettings.password != null) {
val auth = Base64.getEncoder()
.encode("${parameters.httpSettings.user}:${parameters.httpSettings.password}".toByteArray())
.toString(Charsets.UTF_8)
connection.addRequestProperty("Authorization", "Basic $auth")
}
connection.addRequestProperty("Content-Type", "application/json")
connection.requestMethod = "POST"
connection.doOutput = true
connection.outputStream.use {
it.write(Gson().toJson(data).toByteArray())
}
connection.connect()
checkResponseAndLog(connection)
connection.inputStream.use { it.reader().use { reader -> reader.readText() } }
} catch (e: Exception) {
log.debug("Unexpected exception happened ${e.message}: ${e.stackTrace}")
checkResponseAndLog(connection)
} finally {
connection.disconnect()
}
}
log.debug("Report statistic by http takes $elapsedTime ms")
}
private fun reportBuildFinish() {
val buildFinishData = BuildFinishStatisticsData(
projectName = parameters.projectName,
startParameters = parameters.startParameters,
buildUuid = buildUuid,
label = parameters.label,
totalTime = (System.nanoTime() - startTime) / 1_000_000
)
report(buildFinishData)
}
private fun checkResponseAndLog(connection: HttpURLConnection) {
val isResponseBad = connection.responseCode !in 200..299
if (isResponseBad) {
val message = "Failed to send statistic to ${connection.url} with ${connection.responseCode}: ${connection.responseMessage}"
if (!requestPreviousFailed) {
log.warn(message)
} else {
log.debug(message)
}
requestPreviousFailed = true
}
}
}
@@ -12,15 +12,14 @@ import org.jetbrains.kotlin.gradle.internal.build.metrics.GradleBuildMetricsData
import org.jetbrains.kotlin.gradle.internal.build.metrics.BuildOperationData
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.report.data.BuildExecutionData
import org.jetbrains.kotlin.gradle.report.data.BuildExecutionDataProcessor
import java.io.File
import java.io.ObjectOutputStream
import java.io.Serializable
internal class MetricsWriter(
private val outputFile: File,
) : BuildExecutionDataProcessor, Serializable {
override fun process(build: BuildExecutionData, log: Logger) {
): Serializable {
fun process(build: BuildExecutionData, log: Logger) {
if (build.failureMessages.isNotEmpty()) return
try {
@@ -8,7 +8,6 @@ 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.BuildExecutionDataProcessor
import org.jetbrains.kotlin.gradle.report.data.BuildOperationRecord
import org.jetbrains.kotlin.gradle.utils.Printer
import org.jetbrains.kotlin.gradle.utils.asString
@@ -16,25 +15,9 @@ import org.jetbrains.kotlin.gradle.utils.formatSize
import org.jetbrains.kotlin.gradle.utils.formatTime
import java.io.File
import java.io.Serializable
import java.text.SimpleDateFormat
import java.util.*
import kotlin.math.max
internal class PlainTextBuildReportWriterDataProcessor(
val reportingSettings: FileReportSettings,
val rootProjectName: String
) : BuildExecutionDataProcessor, Serializable {
override fun process(build: BuildExecutionData, log: Logger) {
val ts = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Calendar.getInstance().time)
val reportFile = reportingSettings.buildReportDir.resolve("${rootProjectName}-build-$ts.txt")
PlainTextBuildReportWriter(
outputFile = reportFile,
printMetrics = reportingSettings.includeMetricsInReport
).process(build, log)
}
}
internal class PlainTextBuildReportWriter(
private val outputFile: File,
private val printMetrics: Boolean
@@ -1,12 +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.data
import org.gradle.api.logging.Logger
interface BuildExecutionDataProcessor {
fun process(build: BuildExecutionData, log: Logger)
}
@@ -12,7 +12,7 @@ import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.jetbrains.kotlin.gradle.dsl.KotlinJsDce
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService
import org.jetbrains.kotlin.gradle.report.BuildMetricsService
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalDceDsl
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBinaryMode
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBrowserDsl
@@ -211,8 +211,8 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) :
task.description = "build webpack ${mode.name.toLowerCase()} bundle"
task._destinationDirectory = binary.distribution.directory
BuildMetricsReporterService.registerIfAbsent(project)?.let {
task.buildMetricsReporterService.value(it)
BuildMetricsService.registerIfAbsent(project)?.let {
task.buildMetricsService.value(it)
}
task.dependsOn(
@@ -16,7 +16,7 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.isMain
import org.jetbrains.kotlin.gradle.plugin.mpp.isTest
import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService
import org.jetbrains.kotlin.gradle.report.BuildMetricsService
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.js.dsl.*
import org.jetbrains.kotlin.gradle.targets.js.ir.executeTaskBaseName
@@ -237,8 +237,8 @@ abstract class KotlinBrowserJs @Inject constructor(target: KotlinJsTarget) :
task.description = "build webpack ${type.name.toLowerCase()} bundle"
task._destinationDirectory = distribution.directory
BuildMetricsReporterService.registerIfAbsent(project)?.let {
task.buildMetricsReporterService.value(it)
BuildMetricsService.registerIfAbsent(project)?.let {
task.buildMetricsService.value(it)
}
task.commonConfigure(
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.archivesName
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.distsDirectory
import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService
import org.jetbrains.kotlin.gradle.report.BuildMetricsService
import org.jetbrains.kotlin.gradle.targets.js.RequiredKotlinJsDependency
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinWebpackRulesContainer
import org.jetbrains.kotlin.gradle.targets.js.dsl.WebpackRulesDsl
@@ -73,7 +73,7 @@ constructor(
get() = injected
@get:Internal
internal abstract val buildMetricsReporterService: Property<BuildMetricsReporterService?>
internal abstract val buildMetricsService: Property<BuildMetricsService?>
@get:Internal
val metrics: Property<BuildMetricsReporter> = project.objects
@@ -330,7 +330,7 @@ constructor(
buildMetrics.addMetric(BuildPerformanceMetric.BUNDLE_SIZE, it)
}
buildMetricsReporterService.orNull?.also { it.addTask(path, this.javaClass, buildMetrics) }
buildMetricsService.orNull?.also { it.addTask(path, this.javaClass, buildMetrics) }
}
}
@@ -51,8 +51,9 @@ import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService
import org.jetbrains.kotlin.gradle.report.BuildMetricsService
import org.jetbrains.kotlin.gradle.report.BuildReportMode
import org.jetbrains.kotlin.gradle.report.BuildReportsService
import org.jetbrains.kotlin.gradle.report.ReportingSettings
import org.jetbrains.kotlin.gradle.utils.*
import org.jetbrains.kotlin.incremental.*
@@ -270,16 +271,19 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments> @Inject constr
internal open fun isIncrementalCompilationEnabled(): Boolean =
incremental
@get:Internal
val startParameters = BuildMetricsReporterService.getStartParameters(project)
@get:Input
abstract val ownModuleName: Property<String>
@get:Internal
internal abstract val buildMetricsReporterService: Property<BuildMetricsReporterService?>
internal abstract val buildMetricsService: Property<BuildMetricsService?>
internal fun reportingSettings() = buildMetricsReporterService.orNull?.parameters?.reportingSettings ?: ReportingSettings()
@get:Internal
internal abstract val buildReportsService: Property<BuildReportsService?>
@get:Internal
val startParameters = BuildReportsService.getStartParameters(project)
internal fun reportingSettings() = buildReportsService.orNull?.parameters?.reportingSettings?.orNull ?: ReportingSettings()
@get:Internal
protected val multiModuleICSettings: MultiModuleICSettings
@@ -403,7 +407,7 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments> @Inject constr
executeImpl(inputChanges, outputsBackup)
}
buildMetricsReporterService.orNull?.also { it.addTask(path, this.javaClass, buildMetrics) }
buildMetricsService.orNull?.also { it.addTask(path, this.javaClass, buildMetrics) }
}
protected open fun skipCondition(): Boolean = sources.isEmpty
@@ -19,7 +19,8 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPro
import org.jetbrains.kotlin.gradle.plugin.mpp.associateWithClosure
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinCompilationData
import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToKotlinOptions
import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService
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.KOTLIN_BUILD_DIR_NAME
import org.jetbrains.kotlin.project.model.LanguageSettings
@@ -58,10 +59,13 @@ internal abstract class AbstractKotlinCompileConfig<TASK : AbstractKotlinCompile
.disallowChanges()
task.localStateDirectories.from(task.taskBuildLocalStateDirectory).disallowChanges()
BuildMetricsReporterService.registerIfAbsent(project)?.let {
task.buildMetricsReporterService.value(it)
val buildMetricsService = BuildMetricsService.registerIfAbsent(project)?.also {
task.buildMetricsService.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())
@@ -16,7 +16,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinCompilationData
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder
import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService
import org.jetbrains.kotlin.gradle.report.BuildMetricsService
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.project.model.LanguageSettings
@@ -111,18 +111,18 @@ internal open class BaseKotlinCompileConfig<TASK : KotlinCompile> : AbstractKotl
}
project.extensions.extraProperties[TRANSFORMS_REGISTERED] = true
val buildMetricsReporterService = BuildMetricsReporterService.registerIfAbsent(project)
val buildMetricsService = BuildMetricsService.registerIfAbsent(project)
project.dependencies.registerTransform(ClasspathEntrySnapshotTransform::class.java) {
it.from.attribute(ARTIFACT_TYPE_ATTRIBUTE, JAR_ARTIFACT_TYPE)
it.to.attribute(ARTIFACT_TYPE_ATTRIBUTE, CLASSPATH_ENTRY_SNAPSHOT_ARTIFACT_TYPE)
it.parameters.gradleUserHomeDir.set(project.gradle.gradleUserHomeDir)
buildMetricsReporterService?.apply { it.parameters.buildMetricsReporterService.set(this) }
buildMetricsService?.apply { it.parameters.buildMetricsService.set(this) }
}
project.dependencies.registerTransform(ClasspathEntrySnapshotTransform::class.java) {
it.from.attribute(ARTIFACT_TYPE_ATTRIBUTE, DIRECTORY_ARTIFACT_TYPE)
it.to.attribute(ARTIFACT_TYPE_ATTRIBUTE, CLASSPATH_ENTRY_SNAPSHOT_ARTIFACT_TYPE)
it.parameters.gradleUserHomeDir.set(project.gradle.gradleUserHomeDir)
buildMetricsReporterService?.apply { it.parameters.buildMetricsReporterService.set(this) }
buildMetricsService?.apply { it.parameters.buildMetricsService.set(this) }
}
}