diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/FusStatisticsIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/FusStatisticsIT.kt new file mode 100644 index 00000000000..a53999319e1 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/FusStatisticsIT.kt @@ -0,0 +1,125 @@ +/* + * 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 + +import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.testbase.GradleTest +import org.junit.jupiter.api.DisplayName +import org.jetbrains.kotlin.gradle.testbase.* +import org.jetbrains.kotlin.gradle.util.replaceText +import org.jetbrains.kotlin.test.KtAssert.fail +import java.nio.file.Files +import java.nio.file.Path +import kotlin.streams.asSequence + +@DisplayName("FUS statistic") +class FusStatisticsIT : KGPBaseTest() { + @DisplayName("for dokka") + @GradleTest + fun testDokka(gradleVersion: GradleVersion) { + project( + "simpleProject", + gradleVersion, + ) { + applyDokka() + build("compileKotlin", "dokkaHtml", "-Pkotlin.session.logger.root.path=$projectPath") { + assertFileContains( + fusStatisticsPath, + "ENABLED_DOKKA", + "ENABLED_DOKKA_HTML" + ) + } + } + } + + private fun TestProject.applyDokka() { + buildGradle.replaceText( + "plugins {", + """ + plugins { + id("org.jetbrains.dokka") version "1.7.10" + """.trimIndent() + ) + buildGradle.modify { + """ + import org.jetbrains.dokka.DokkaConfiguration.Visibility + import org.jetbrains.dokka.gradle.DokkaTask + $it + tasks.withType(DokkaTask.class) { + dokkaSourceSets.configureEach { + documentedVisibilities.set([ + Visibility.PUBLIC, + Visibility.PROTECTED + ]) + } + } + """.trimIndent() + + } + } + + private val GradleProject.fusStatisticsPath: Path + get() { + val statisticsDir = projectPath.resolve("kotlin-profile") + return Files.list(statisticsDir).use { + val files = it.asSequence().toList() + files.singleOrNull() ?: fail("The directory must contain a single statistics file, but got: $files") + } + } + + @DisplayName("general fields") + @GradleTest + fun testFusStatistics(gradleVersion: GradleVersion) { + project( + "simpleProject", + gradleVersion, + ) { + build("compileKotlin", "-Pkotlin.session.logger.root.path=$projectPath") { + val fusStatisticsPath = fusStatisticsPath + assertFileContains( + fusStatisticsPath, + "OS_TYPE", + "BUILD_FAILED=false", + "EXECUTED_FROM_IDEA=false", + "BUILD_FINISH_TIME", + "GRADLE_VERSION", + "KOTLIN_STDLIB_VERSION", + "KOTLIN_COMPILER_VERSION", + ) + assertFileDoesNotContain( + fusStatisticsPath, + "ENABLED_DOKKA", + "ENABLED_DOKKA_HTML", + ) // asserts that we do not put DOKKA metrics everywhere just in case + } + } + } + + @DisplayName("for failed build") + @GradleTest + fun testFusStatisticsForFailedBuild(gradleVersion: GradleVersion) { + project( + "simpleProject", + gradleVersion, + ) { + projectPath.resolve("src/main/kotlin/helloWorld.kt").modify { + it.replace("java.util.ArrayList", "") + } + buildAndFail("compileKotlin", "-Pkotlin.session.logger.root.path=$projectPath") { + assertFileContains( + fusStatisticsPath, + "BUILD_FAILED=true", + "OS_TYPE", + "EXECUTED_FROM_IDEA=false", + "BUILD_FINISH_TIME", + "GRADLE_VERSION", + "KOTLIN_STDLIB_VERSION", + "KOTLIN_COMPILER_VERSION", + ) + } + } + } +} \ No newline at end of file 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 b00468de383..927e5da77e0 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 @@ -26,6 +26,8 @@ import kotlin.system.measureTimeMillis class KotlinBuildStatHandler { companion object { + const val DOKKA_PLUGIN = "org.jetbrains.dokka" + @JvmStatic internal fun getLogger() = Logging.getLogger(KotlinBuildStatHandler::class.java) @@ -101,6 +103,9 @@ class KotlinBuildStatHandler { val statisticOverhead = measureTimeMillis { gradle.allprojects { project -> + project.plugins.findPlugin(DOKKA_PLUGIN)?.also { + sessionLogger.report(BooleanMetrics.ENABLED_DOKKA, true) + } for (configuration in project.configurations) { val configurationName = configuration.name val dependencies = configuration.dependencies 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 3aa2f02e0a4..f7f412ca90e 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 @@ -13,6 +13,9 @@ import org.gradle.api.logging.Logger import org.gradle.api.logging.Logging import org.gradle.initialization.BuildRequestMetaData import org.gradle.invocation.DefaultGradle +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.statistics.KotlinBuildStatHandler.Companion.runSafe import org.jetbrains.kotlin.gradle.plugin.statistics.old.Pre232IdeaKotlinBuildStatsMXBean import org.jetbrains.kotlin.gradle.plugin.statistics.old.Pre232IdeaKotlinBuildStatsService @@ -131,6 +134,14 @@ internal abstract class KotlinBuildStatsService internal constructor() : BuildAd if (!isConfigurationCacheAvailable(gradle)) { gradle.addBuildListener(instance!!) } + + BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry.onTaskCompletion(project.provider { + OperationCompletionListener { event -> + if (event is TaskFinishEvent) { + reportTaskIfNeed(event.descriptor.name) + } + } + }) } instance } @@ -147,6 +158,24 @@ internal abstract class KotlinBuildStatsService internal constructor() : BuildAd } } + protected fun reportTaskIfNeed(task: String) { + val metric = when (task.substringAfterLast(":")) { + "dokkaHtml" -> BooleanMetrics.ENABLED_DOKKA_HTML + "dokkaGfm" -> BooleanMetrics.ENABLED_DOKKA_GFM + "dokkaJavadoc" -> BooleanMetrics.ENABLED_DOKKA_JAVADOC + "dokkaJekyll" -> BooleanMetrics.ENABLED_DOKKA_JEKYLL + "dokkaHtmlMultiModule" -> BooleanMetrics.ENABLED_DOKKA_HTML_MULTI_MODULE + "dokkaGfmMultiModule" -> BooleanMetrics.ENABLED_DOKKA_GFM_MULTI_MODULE + "dokkaJekyllMultiModule" -> BooleanMetrics.ENABLED_DOKKA_JEKYLL_MULTI_MODULE + "dokkaHtmlCollector" -> BooleanMetrics.ENABLED_DOKKA_HTML_COLLECTOR + "dokkaGfmCollector" -> BooleanMetrics.ENABLED_DOKKA_GFM_COLLECTOR + "dokkaJavadocCollector" -> BooleanMetrics.ENABLED_DOKKA_JAVADOC_COLLECTOR + "dokkaJekyllCollector" -> BooleanMetrics.ENABLED_DOKKA_JEKYLL_COLLECTOR + else -> null + } + metric?.also { getInstance()?.report(it, true) } + } + /** * Invokes provided collector if the reporting service is initialised. @@ -220,14 +249,31 @@ internal class JMXKotlinBuildStatsService(private val mbs: MBeanServer, private internal abstract class AbstractKotlinBuildStatsService( gradle: Gradle, - protected val beanName: ObjectName + protected val beanName: ObjectName, ) : KotlinBuildStatsService() { + companion object { + //test only + const val CUSTOM_LOGGER_ROOT_PATH = "kotlin.session.logger.root.path" + + private val logger = Logging.getLogger(AbstractKotlinBuildStatsService::class.java) + } + private val forcePropertiesValidation = if (gradle.rootProject.hasProperty(FORCE_VALUES_VALIDATION)) { gradle.rootProject.property(FORCE_VALUES_VALIDATION).toString().toBoolean() } else { false } - protected val sessionLogger = BuildSessionLogger(gradle.gradleUserHomeDir, forceValuesValidation = forcePropertiesValidation) + + private val customSessionLoggerRootPath = + gradle.rootProject.properties[CUSTOM_LOGGER_ROOT_PATH]?.also { + logger.warn("$CUSTOM_LOGGER_ROOT_PATH property for test purpose only") + } as String? + + + private val sessionLoggerRootPath = + customSessionLoggerRootPath?.let { File(it) } ?: gradle.gradleUserHomeDir + + protected val sessionLogger = BuildSessionLogger(sessionLoggerRootPath, forceValuesValidation = forcePropertiesValidation) private fun gradleBuildStartTime(gradle: Gradle): Long? { return (gradle as? DefaultGradle)?.services?.get(BuildRequestMetaData::class.java)?.startTime diff --git a/libraries/tools/kotlin-gradle-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/metrics/BooleanMetrics.kt b/libraries/tools/kotlin-gradle-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/metrics/BooleanMetrics.kt index d5c93e85dd2..7b3e925d2b3 100644 --- a/libraries/tools/kotlin-gradle-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/metrics/BooleanMetrics.kt +++ b/libraries/tools/kotlin-gradle-statistics/src/main/kotlin/org/jetbrains/kotlin/statistics/metrics/BooleanMetrics.kt @@ -49,6 +49,20 @@ enum class BooleanMetrics(val type: BooleanOverridePolicy, val anonymization: Bo JS_KLIB_INCREMENTAL(OR, SAFE), JS_IR_INCREMENTAL(OR, SAFE), + //Dokka features + ENABLED_DOKKA(OR, SAFE), + ENABLED_DOKKA_HTML(OR, SAFE), + ENABLED_DOKKA_JAVADOC(OR, SAFE), + ENABLED_DOKKA_GFM(OR, SAFE), + ENABLED_DOKKA_JEKYLL(OR, SAFE), + ENABLED_DOKKA_HTML_MULTI_MODULE(OR, SAFE), + ENABLED_DOKKA_GFM_MULTI_MODULE(OR, SAFE), + ENABLED_DOKKA_JEKYLL_MULTI_MODULE(OR, SAFE), + ENABLED_DOKKA_HTML_COLLECTOR(OR, SAFE), + ENABLED_DOKKA_JAVADOC_COLLECTOR(OR, SAFE), + ENABLED_DOKKA_GFM_COLLECTOR(OR, SAFE), + ENABLED_DOKKA_JEKYLL_COLLECTOR(OR, SAFE), + // User scenarios DEBUGGER_ENABLED(OVERRIDE, SAFE), COMPILATION_STARTED(OVERRIDE, SAFE),