fixup! Support configuration cache and project isolation for FUS statistics
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/BuildFusService.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/BuildFusService.kt
index f2b32e9..cdd5b1b 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/BuildFusService.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/BuildFusService.kt
@@ -7,6 +7,7 @@
import org.gradle.api.Project
import org.gradle.api.Task
+import org.gradle.api.artifacts.component.BuildIdentifier
import org.gradle.api.logging.Logging
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
@@ -29,6 +30,8 @@
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.currentBuild
+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
@@ -52,6 +55,7 @@
interface Parameters : BuildServiceParameters {
val configurationMetrics: ListProperty<MetricContainer>
val useBuildFinishFlowAction: Property<Boolean>
+ val buildId: Property<String>
}
private val fusMetricsConsumer = NonSynchronizedMetricsContainer()
@@ -99,8 +103,9 @@
// so there is a change that no VariantImplementationFactory will be found
return project.gradle.sharedServices.registerIfAbsent(serviceName, BuildFusService::class.java) { spec ->
KotlinBuildStatsService.getOrCreateInstance(project)
+ val buildId = generateBuildId(project.currentBuildId())
KotlinBuildStatsService.applyIfInitialised {
- it.recordProjectsEvaluated(project)
+ it.recordProjectsEvaluated(project, buildId)
}
spec.parameters.configurationMetrics.add(project.provider {
@@ -115,6 +120,7 @@
KotlinBuildStatHandler.collectProjectConfigurationTimeMetrics(project)
})
spec.parameters.useBuildFinishFlowAction.set(GradleVersion.current().baseVersion >= GradleVersion.version("8.1"))
+ spec.parameters.buildId.set(project.provider { generateBuildId(project.currentBuildId()) })
}.also { buildService ->
BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(buildService)
if (GradleVersion.current().baseVersion >= GradleVersion.version("8.1")) {
@@ -122,6 +128,16 @@
}
}
}
+ private fun generateBuildId(buildIdentifier: BuildIdentifier): String {
+// val buildName = if (GradleVersion.current().baseVersion < GradleVersion.version("8.2")) {
+// buildIdentifier.name
+// } else {
+// buildIdentifier.buildPath
+// }
+// .replace("\\W+", "")
+// return "${buildName}_${buildIdentifier.hashCode()}"
+ return buildIdentifier.hashCode().toString()
+ }
}
override fun onFinish(event: FinishEvent?) {
@@ -146,10 +162,11 @@
KotlinBuildStatHandler.reportGlobalMetrics(fusMetricsConsumer)
parameters.configurationMetrics.orElse(emptyList()).get().forEach { it.addToConsumer(fusMetricsConsumer) }
KotlinBuildStatsService.applyIfInitialised {
- it.recordBuildFinish(action, buildFailed, fusMetricsConsumer)
+ it.recordBuildFinish(action, buildFailed, fusMetricsConsumer, parameters.buildId.get())
}
KotlinBuildStatsService.closeServices()
}
+
private fun reportTaskMetrics(taskExecutionResult: TaskExecutionResult, event: TaskFinishEvent) {
val totalTimeMs = event.result.endTime - event.result.startTime
val buildMetrics = taskExecutionResult.buildMetrics
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatHandler.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatHandler.kt
index 93d891f..d6bcc26 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatHandler.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatHandler.kt
@@ -268,8 +268,10 @@
action: String?,
buildFailed: Boolean,
metricsContainer: NonSynchronizedMetricsContainer,
+ buildId: String
) {
runSafe("${KotlinBuildStatHandler::class.java}.reportBuildFinish") {
+// sessionLogger.initTrackingFile(buildId)
metricsContainer.sendToConsumer(sessionLogger)
sessionLogger.finishBuildSession(action, buildFailed)
}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatsService.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatsService.kt
index 7fe900a..0928397 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatsService.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatsService.kt
@@ -190,9 +190,9 @@
/**
* Collects metrics at the end of a build
*/
- open fun recordBuildFinish(action: String?, buildFailed: Boolean, metricsContainer: NonSynchronizedMetricsContainer) {}
+ open fun recordBuildFinish(action: String?, buildFailed: Boolean, metricsContainer: NonSynchronizedMetricsContainer, buildId: String) {}
- open fun recordProjectsEvaluated(project: Project) {}
+ open fun recordProjectsEvaluated(project: Project, buildId: String? = null) {}
}
internal class JMXKotlinBuildStatsService(private val mbs: MBeanServer, private val beanName: ObjectName) :
@@ -259,12 +259,13 @@
return (gradle as? DefaultGradle)?.services?.get(BuildRequestMetaData::class.java)?.startTime
}
- override fun recordProjectsEvaluated(project: Project) {
+ override fun recordProjectsEvaluated(project: Project, buildId: String?) {
runSafe("${DefaultKotlinBuildStatsService::class.java}.projectEvaluated") {
if (!sessionLogger.isBuildSessionStarted()) {
sessionLogger.startBuildSession(
DaemonReuseCounter.incrementAndGetOrdinal(),
gradleBuildStartTime(project.gradle),
+ buildId
)
}
}
@@ -302,7 +303,7 @@
report(StringMetrics.valueOf(name), value, subprojectName, weight)
//only one jmx bean service should report global metrics
- override fun recordBuildFinish(action: String?, buildFailed: Boolean, metricsContainer: NonSynchronizedMetricsContainer) {
- KotlinBuildStatHandler().reportBuildFinished(sessionLogger, action, buildFailed, metricsContainer)
+ override fun recordBuildFinish(action: String?, buildFailed: Boolean, metricsContainer: NonSynchronizedMetricsContainer, buildId: String) {
+ KotlinBuildStatHandler().reportBuildFinished(sessionLogger, action, buildFailed, metricsContainer, buildId)
}
}
diff --git a/libraries/tools/kotlin-gradle-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/BuildSessionLogger.kt b/libraries/tools/kotlin-gradle-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/BuildSessionLogger.kt
index b648fee..e301d7c 100644
--- a/libraries/tools/kotlin-gradle-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/BuildSessionLogger.kt
+++ b/libraries/tools/kotlin-gradle-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/BuildSessionLogger.kt
@@ -72,7 +72,7 @@
* - files with age (current time - last modified) more than maxFileAge should be deleted (if we trust lastModified returned by FS)
*/
@Synchronized
- private fun initTrackingFile(buildUid: String?) {
+ internal fun initTrackingFile(buildUid: String?) {
closeTrackingFile()
val fileName = buildUid ?: profileFileNameFormatter.format(LocalDateTime.now())
@@ -81,7 +81,7 @@
private fun clearOldFiles() {
// Get list of existing files. Try to create folder if possible, return from function if failed to create folder
- val fileCandidates = listProfileFiles(statisticsFolder) ?: return
+ val fileCandidates = listProfileFiles(statisticsFolder) ?: if (statisticsFolder.mkdirs()) emptyList() else return
for ((index, file) in fileCandidates.withIndex()) {
if (index < fileCandidates.size - maxProfileFiles) {
@@ -102,6 +102,7 @@
try {
// nanotime could not be used as build start time in nanotime is unknown. As result, the measured duration
// could be affected by system clock correction
+
val finishTime = System.currentTimeMillis()
buildSession?.also {
if (it.buildStartedTime != null) {
diff --git a/libraries/tools/kotlin-gradle-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/fileloggers/FileRecordLogger.kt b/libraries/tools/kotlin-gradle-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/fileloggers/FileRecordLogger.kt
index b5708f4..d5f0c93 100644
--- a/libraries/tools/kotlin-gradle-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/fileloggers/FileRecordLogger.kt
+++ b/libraries/tools/kotlin-gradle-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/fileloggers/FileRecordLogger.kt
@@ -25,9 +25,17 @@
statisticsFolder.mkdirs()
val file = File(statisticsFolder, fileName + PROFILE_FILE_NAME_SUFFIX)
- FileOutputStream(file, true).bufferedWriter().use {
- for (value in metrics) {
- it.appendLine(value)
+ FileOutputStream(file, true).use {
+ val channel = it.getChannel()
+ val lockFile = channel.lock()
+ try {
+ it.bufferedWriter().use { writer ->
+ for (value in metrics) {
+ writer.appendLine(value)
+ }
+ }
+ } finally {
+ lockFile.release()
}
}
} catch (e: IOException) {
diff --git a/libraries/tools/kotlin-gradle-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/fileloggers/MetricsContainer.kt b/libraries/tools/kotlin-gradle-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/fileloggers/MetricsContainer.kt
index 95a8306..a2a19cb 100644
--- a/libraries/tools/kotlin-gradle-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/fileloggers/MetricsContainer.kt
+++ b/libraries/tools/kotlin-gradle-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/fileloggers/MetricsContainer.kt
@@ -51,12 +51,15 @@
val channel = FileChannel.open(Paths.get(file.toURI()), StandardOpenOption.WRITE, StandardOpenOption.READ)
channel.tryLock() ?: return false
+ //Any plugin can create fus-report-file and write into it. The file is ready to read by IDEA only when kotlin build is finished
+ var buildFinished = false
val inputStream = Channels.newInputStream(channel)
try {
var container = MetricsContainer()
// Note: close is called at forEachLine
BufferedReader(InputStreamReader(inputStream, ENCODING)).forEachLine { line ->
if (BUILD_SESSION_SEPARATOR == line) {
+ buildFinished = true
consumer.invoke(container)
container = MetricsContainer()
} else {
@@ -96,7 +99,7 @@
} finally {
channel.close()
}
- return true
+ return buildFinished
}
}