Add kotlin.build.report.inputs property to log task's inputs in reports
diff --git a/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildAttribute.kt b/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildAttribute.kt
index 9115e6d..7df2deb 100644
--- a/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildAttribute.kt
+++ b/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildAttribute.kt
@@ -7,8 +7,8 @@
 
 import java.io.Serializable
 
-enum class BuildAttributeKind : Serializable {
-    REBUILD_REASON;
+enum class BuildAttributeKind(val readableString: String) : Serializable {
+    REBUILD_REASON("Rebuild reason");
 
     companion object {
         const val serialVersionUID = 0L
diff --git a/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildInputs.kt b/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildInputs.kt
new file mode 100644
index 0000000..5741d18f
--- /dev/null
+++ b/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildInputs.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.build.report.metrics
+
+import java.io.Serializable
+import java.util.*
+import kotlin.collections.ArrayList
+
+class BuildInputs : Serializable {
+
+    private val myInputs = TreeMap<String, MutableList<String>>()
+
+    fun add(propertyName: String, value: Any?) {
+        when (value) {
+            is Map<*, *> -> {
+                value.forEach {
+                    myInputs.getOrPut("$propertyName.${it.key}") { ArrayList() }.add(it.value.toString())
+                }
+            }
+            is Iterable<*> -> {
+                myInputs.getOrPut(propertyName) { ArrayList() }.addAll(value.map { it.toString() })
+            }
+            else -> {
+                myInputs.getOrPut(propertyName) { ArrayList() }.add(value.toString())
+            }
+        }
+    }
+
+    fun addAll(other: BuildInputs) {
+        for ((property, value) in other.myInputs) {
+            add(property, value)
+        }
+    }
+
+    fun asMap(): Map<String, List<String>> = myInputs
+
+    companion object {
+        const val serialVersionUID = 0L
+        const val fileProperty = "FILE"
+    }
+}
\ No newline at end of file
diff --git a/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildMetrics.kt b/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildMetrics.kt
index 68d92fe..0fb7ab2 100644
--- a/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildMetrics.kt
+++ b/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildMetrics.kt
@@ -10,15 +10,18 @@
 data class BuildMetrics(
     val buildTimes: BuildTimes = BuildTimes(),
     val buildPerformanceMetrics: BuildPerformanceMetrics = BuildPerformanceMetrics(),
-    val buildAttributes: BuildAttributes = BuildAttributes()
+    val buildAttributes: BuildAttributes = BuildAttributes(),
+    val buildInputs: BuildInputs = BuildInputs()
 ) : Serializable {
-    fun addAll(other: BuildMetrics) {
-        buildTimes.addAll(other.buildTimes)
-        buildPerformanceMetrics.addAll(other.buildPerformanceMetrics)
-        buildAttributes.addAll(other.buildAttributes)
-    }
+    fun addAll(other: BuildMetrics?) =
+        other?.also {
+            buildTimes.addAll(other.buildTimes)
+            buildPerformanceMetrics.addAll(other.buildPerformanceMetrics)
+            buildAttributes.addAll(other.buildAttributes)
+            buildInputs.addAll(other.buildInputs)
+        }
 
     companion object {
-        const val serialVersionUID = 0L
+        const val serialVersionUID = 1L
     }
 }
\ No newline at end of file
diff --git a/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildMetricsReporter.kt b/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildMetricsReporter.kt
index 5c51429..1547dd3 100644
--- a/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildMetricsReporter.kt
+++ b/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildMetricsReporter.kt
@@ -17,6 +17,9 @@
 
     fun getMetrics(): BuildMetrics
     fun addMetrics(metrics: BuildMetrics)
+
+    fun addInputFile(path: String)
+    fun addInputProperty(name: String, value: Any?)
 }
 
 inline fun <T> BuildMetricsReporter.measure(time: BuildTime, fn: () -> T): T {
diff --git a/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildMetricsReporterImpl.kt b/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildMetricsReporterImpl.kt
index f7f2dcf..535ba73 100644
--- a/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildMetricsReporterImpl.kt
+++ b/build-common/src/org/jetbrains/kotlin/build/report/metrics/BuildMetricsReporterImpl.kt
@@ -16,6 +16,7 @@
     private val myBuildTimes = BuildTimes()
     private val myBuildMetrics = BuildPerformanceMetrics()
     private val myBuildAttributes = BuildAttributes()
+    private val myInputs = BuildInputs()
 
     override fun startMeasure(time: BuildTime) {
         if (time in myBuildTimeStartNs) {
@@ -46,7 +47,8 @@
         BuildMetrics(
             buildTimes = myBuildTimes,
             buildPerformanceMetrics = myBuildMetrics,
-            buildAttributes = myBuildAttributes
+            buildAttributes = myBuildAttributes,
+            buildInputs = myInputs
         )
 
     override fun addMetrics(metrics: BuildMetrics) {
@@ -54,4 +56,12 @@
         myBuildTimes.addAll(metrics.buildTimes)
         myBuildMetrics.addAll(metrics.buildPerformanceMetrics)
     }
+
+    override fun addInputFile(path: String) {
+        myInputs.add(BuildInputs.fileProperty, path)
+    }
+
+    override fun addInputProperty(name: String, value: Any?) {
+        myInputs.add(name, value)
+    }
 }
\ No newline at end of file
diff --git a/build-common/src/org/jetbrains/kotlin/build/report/metrics/DoNothingBuildMetricsReporter.kt b/build-common/src/org/jetbrains/kotlin/build/report/metrics/DoNothingBuildMetricsReporter.kt
index 6764c1f..2905352 100644
--- a/build-common/src/org/jetbrains/kotlin/build/report/metrics/DoNothingBuildMetricsReporter.kt
+++ b/build-common/src/org/jetbrains/kotlin/build/report/metrics/DoNothingBuildMetricsReporter.kt
@@ -29,4 +29,10 @@
         )
 
     override fun addMetrics(metrics: BuildMetrics) {}
+
+    override fun addInputFile(path: String) {
+    }
+
+    override fun addInputProperty(name: String, value: Any?) {
+    }
 }
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildReportsIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildReportsIT.kt
index 4e37cdb..7936777 100644
--- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildReportsIT.kt
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildReportsIT.kt
@@ -70,7 +70,24 @@
             assertTrue { report.contains("ABI snapshot size:") }
             //for non-incremental builds
             assertTrue { report.contains("Build attributes:") }
-            assertTrue { report.contains("REBUILD_REASON:") }
+            assertTrue { report.contains("Rebuild reason:") }
+        }
+    }
+
+    @DisplayName("Build metrics contains task's inputs")
+    @GradleTest
+    fun testBuildMetricsWithTaskInputs(gradleVersion: GradleVersion) {
+        project("simpleProject", gradleVersion) {
+            build("assemble", "-Pkotlin.build.report.inputs=true") {
+                assertOutputContains("Kotlin build report is written to")
+            }
+            val reportFolder = projectPath.resolve("build/reports/kotlin-build").toFile()
+            val reports = reportFolder.listFiles()
+            assertNotNull(reports)
+            assertEquals(1, reports.size)
+            val report = reports[0].readText()
+
+            assertTrue { report.contains("Task inputs:") }
         }
     }
 
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleBuildServices.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleBuildServices.kt
index 0922f57..65f8e46 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleBuildServices.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleBuildServices.kt
@@ -12,15 +12,13 @@
 import org.gradle.api.services.BuildService
 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 {
+internal abstract class KotlinGradleBuildServices : BuildService<KotlinGradleBuildServices.Parameters>, AutoCloseable, TaskCachesManagement() {
 
     interface Parameters : BuildServiceParameters {
         var buildDir: File
@@ -42,8 +40,7 @@
         buildHandler.buildFinished(parameters.buildDir, parameters.rootDir)
         log.kotlinDebug(DISPOSE_MESSAGE)
 
-        TaskLoggers.clear()
-        TaskExecutionResults.clear()
+        cleanTaskExecutionCaches()
     }
 
     companion object {
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleFinishBuildHandler.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleFinishBuildHandler.kt
index 2a117fe..1e9efef 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleFinishBuildHandler.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleFinishBuildHandler.kt
@@ -10,6 +10,7 @@
 import org.jetbrains.kotlin.compilerRunner.DELETED_SESSION_FILE_PREFIX
 import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner
 import org.jetbrains.kotlin.gradle.logging.kotlinDebug
+import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskBuildMetrics
 import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults
 import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers
 import org.jetbrains.kotlin.gradle.utils.relativeOrCanonical
@@ -18,7 +19,7 @@
 import java.lang.management.ManagementFactory
 import kotlin.math.max
 
-internal class KotlinGradleFinishBuildHandler {
+internal class KotlinGradleFinishBuildHandler : TaskCachesManagement() {
 
     companion object {
         const val SHOULD_REPORT_MEMORY_USAGE_PROPERTY = "kotlin.gradle.test.report.memory.usage"
@@ -34,9 +35,7 @@
     }
 
     fun buildFinished(rootProjectBuildDir: File, rootProjectRootDir: File) {
-        TaskLoggers.clear()
-        TaskExecutionResults.clear()
-
+        cleanTaskExecutionCaches()
         GradleCompilerRunner.clearBuildModulesInfo()
 
         val sessionsDir = GradleCompilerRunner.sessionsDir(rootProjectBuildDir)
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt
index dfa5f17..bb885b7 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinProperties.kt
@@ -73,6 +73,9 @@
     val buildReportLabel: String?
         get() = property("kotlin.build.report.label")
 
+    val buildReportIncludeTaskInputs: Boolean
+        get() = booleanProperty("kotlin.build.report.inputs") ?: false
+
     val buildReportFileOutputDir: File?
         get() = property("kotlin.build.report.file.output_dir")?.let { File(it) }
 
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/TaskCachesManagement.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/TaskCachesManagement.kt
new file mode 100644
index 0000000..2f5766a
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/TaskCachesManagement.kt
@@ -0,0 +1,18 @@
+/*
+ * 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.plugin
+
+import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskBuildMetrics
+import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults
+import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers
+
+open class TaskCachesManagement {
+    fun cleanTaskExecutionCaches() {
+        TaskLoggers.clear()
+        TaskExecutionResults.clear()
+        TaskBuildMetrics.clear()
+    }
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/internal/state/TaskExecutionResults.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/internal/state/TaskExecutionResults.kt
index fc22031..214ef94 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/internal/state/TaskExecutionResults.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/internal/state/TaskExecutionResults.kt
@@ -5,6 +5,8 @@
 
 package org.jetbrains.kotlin.gradle.plugin.internal.state
 
+import org.jetbrains.kotlin.build.report.metrics.BuildMetrics
+import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporter
 import org.jetbrains.kotlin.gradle.report.TaskExecutionResult
 import java.util.concurrent.ConcurrentHashMap
 
@@ -21,4 +23,19 @@
     fun clear() {
         results.clear()
     }
+}
+
+internal object TaskBuildMetrics {
+    private val results = ConcurrentHashMap<String, BuildMetricsReporter>()
+
+    operator fun get(taskPath: String): BuildMetrics? =
+        results[taskPath]?.getMetrics()
+
+    operator fun set(taskPath: String, result: BuildMetricsReporter) {
+        results[taskPath] = result
+    }
+
+    fun clear() {
+        results.clear()
+    }
 }
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/CompileStatisticsData.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/CompileStatisticsData.kt
index f2cf7c6..d41d3c5 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/CompileStatisticsData.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/CompileStatisticsData.kt
@@ -30,7 +30,8 @@
     val nonIncrementalAttributes: Set<BuildAttribute>,
     //TODO think about it,time in milliseconds
     val buildTimesMetrics: Map<BuildTime, Long>,
-    val performanceMetrics: Map<BuildPerformanceMetric, Long>
+    val performanceMetrics: Map<BuildPerformanceMetric, Long>,
+    val taskInputs: List<String>
 ) {
     companion object {
         private val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").also { it.timeZone = TimeZone.getTimeZone("UTC")}
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatListener.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatListener.kt
index c6104b5..4e438e2 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatListener.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/statistics/KotlinBuildStatListener.kt
@@ -9,17 +9,16 @@
 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.cli.common.CompilerSystemProperties
-import org.jetbrains.kotlin.cli.common.toBooleanLenient
+import org.jetbrains.kotlin.build.report.metrics.BuildMetrics
+import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskBuildMetrics
 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.gradle.report.TaskExecutionInfo
 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,
@@ -71,9 +70,11 @@
             }
 
             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 taskBuildMetrics = TaskBuildMetrics[taskPath]
+            val buildMetrics = taskExecutionResult?.buildMetrics?.addAll(taskBuildMetrics) ?: taskBuildMetrics
+
+            val buildTimesMs = buildMetrics?.buildTimes?.asMapMs()?.filterValues { value -> value != 0L } ?: emptyMap()
+            val perfData = 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 }
@@ -89,20 +90,21 @@
                 projectName = projectName,
                 taskName = taskPath,
                 changes = changes,
-                tags = parseTags(taskExecutionResult).map { it.name },
-                nonIncrementalAttributes = taskExecutionResult?.buildMetrics?.buildAttributes?.asMap()?.filter { it.value > 0 }?.keys ?: emptySet(),
+                tags = parseTags(taskExecutionResult?.taskInfo, buildMetrics).map { it.name },
+                nonIncrementalAttributes = buildMetrics?.buildAttributes?.asMap()?.filter { it.value > 0 }?.keys ?: emptySet(),
                 hostName = hostName,
                 kotlinVersion = kotlinVersion,
                 buildUuid = uuid,
                 finishTime = System.currentTimeMillis(),
-                compilerArguments = taskExecutionResult?.taskInfo?.compilerArguments?.asList() ?: emptyList()
+                compilerArguments = taskExecutionResult?.taskInfo?.compilerArguments?.asList() ?: emptyList(),
+                taskInputs = buildMetrics?.buildInputs?.asMap()?.map { (key, value) -> "$key:${value.joinToString()}" } ?: emptyList()
             )
         }
 
-        private fun parseTags(taskExecutionResult: TaskExecutionResult?): List<StatTag> {
+        private fun parseTags(taskInfo: TaskExecutionInfo?, buildMetrics: BuildMetrics?): List<StatTag> {
             val tags = ArrayList<StatTag>()
 
-            val nonIncrementalAttributes = taskExecutionResult?.buildMetrics?.buildAttributes?.asMap() ?: emptyMap()
+            val nonIncrementalAttributes = buildMetrics?.buildAttributes?.asMap() ?: emptyMap()
 
             if (nonIncrementalAttributes.isEmpty()) {
                 tags.add(StatTag.INCREMENTAL)
@@ -110,8 +112,6 @@
                 tags.add(StatTag.NON_INCREMENTAL)
             }
 
-            val taskInfo = taskExecutionResult?.taskInfo
-
             taskInfo?.withAbiSnapshot?.ifTrue {
                 tags.add(StatTag.ABI_SNAPSHOT)
             }
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/report/BuildMetricsReporterService.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/report/BuildMetricsReporterService.kt
index 936ee60..48ce60b 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/report/BuildMetricsReporterService.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/report/BuildMetricsReporterService.kt
@@ -19,6 +19,7 @@
 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.TaskBuildMetrics
 import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults
 import org.jetbrains.kotlin.gradle.report.data.BuildExecutionData
 import org.jetbrains.kotlin.gradle.report.data.BuildExecutionDataProcessor
@@ -42,13 +43,9 @@
     private val failureMessages = ConcurrentLinkedQueue<String>()
 
     // Info for tasks only
-    private val taskPathToMetricsReporter = ConcurrentHashMap<String, BuildMetricsReporter>()
     private val taskPathToTaskClass = ConcurrentHashMap<String, String>()
 
     open fun addTask(taskPath: String, taskClass: Class<*>, metricsReporter: BuildMetricsReporter) {
-        taskPathToMetricsReporter.put(taskPath, metricsReporter).also {
-            if (it != null) log.warn("Duplicate task path: $taskPath") // Should never happen but log it just in case
-        }
         taskPathToTaskClass.put(taskPath, taskClass.name).also {
             if (it != null) log.warn("Duplicate task path: $taskPath") // Should never happen but log it just in case
         }
@@ -77,8 +74,8 @@
 
             val buildMetrics = BuildMetrics()
             buildMetrics.buildTimes.addTimeMs(BuildTime.GRADLE_TASK, totalTimeMs)
-            taskPathToMetricsReporter[taskPath]?.let {
-                buildMetrics.addAll(it.getMetrics())
+            TaskBuildMetrics[taskPath]?.let {
+                buildMetrics.addAll(it)
             }
             val taskExecutionResult = TaskExecutionResults[taskPath]
             taskExecutionResult?.buildMetrics?.also { buildMetrics.addAll(it) }
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/report/HttpReportService.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/report/HttpReportService.kt
index a7928a8..0dcaa27 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/report/HttpReportService.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/report/HttpReportService.kt
@@ -101,7 +101,7 @@
                 connection.requestMethod = "POST"
                 connection.doOutput = true
                 connection.outputStream.use {
-                    it.write(Gson().toJson(data).toByteArray())
+                    it.write(Gson().toJson(data).replace("$", ".").toByteArray())
                 }
                 connection.connect()
                 checkResponseAndLog(connection)
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/report/PlainTextBuildReportWriter.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/report/PlainTextBuildReportWriter.kt
index ee8b512..8e60a24 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/report/PlainTextBuildReportWriter.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/report/PlainTextBuildReportWriter.kt
@@ -164,7 +164,18 @@
         p.withIndent("Build attributes:") {
             val attributesByKind = allAttributes.entries.groupBy { it.key.kind }.toSortedMap()
             for ((kind, attributesCounts) in attributesByKind) {
-                printMap(p, kind.name, attributesCounts.map { (k, v) -> k.readableString to v }.toMap())
+                printMap(p, kind.readableString, attributesCounts.associate { (k, v) -> k.readableString to v })
+            }
+        }
+    }
+
+    private fun printBuildInputs(buildInputs: BuildInputs) {
+        val allInputs = buildInputs.asMap()
+        if (allInputs.isEmpty()) return
+
+        p.withIndent("Task inputs:") {
+            allInputs.forEach {
+                p.println("${it.key} : ${it.value.joinToString(prefix = "[", postfix = "]")}")
             }
         }
     }
@@ -225,6 +236,7 @@
 
         if (printMetrics) {
             printMetrics(task.buildMetrics)
+            printBuildInputs(task.buildMetrics.buildInputs)
         }
     }
 }
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/report/ReportingSettings.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/report/ReportingSettings.kt
index 97fda8e..0d8c755 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/report/ReportingSettings.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/report/ReportingSettings.kt
@@ -13,6 +13,7 @@
     val buildReportOutputs: List<BuildReportType> = emptyList(),
     val buildReportMode: BuildReportMode = BuildReportMode.NONE,
     val buildReportLabel: String? = null,
+    val includeTaskInputs: Boolean = false,
     val fileReportSettings: FileReportSettings? = null,
     val httpReportSettings: HttpReportSettings? = null
 ) : Serializable {
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/report/configureReporing.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/report/configureReporing.kt
index b5e9a83..beffa97 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/report/configureReporing.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/report/configureReporing.kt
@@ -41,6 +41,7 @@
         metricsOutputFile = metricsOutputFile,
         buildReportMode = buildReportMode,
         buildReportLabel = properties.buildReportLabel,
+        includeTaskInputs = properties.buildReportIncludeTaskInputs,
         fileReportSettings = fileReportSettings,
         httpReportSettings = httpReportSettings,
         buildReportOutputs = buildReportOutputTypes
diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt
index e712114..85c561d 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt
@@ -49,6 +49,7 @@
 import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector
 import org.jetbrains.kotlin.gradle.logging.kotlinDebug
 import org.jetbrains.kotlin.gradle.plugin.*
+import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskBuildMetrics
 import org.jetbrains.kotlin.gradle.plugin.mpp.*
 import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
 import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService
@@ -352,6 +353,12 @@
                 else
                     it.report(BooleanMetrics.COMPILATION_STARTED, true)
             }
+
+            if (reportingSettings().includeTaskInputs) {
+                inputs.files.files.forEach { buildMetrics.addInputFile(it.path) }
+                inputs.properties.forEach { buildMetrics.addInputProperty(it.key, it.value) }
+            }
+
             validateCompilerClasspath()
             systemPropertiesService.get().startIntercept()
             CompilerSystemProperties.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY.value = "true"
@@ -385,6 +392,7 @@
         }
 
         buildMetricsReporterService.orNull?.also { it.addTask(path, this.javaClass, buildMetrics) }
+        TaskBuildMetrics[path] = buildMetrics
     }
 
     protected open fun skipCondition(): Boolean = sources.isEmpty