Add build reports for diagnosing build problems in Gradle
#KT-12700 Fixed To turn build reports, add 'kotlin.build.report.enable=true' to gradle.properties file (or pass it in CLI via`-Pkotlin.build.report.enable=true`). By default build reports are created in `rootProject/build/reports/kotlin-build`. Build report dir can be customized via `kotlin.build.report.dir` property.
This commit is contained in:
+2
-1
@@ -26,5 +26,6 @@ interface CompilationResults : Remote {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum class CompilationResultCategory(val code: Int) {
|
enum class CompilationResultCategory(val code: Int) {
|
||||||
IC_COMPILE_ITERATION(0)
|
IC_COMPILE_ITERATION(0),
|
||||||
|
IC_LOG(1)
|
||||||
}
|
}
|
||||||
@@ -511,13 +511,18 @@ class CompileServiceImpl(
|
|||||||
|
|
||||||
val workingDir = incrementalCompilationOptions.workingDir
|
val workingDir = incrementalCompilationOptions.workingDir
|
||||||
val modulesApiHistory = ModulesApiHistoryJs(incrementalCompilationOptions.modulesInfo)
|
val modulesApiHistory = ModulesApiHistoryJs(incrementalCompilationOptions.modulesInfo)
|
||||||
|
|
||||||
val compiler = IncrementalJsCompilerRunner(
|
val compiler = IncrementalJsCompilerRunner(
|
||||||
workingDir = workingDir,
|
workingDir = workingDir,
|
||||||
reporter = reporter,
|
reporter = reporter,
|
||||||
buildHistoryFile = incrementalCompilationOptions.multiModuleICSettings.buildHistoryFile,
|
buildHistoryFile = incrementalCompilationOptions.multiModuleICSettings.buildHistoryFile,
|
||||||
modulesApiHistory = modulesApiHistory
|
modulesApiHistory = modulesApiHistory
|
||||||
)
|
)
|
||||||
return compiler.compile(allKotlinFiles, args, compilerMessageCollector, changedFiles)
|
return try {
|
||||||
|
compiler.compile(allKotlinFiles, args, compilerMessageCollector, changedFiles)
|
||||||
|
} finally {
|
||||||
|
reporter.flush()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun execIncrementalCompiler(
|
private fun execIncrementalCompiler(
|
||||||
@@ -570,6 +575,8 @@ class CompileServiceImpl(
|
|||||||
val workingDir = incrementalCompilationOptions.workingDir
|
val workingDir = incrementalCompilationOptions.workingDir
|
||||||
|
|
||||||
val modulesApiHistory = incrementalCompilationOptions.run {
|
val modulesApiHistory = incrementalCompilationOptions.run {
|
||||||
|
reporter.report { "Use module detection: ${multiModuleICSettings.useModuleDetection}" }
|
||||||
|
|
||||||
if (!multiModuleICSettings.useModuleDetection) {
|
if (!multiModuleICSettings.useModuleDetection) {
|
||||||
ModulesApiHistoryJvm(modulesInfo)
|
ModulesApiHistoryJvm(modulesInfo)
|
||||||
} else {
|
} else {
|
||||||
@@ -587,7 +594,11 @@ class CompileServiceImpl(
|
|||||||
modulesApiHistory = modulesApiHistory,
|
modulesApiHistory = modulesApiHistory,
|
||||||
kotlinSourceFilesExtensions = allKotlinExtensions
|
kotlinSourceFilesExtensions = allKotlinExtensions
|
||||||
)
|
)
|
||||||
return compiler.compile(allKotlinFiles, k2jvmArgs, compilerMessageCollector, changedFiles)
|
return try {
|
||||||
|
compiler.compile(allKotlinFiles, k2jvmArgs, compilerMessageCollector, changedFiles)
|
||||||
|
} finally {
|
||||||
|
reporter.flush()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun leaseReplSession(
|
override fun leaseReplSession(
|
||||||
|
|||||||
@@ -22,26 +22,54 @@ import org.jetbrains.kotlin.incremental.ICReporter
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
internal class RemoteICReporter(
|
internal class RemoteICReporter(
|
||||||
private val servicesFacade: CompilerServicesFacadeBase,
|
private val servicesFacade: CompilerServicesFacadeBase,
|
||||||
private val compilationResults: CompilationResults,
|
private val compilationResults: CompilationResults,
|
||||||
compilationOptions: CompilationOptions
|
compilationOptions: IncrementalCompilationOptions
|
||||||
) : ICReporter {
|
) : ICReporter {
|
||||||
|
|
||||||
|
private val rootDir = compilationOptions.modulesInfo.projectRoot
|
||||||
private val shouldReportMessages = ReportCategory.IC_MESSAGE.code in compilationOptions.reportCategories
|
private val shouldReportMessages = ReportCategory.IC_MESSAGE.code in compilationOptions.reportCategories
|
||||||
private val isVerbose = compilationOptions.reportSeverity == ReportSeverity.DEBUG.code
|
private val isVerbose = compilationOptions.reportSeverity == ReportSeverity.DEBUG.code
|
||||||
private val shouldReportCompileIteration = CompilationResultCategory.IC_COMPILE_ITERATION.code in compilationOptions.requestedCompilationResults
|
private val shouldReportCompileIteration =
|
||||||
|
CompilationResultCategory.IC_COMPILE_ITERATION.code in compilationOptions.requestedCompilationResults
|
||||||
|
private val shouldReportICLog = CompilationResultCategory.IC_LOG.code in compilationOptions.requestedCompilationResults
|
||||||
|
private val icLogLines = arrayListOf<String>()
|
||||||
|
|
||||||
override fun report(message: () -> String) {
|
override fun report(message: () -> String) {
|
||||||
|
val lazyMessage = lazy { message() }
|
||||||
if (shouldReportMessages && isVerbose) {
|
if (shouldReportMessages && isVerbose) {
|
||||||
servicesFacade.report(ReportCategory.IC_MESSAGE, ReportSeverity.DEBUG, message())
|
servicesFacade.report(ReportCategory.IC_MESSAGE, ReportSeverity.DEBUG, lazyMessage.value)
|
||||||
|
}
|
||||||
|
if (shouldReportICLog) {
|
||||||
|
icLogLines.add(lazyMessage.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun reportCompileIteration(sourceFiles: Collection<File>, exitCode: ExitCode) {
|
override fun reportCompileIteration(sourceFiles: Collection<File>, exitCode: ExitCode) {
|
||||||
if (shouldReportCompileIteration) {
|
if (shouldReportCompileIteration) {
|
||||||
compilationResults.add(CompilationResultCategory.IC_COMPILE_ITERATION.code,
|
compilationResults.add(
|
||||||
CompileIterationResult(sourceFiles, exitCode.toString())
|
CompilationResultCategory.IC_COMPILE_ITERATION.code,
|
||||||
|
CompileIterationResult(sourceFiles, exitCode.toString())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (shouldReportICLog) {
|
||||||
|
icLogLines.add("compile iteration:")
|
||||||
|
sourceFiles.relativePaths(rootDir).forEach {
|
||||||
|
icLogLines.add(" $it")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun flush() {
|
||||||
|
if (shouldReportICLog) {
|
||||||
|
compilationResults.add(CompilationResultCategory.IC_LOG.code, icLogLines)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun File.relativeOrCanonical(base: File): String =
|
||||||
|
relativeToOrNull(base)?.path ?: canonicalPath
|
||||||
|
|
||||||
|
private fun Iterable<File>.relativePaths(base: File): List<String> =
|
||||||
|
map { it.relativeOrCanonical(base) }.sorted()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
-9
@@ -21,18 +21,27 @@ internal class GradleCompilationResults(
|
|||||||
LoopbackNetworkInterface.clientLoopbackSocketFactory,
|
LoopbackNetworkInterface.clientLoopbackSocketFactory,
|
||||||
LoopbackNetworkInterface.serverLoopbackSocketFactory
|
LoopbackNetworkInterface.serverLoopbackSocketFactory
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
var icLogLines: List<String>? = null
|
||||||
|
|
||||||
@Throws(RemoteException::class)
|
@Throws(RemoteException::class)
|
||||||
override fun add(compilationResultCategory: Int, value: Serializable) {
|
override fun add(compilationResultCategory: Int, value: Serializable) {
|
||||||
if (compilationResultCategory == CompilationResultCategory.IC_COMPILE_ITERATION.code) {
|
when (compilationResultCategory) {
|
||||||
@Suppress("UNCHECKED_CAST")
|
CompilationResultCategory.IC_COMPILE_ITERATION.code -> {
|
||||||
val compileIterationResult = value as? CompileIterationResult
|
@Suppress("UNCHECKED_CAST")
|
||||||
if (compileIterationResult != null) {
|
val compileIterationResult = value as? CompileIterationResult
|
||||||
val sourceFiles = compileIterationResult.sourceFiles
|
if (compileIterationResult != null) {
|
||||||
if (sourceFiles.any()) {
|
val sourceFiles = compileIterationResult.sourceFiles
|
||||||
log.kotlinDebug { "compile iteration: ${sourceFiles.pathsAsStringRelativeTo(projectRootFile)}" }
|
if (sourceFiles.any()) {
|
||||||
|
log.kotlinDebug { "compile iteration: ${sourceFiles.pathsAsStringRelativeTo(projectRootFile)}" }
|
||||||
|
}
|
||||||
|
val exitCode = compileIterationResult.exitCode
|
||||||
|
log.kotlinDebug { "compiler exit code: $exitCode" }
|
||||||
}
|
}
|
||||||
val exitCode = compileIterationResult.exitCode
|
}
|
||||||
log.kotlinDebug { "compiler exit code: $exitCode" }
|
CompilationResultCategory.IC_LOG.code -> {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
icLogLines = value as? List<String>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -16,6 +16,7 @@ internal class GradleCompilerEnvironment(
|
|||||||
messageCollector: GradlePrintingMessageCollector,
|
messageCollector: GradlePrintingMessageCollector,
|
||||||
outputItemsCollector: OutputItemsCollector,
|
outputItemsCollector: OutputItemsCollector,
|
||||||
val outputFiles: FileCollection,
|
val outputFiles: FileCollection,
|
||||||
|
val reportExecutionResult: Boolean,
|
||||||
val incrementalCompilationEnvironment: IncrementalCompilationEnvironment? = null
|
val incrementalCompilationEnvironment: IncrementalCompilationEnvironment? = null
|
||||||
) : CompilerEnvironment(Services.EMPTY, messageCollector, outputItemsCollector) {
|
) : CompilerEnvironment(Services.EMPTY, messageCollector, outputItemsCollector) {
|
||||||
val toolsJar: File? by lazy { findToolsJar() }
|
val toolsJar: File? by lazy { findToolsJar() }
|
||||||
|
|||||||
+2
-1
@@ -141,7 +141,8 @@ internal open class GradleCompilerRunner(protected val task: Task) {
|
|||||||
incrementalModuleInfo = modulesInfo,
|
incrementalModuleInfo = modulesInfo,
|
||||||
buildFile = buildFile,
|
buildFile = buildFile,
|
||||||
outputFiles = environment.outputFiles.toList(),
|
outputFiles = environment.outputFiles.toList(),
|
||||||
taskPath = task.path
|
taskPath = task.path,
|
||||||
|
reportExecutionResult = environment.reportExecutionResult
|
||||||
)
|
)
|
||||||
TaskLoggers.put(task.path, task.logger)
|
TaskLoggers.put(task.path, task.logger)
|
||||||
runCompilerAsync(workArgs)
|
runCompilerAsync(workArgs)
|
||||||
|
|||||||
+59
-5
@@ -11,7 +11,9 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
|||||||
import org.jetbrains.kotlin.config.Services
|
import org.jetbrains.kotlin.config.Services
|
||||||
import org.jetbrains.kotlin.daemon.common.*
|
import org.jetbrains.kotlin.daemon.common.*
|
||||||
import org.jetbrains.kotlin.gradle.logging.*
|
import org.jetbrains.kotlin.gradle.logging.*
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults
|
||||||
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers
|
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers
|
||||||
|
import org.jetbrains.kotlin.gradle.report.TaskExecutionResult
|
||||||
import org.jetbrains.kotlin.gradle.tasks.throwGradleExceptionIfError
|
import org.jetbrains.kotlin.gradle.tasks.throwGradleExceptionIfError
|
||||||
import org.jetbrains.kotlin.gradle.utils.stackTraceAsString
|
import org.jetbrains.kotlin.gradle.utils.stackTraceAsString
|
||||||
import org.jetbrains.kotlin.incremental.ChangedFiles
|
import org.jetbrains.kotlin.incremental.ChangedFiles
|
||||||
@@ -20,6 +22,7 @@ import org.slf4j.LoggerFactory
|
|||||||
import java.io.*
|
import java.io.*
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
import java.rmi.RemoteException
|
import java.rmi.RemoteException
|
||||||
|
import java.util.*
|
||||||
import java.util.concurrent.Callable
|
import java.util.concurrent.Callable
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -50,7 +53,8 @@ internal class GradleKotlinCompilerWorkArguments(
|
|||||||
val incrementalModuleInfo: IncrementalModuleInfo?,
|
val incrementalModuleInfo: IncrementalModuleInfo?,
|
||||||
val buildFile: File?,
|
val buildFile: File?,
|
||||||
val outputFiles: List<File>,
|
val outputFiles: List<File>,
|
||||||
val taskPath: String
|
val taskPath: String,
|
||||||
|
val reportExecutionResult: Boolean
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
companion object {
|
companion object {
|
||||||
const val serialVersionUID: Long = 0
|
const val serialVersionUID: Long = 0
|
||||||
@@ -88,6 +92,7 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
|||||||
private val buildFile = config.buildFile
|
private val buildFile = config.buildFile
|
||||||
private val outputFiles = config.outputFiles
|
private val outputFiles = config.outputFiles
|
||||||
private val taskPath = config.taskPath
|
private val taskPath = config.taskPath
|
||||||
|
private val reportExecutionResult = config.reportExecutionResult
|
||||||
|
|
||||||
private val log: KotlinLogger =
|
private val log: KotlinLogger =
|
||||||
TaskLoggers.get(taskPath)?.let { GradleKotlinLogger(it).apply { debug("Using '$taskPath' logger") } }
|
TaskLoggers.get(taskPath)?.let { GradleKotlinLogger(it).apply { debug("Using '$taskPath' logger") } }
|
||||||
@@ -226,7 +231,16 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
|||||||
requestedCompilationResults = emptyArray()
|
requestedCompilationResults = emptyArray()
|
||||||
)
|
)
|
||||||
val servicesFacade = GradleCompilerServicesFacadeImpl(log, bufferingMessageCollector)
|
val servicesFacade = GradleCompilerServicesFacadeImpl(log, bufferingMessageCollector)
|
||||||
return daemon.compile(sessionId, compilerArgs, compilationOptions, servicesFacade, compilationResults = null)
|
return try {
|
||||||
|
daemon.compile(sessionId, compilerArgs, compilationOptions, servicesFacade, compilationResults = null)
|
||||||
|
} finally {
|
||||||
|
reportExecutionResultIfNeeded {
|
||||||
|
TaskExecutionResult(
|
||||||
|
executionStrategy = DAEMON_EXECUTION_STRATEGY,
|
||||||
|
icLogLines = nonIcBuildLog("incremental compilation is not enabled for '$taskPath'")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun incrementalCompilationWithDaemon(
|
private fun incrementalCompilationWithDaemon(
|
||||||
@@ -238,6 +252,11 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
|||||||
val icEnv = incrementalCompilationEnvironment ?: error("incrementalCompilationEnvironment is null!")
|
val icEnv = incrementalCompilationEnvironment ?: error("incrementalCompilationEnvironment is null!")
|
||||||
val knownChangedFiles = icEnv.changedFiles as? ChangedFiles.Known
|
val knownChangedFiles = icEnv.changedFiles as? ChangedFiles.Known
|
||||||
|
|
||||||
|
val requestedCompilationResults = EnumSet.of(CompilationResultCategory.IC_COMPILE_ITERATION)
|
||||||
|
if (reportExecutionResult) {
|
||||||
|
requestedCompilationResults.add(CompilationResultCategory.IC_LOG)
|
||||||
|
}
|
||||||
|
|
||||||
val compilationOptions = IncrementalCompilationOptions(
|
val compilationOptions = IncrementalCompilationOptions(
|
||||||
areFileChangesKnown = knownChangedFiles != null,
|
areFileChangesKnown = knownChangedFiles != null,
|
||||||
modifiedFiles = knownChangedFiles?.modified,
|
modifiedFiles = knownChangedFiles?.modified,
|
||||||
@@ -245,7 +264,7 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
|||||||
workingDir = icEnv.workingDir,
|
workingDir = icEnv.workingDir,
|
||||||
reportCategories = reportCategories(isVerbose),
|
reportCategories = reportCategories(isVerbose),
|
||||||
reportSeverity = reportSeverity(isVerbose),
|
reportSeverity = reportSeverity(isVerbose),
|
||||||
requestedCompilationResults = arrayOf(CompilationResultCategory.IC_COMPILE_ITERATION.code),
|
requestedCompilationResults = requestedCompilationResults.map { it.code }.toTypedArray(),
|
||||||
compilerMode = CompilerMode.INCREMENTAL_COMPILER,
|
compilerMode = CompilerMode.INCREMENTAL_COMPILER,
|
||||||
targetPlatform = targetPlatform,
|
targetPlatform = targetPlatform,
|
||||||
usePreciseJavaTracking = icEnv.usePreciseJavaTracking,
|
usePreciseJavaTracking = icEnv.usePreciseJavaTracking,
|
||||||
@@ -257,11 +276,29 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
|||||||
log.info("Options for KOTLIN DAEMON: $compilationOptions")
|
log.info("Options for KOTLIN DAEMON: $compilationOptions")
|
||||||
val servicesFacade = GradleIncrementalCompilerServicesFacadeImpl(log, bufferingMessageCollector)
|
val servicesFacade = GradleIncrementalCompilerServicesFacadeImpl(log, bufferingMessageCollector)
|
||||||
val compilationResults = GradleCompilationResults(log, projectRootFile)
|
val compilationResults = GradleCompilationResults(log, projectRootFile)
|
||||||
return daemon.compile(sessionId, compilerArgs, compilationOptions, servicesFacade, compilationResults)
|
val result = daemon.compile(sessionId, compilerArgs, compilationOptions, servicesFacade, compilationResults)
|
||||||
|
|
||||||
|
reportExecutionResultIfNeeded {
|
||||||
|
TaskExecutionResult(
|
||||||
|
executionStrategy = DAEMON_EXECUTION_STRATEGY,
|
||||||
|
icLogLines = compilationResults.icLogLines
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun compileOutOfProcess(): ExitCode =
|
private fun compileOutOfProcess(): ExitCode =
|
||||||
runToolInSeparateProcess(compilerArgs, compilerClassName, compilerFullClasspath, log)
|
try {
|
||||||
|
runToolInSeparateProcess(compilerArgs, compilerClassName, compilerFullClasspath, log)
|
||||||
|
} finally {
|
||||||
|
reportExecutionResultIfNeeded {
|
||||||
|
TaskExecutionResult(
|
||||||
|
executionStrategy = OUT_OF_PROCESS_EXECUTION_STRATEGY,
|
||||||
|
icLogLines = nonIcBuildLog("$OUT_OF_PROCESS_EXECUTION_STRATEGY execution strategy does not support incremental compilation")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun compileInProcess(messageCollector: MessageCollector): ExitCode {
|
private fun compileInProcess(messageCollector: MessageCollector): ExitCode {
|
||||||
// in-process compiler should always be run in a different thread
|
// in-process compiler should always be run in a different thread
|
||||||
@@ -276,6 +313,13 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
|||||||
} finally {
|
} finally {
|
||||||
bufferingMessageCollector.flush(messageCollector)
|
bufferingMessageCollector.flush(messageCollector)
|
||||||
threadPool.shutdown()
|
threadPool.shutdown()
|
||||||
|
|
||||||
|
reportExecutionResultIfNeeded {
|
||||||
|
TaskExecutionResult(
|
||||||
|
executionStrategy = IN_PROCESS_EXECUTION_STRATEGY,
|
||||||
|
icLogLines = nonIcBuildLog("$IN_PROCESS_EXECUTION_STRATEGY execution strategy does not support incremental compilation")
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,4 +364,14 @@ internal class GradleKotlinCompilerWork @Inject constructor(
|
|||||||
} else {
|
} else {
|
||||||
ReportSeverity.DEBUG.code
|
ReportSeverity.DEBUG.code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inline fun reportExecutionResultIfNeeded(fn: () -> TaskExecutionResult) {
|
||||||
|
if (reportExecutionResult) {
|
||||||
|
val result = fn()
|
||||||
|
TaskExecutionResults[taskPath] = result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun nonIcBuildLog(reason: String): List<String> =
|
||||||
|
listOf("Performing non-incremental build: $reason")
|
||||||
}
|
}
|
||||||
+1
@@ -64,6 +64,7 @@ open class KaptWithKotlincTask : KaptTask(), CompilerArgumentAwareWithInput<K2JV
|
|||||||
val outputItemCollector = OutputItemsCollectorImpl()
|
val outputItemCollector = OutputItemsCollectorImpl()
|
||||||
val environment = GradleCompilerEnvironment(
|
val environment = GradleCompilerEnvironment(
|
||||||
compilerClasspath, messageCollector, outputItemCollector,
|
compilerClasspath, messageCollector, outputItemCollector,
|
||||||
|
reportExecutionResult = kotlinCompileTask.reportExecutionResult,
|
||||||
outputFiles = allOutputFiles()
|
outputFiles = allOutputFiles()
|
||||||
)
|
)
|
||||||
if (environment.toolsJar == null && !isAtLeastJava9) {
|
if (environment.toolsJar == null && !isAtLeastJava9) {
|
||||||
|
|||||||
+19
-2
@@ -19,16 +19,26 @@ package org.jetbrains.kotlin.gradle.plugin
|
|||||||
import org.gradle.BuildAdapter
|
import org.gradle.BuildAdapter
|
||||||
import org.gradle.BuildResult
|
import org.gradle.BuildResult
|
||||||
import org.gradle.api.invocation.Gradle
|
import org.gradle.api.invocation.Gradle
|
||||||
|
import org.gradle.api.logging.Logger
|
||||||
import org.gradle.api.logging.Logging
|
import org.gradle.api.logging.Logging
|
||||||
import org.jetbrains.kotlin.compilerRunner.DELETED_SESSION_FILE_PREFIX
|
import org.jetbrains.kotlin.compilerRunner.DELETED_SESSION_FILE_PREFIX
|
||||||
import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner
|
import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner
|
||||||
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers
|
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers
|
||||||
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
|
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults
|
||||||
|
import org.jetbrains.kotlin.gradle.report.KotlinBuildReporter
|
||||||
|
import org.jetbrains.kotlin.gradle.report.configureBuildReporter
|
||||||
|
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
|
||||||
import org.jetbrains.kotlin.gradle.utils.relativeToRoot
|
import org.jetbrains.kotlin.gradle.utils.relativeToRoot
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.sumByLong
|
import org.jetbrains.kotlin.utils.addToStdlib.sumByLong
|
||||||
|
import java.io.File
|
||||||
import java.lang.management.ManagementFactory
|
import java.lang.management.ManagementFactory
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
internal class KotlinGradleBuildServices private constructor(gradle: Gradle) : BuildAdapter() {
|
internal class KotlinGradleBuildServices private constructor(
|
||||||
|
private val gradle: Gradle
|
||||||
|
) : BuildAdapter() {
|
||||||
companion object {
|
companion object {
|
||||||
private val CLASS_NAME = KotlinGradleBuildServices::class.java.simpleName
|
private val CLASS_NAME = KotlinGradleBuildServices::class.java.simpleName
|
||||||
const val FORCE_SYSTEM_GC_MESSAGE = "Forcing System.gc()"
|
const val FORCE_SYSTEM_GC_MESSAGE = "Forcing System.gc()"
|
||||||
@@ -68,10 +78,17 @@ internal class KotlinGradleBuildServices private constructor(gradle: Gradle) : B
|
|||||||
// but it is called before any plugin can attach build listener
|
// but it is called before any plugin can attach build listener
|
||||||
fun buildStarted() {
|
fun buildStarted() {
|
||||||
startMemory = getUsedMemoryKb()
|
startMemory = getUsedMemoryKb()
|
||||||
|
|
||||||
TaskLoggers.clear()
|
TaskLoggers.clear()
|
||||||
|
TaskExecutionResults.clear()
|
||||||
|
|
||||||
|
configureBuildReporter(gradle, log)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun buildFinished(result: BuildResult) {
|
override fun buildFinished(result: BuildResult) {
|
||||||
|
TaskLoggers.clear()
|
||||||
|
TaskExecutionResults.clear()
|
||||||
|
|
||||||
val gradle = result.gradle!!
|
val gradle = result.gradle!!
|
||||||
GradleCompilerRunner.clearBuildModulesInfo()
|
GradleCompilerRunner.clearBuildModulesInfo()
|
||||||
|
|
||||||
@@ -100,7 +117,6 @@ internal class KotlinGradleBuildServices private constructor(gradle: Gradle) : B
|
|||||||
log.lifecycle("[KOTLIN][PERF] Used memory after build: $endMem kb (difference since build start: ${"%+d".format(endMem - startMem)} kb)")
|
log.lifecycle("[KOTLIN][PERF] Used memory after build: $endMem kb (difference since build start: ${"%+d".format(endMem - startMem)} kb)")
|
||||||
}
|
}
|
||||||
|
|
||||||
TaskLoggers.clear()
|
|
||||||
gradle.removeListener(this)
|
gradle.removeListener(this)
|
||||||
instance = null
|
instance = null
|
||||||
log.kotlinDebug(DISPOSE_MESSAGE)
|
log.kotlinDebug(DISPOSE_MESSAGE)
|
||||||
@@ -122,3 +138,4 @@ internal class KotlinGradleBuildServices private constructor(gradle: Gradle) : B
|
|||||||
private fun getGcCount(): Long =
|
private fun getGcCount(): Long =
|
||||||
ManagementFactory.getGarbageCollectorMXBeans().sumByLong { Math.max(0, it.collectionCount) }
|
ManagementFactory.getGarbageCollectorMXBeans().sumByLong { Math.max(0, it.collectionCount) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
@@ -18,9 +18,11 @@ package org.jetbrains.kotlin.gradle.plugin
|
|||||||
|
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.jetbrains.kotlin.gradle.dsl.Coroutines
|
import org.jetbrains.kotlin.gradle.dsl.Coroutines
|
||||||
|
import org.jetbrains.kotlin.gradle.report.BuildReportMode
|
||||||
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
|
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
|
||||||
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
|
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
|
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
internal fun PropertiesProvider.mapKotlinTaskProperties(task: AbstractKotlinCompile<*>) {
|
internal fun PropertiesProvider.mapKotlinTaskProperties(task: AbstractKotlinCompile<*>) {
|
||||||
@@ -54,6 +56,12 @@ internal class PropertiesProvider(private val project: Project) {
|
|||||||
val coroutines: Coroutines?
|
val coroutines: Coroutines?
|
||||||
get() = property("kotlin.coroutines")?.let { Coroutines.byCompilerArgument(it) }
|
get() = property("kotlin.coroutines")?.let { Coroutines.byCompilerArgument(it) }
|
||||||
|
|
||||||
|
val buildReportEnabled: Boolean
|
||||||
|
get() = booleanProperty("kotlin.build.report.enable") ?: false
|
||||||
|
|
||||||
|
val buildReportDir: File?
|
||||||
|
get() = property("kotlin.build.report.dir")?.let { File(it) }
|
||||||
|
|
||||||
val incrementalJvm: Boolean?
|
val incrementalJvm: Boolean?
|
||||||
get() = booleanProperty("kotlin.incremental")
|
get() = booleanProperty("kotlin.incremental")
|
||||||
|
|
||||||
|
|||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. 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.internal.state
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.gradle.report.TaskExecutionResult
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
internal object TaskExecutionResults {
|
||||||
|
private val results = ConcurrentHashMap<String, TaskExecutionResult>()
|
||||||
|
|
||||||
|
operator fun get(taskPath: String): TaskExecutionResult? =
|
||||||
|
results[taskPath]
|
||||||
|
|
||||||
|
operator fun set(taskPath: String, result: TaskExecutionResult) {
|
||||||
|
results[taskPath] = result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
results.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
+176
@@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. 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 org.gradle.BuildAdapter
|
||||||
|
import org.gradle.BuildResult
|
||||||
|
import org.gradle.api.Task
|
||||||
|
import org.gradle.api.execution.TaskExecutionListener
|
||||||
|
import org.gradle.api.invocation.Gradle
|
||||||
|
import org.gradle.api.logging.Logger
|
||||||
|
import org.gradle.api.tasks.TaskState
|
||||||
|
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults
|
||||||
|
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
|
||||||
|
import java.io.File
|
||||||
|
import java.lang.StringBuilder
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
internal fun configureBuildReporter(gradle: Gradle, log: Logger) {
|
||||||
|
val rootProject = gradle.rootProject
|
||||||
|
val properties = PropertiesProvider(rootProject)
|
||||||
|
|
||||||
|
if (!properties.buildReportEnabled) return
|
||||||
|
|
||||||
|
val perfLogDir = properties.buildReportDir
|
||||||
|
?: rootProject.buildDir.resolve("reports/kotlin-build").apply { mkdirs() }
|
||||||
|
|
||||||
|
if (perfLogDir.isFile) {
|
||||||
|
log.error("Kotlin build report cannot be created: '$perfLogDir' is a file")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
perfLogDir.mkdirs()
|
||||||
|
val ts = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(Calendar.getInstance().time)
|
||||||
|
|
||||||
|
val perfReportFile = perfLogDir.resolve("${gradle.rootProject.name}-build-$ts.txt")
|
||||||
|
val reporter = KotlinBuildReporter(perfReportFile)
|
||||||
|
gradle.addBuildListener(reporter)
|
||||||
|
|
||||||
|
gradle.taskGraph.whenReady { graph ->
|
||||||
|
graph.allTasks.asSequence()
|
||||||
|
.filterIsInstance<AbstractKotlinCompile<*>>()
|
||||||
|
.forEach { it.reportExecutionResult = true }
|
||||||
|
}
|
||||||
|
|
||||||
|
log.kotlinDebug { "Configured Kotlin build reporter" }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class KotlinBuildReporter(private val perfReportFile: File) : BuildAdapter(), TaskExecutionListener {
|
||||||
|
init {
|
||||||
|
val dir = perfReportFile.parentFile
|
||||||
|
check(dir.isDirectory) { "$dir does not exist or is a file" }
|
||||||
|
check(!perfReportFile.isFile) { "Build report log file $perfReportFile exists already" }
|
||||||
|
}
|
||||||
|
|
||||||
|
private val taskStartNs = HashMap<Task, Long>()
|
||||||
|
private val kotlinTaskTimeNs = HashMap<Task, Long>()
|
||||||
|
private val tasksSb = StringBuilder()
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
private var allTasksTimeNs: Long = 0L
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun beforeExecute(task: Task) {
|
||||||
|
taskStartNs[task] = System.nanoTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun afterExecute(task: Task, state: TaskState) {
|
||||||
|
val startNs = taskStartNs[task] ?: return
|
||||||
|
|
||||||
|
val endNs = System.nanoTime()
|
||||||
|
val timeNs = endNs - startNs
|
||||||
|
allTasksTimeNs += timeNs
|
||||||
|
|
||||||
|
if (!task.javaClass.name.startsWith("org.jetbrains.kotlin")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinTaskTimeNs[task] = timeNs
|
||||||
|
tasksSb.appendln()
|
||||||
|
|
||||||
|
val skipMessage = state.skipMessage
|
||||||
|
if (skipMessage != null) {
|
||||||
|
tasksSb.appendln("$task was skipped: $skipMessage")
|
||||||
|
} else {
|
||||||
|
tasksSb.appendln("$task finished in ${formatTime(timeNs)}")
|
||||||
|
}
|
||||||
|
|
||||||
|
val path = task.path
|
||||||
|
val executionResult = TaskExecutionResults[path]
|
||||||
|
if (executionResult != null) {
|
||||||
|
tasksSb.appendln("Execution strategy: ${executionResult.executionStrategy}")
|
||||||
|
|
||||||
|
executionResult.icLogLines?.let { lines ->
|
||||||
|
tasksSb.appendln("Compilation log for $task:")
|
||||||
|
lines.forEach { tasksSb.appendln(" $it") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun buildFinished(result: BuildResult) {
|
||||||
|
val logger = result.gradle?.rootProject?.logger
|
||||||
|
try {
|
||||||
|
perfReportFile.writeText(taskOverview() + tasksSb.toString())
|
||||||
|
logger?.lifecycle("Kotlin build report is written to ${perfReportFile.canonicalPath}")
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
logger?.error("Could not write Kotlin build report to ${perfReportFile.canonicalPath}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun taskOverview(): String {
|
||||||
|
val sb = StringBuilder()
|
||||||
|
val kotlinTotalTimeNs = kotlinTaskTimeNs.values.sum()
|
||||||
|
val ktTaskPercent = (kotlinTotalTimeNs.toDouble() / allTasksTimeNs * 100).asString(1)
|
||||||
|
|
||||||
|
sb.appendln("Total time for Kotlin tasks: ${formatTime(kotlinTotalTimeNs)} ($ktTaskPercent % of all tasks time)")
|
||||||
|
|
||||||
|
val table = TextTable("Time", "% of Kotlin time", "Task")
|
||||||
|
kotlinTaskTimeNs.entries
|
||||||
|
.sortedByDescending { (_, timeNs) -> timeNs }
|
||||||
|
.forEach { (task, timeNs) ->
|
||||||
|
val percent = (timeNs.toDouble() / kotlinTotalTimeNs * 100).asString(1)
|
||||||
|
table.addRow(formatTime(timeNs), "$percent %", task.path)
|
||||||
|
}
|
||||||
|
table.printTo(sb)
|
||||||
|
sb.appendln()
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun formatTime(ns: Long): String {
|
||||||
|
val seconds = ns.toDouble() / 1_000_000_000
|
||||||
|
return seconds.asString(2) + " s"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Double.asString(decPoints: Int): String =
|
||||||
|
String.format("%.${decPoints}f", this)
|
||||||
|
|
||||||
|
private class TextTable(vararg columnNames: String) {
|
||||||
|
private val rows = ArrayList<List<String>>()
|
||||||
|
private val columnsCount = columnNames.size
|
||||||
|
private val maxLengths = IntArray(columnsCount) { columnNames[it].length }
|
||||||
|
|
||||||
|
init {
|
||||||
|
rows.add(columnNames.toList())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addRow(vararg row: String) {
|
||||||
|
check(row.size == columnsCount) { "Row size ${row.size} differs from columns count $columnsCount" }
|
||||||
|
rows.add(row.toList())
|
||||||
|
|
||||||
|
for ((i, col) in row.withIndex()) {
|
||||||
|
maxLengths[i] = max(maxLengths[i], col.length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun printTo(sb: StringBuilder) {
|
||||||
|
for (row in rows) {
|
||||||
|
sb.appendln()
|
||||||
|
for ((i, col) in row.withIndex()) {
|
||||||
|
if (i > 0) sb.append("|")
|
||||||
|
|
||||||
|
sb.append(col.padEnd(maxLengths[i], ' '))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. 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
|
||||||
|
|
||||||
|
internal class TaskExecutionResult(
|
||||||
|
val executionStrategy: String,
|
||||||
|
val icLogLines: List<String>?
|
||||||
|
)
|
||||||
+1
@@ -71,6 +71,7 @@ open class KotlinCompileCommon : AbstractKotlinCompile<K2MetadataCompilerArgumen
|
|||||||
val compilerRunner = compilerRunner()
|
val compilerRunner = compilerRunner()
|
||||||
val environment = GradleCompilerEnvironment(
|
val environment = GradleCompilerEnvironment(
|
||||||
computedCompilerClasspath, messageCollector, outputItemCollector,
|
computedCompilerClasspath, messageCollector, outputItemCollector,
|
||||||
|
reportExecutionResult = reportExecutionResult,
|
||||||
outputFiles = allOutputFiles()
|
outputFiles = allOutputFiles()
|
||||||
)
|
)
|
||||||
compilerRunner.runMetadataCompilerAsync(sourceRoots.kotlinSourceFiles, args, environment)
|
compilerRunner.runMetadataCompilerAsync(sourceRoots.kotlinSourceFiles, args, environment)
|
||||||
|
|||||||
+5
@@ -132,6 +132,9 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractKo
|
|||||||
logger.kotlinDebug { "Set $this.incremental=$value" }
|
logger.kotlinDebug { "Set $this.incremental=$value" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@get:Internal
|
||||||
|
var reportExecutionResult: Boolean = false
|
||||||
|
|
||||||
@get:Internal
|
@get:Internal
|
||||||
internal val buildHistoryFile: File
|
internal val buildHistoryFile: File
|
||||||
get() = File(taskBuildDirectory, "build-history.bin")
|
get() = File(taskBuildDirectory, "build-history.bin")
|
||||||
@@ -436,6 +439,7 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
|
|||||||
val environment = GradleCompilerEnvironment(
|
val environment = GradleCompilerEnvironment(
|
||||||
computedCompilerClasspath, messageCollector, outputItemCollector,
|
computedCompilerClasspath, messageCollector, outputItemCollector,
|
||||||
outputFiles = allOutputFiles(),
|
outputFiles = allOutputFiles(),
|
||||||
|
reportExecutionResult = reportExecutionResult,
|
||||||
incrementalCompilationEnvironment = icEnv
|
incrementalCompilationEnvironment = icEnv
|
||||||
)
|
)
|
||||||
compilerRunner.runJvmCompilerAsync(
|
compilerRunner.runJvmCompilerAsync(
|
||||||
@@ -593,6 +597,7 @@ open class Kotlin2JsCompile : AbstractKotlinCompile<K2JSCompilerArguments>(), Ko
|
|||||||
val environment = GradleCompilerEnvironment(
|
val environment = GradleCompilerEnvironment(
|
||||||
computedCompilerClasspath, messageCollector, outputItemCollector,
|
computedCompilerClasspath, messageCollector, outputItemCollector,
|
||||||
outputFiles = allOutputFiles(),
|
outputFiles = allOutputFiles(),
|
||||||
|
reportExecutionResult = reportExecutionResult,
|
||||||
incrementalCompilationEnvironment = icEnv
|
incrementalCompilationEnvironment = icEnv
|
||||||
)
|
)
|
||||||
compilerRunner.runJsCompilerAsync(sourceRoots.kotlinSourceFiles, commonSourceSet.toList(), args, environment)
|
compilerRunner.runJsCompilerAsync(sourceRoots.kotlinSourceFiles, commonSourceSet.toList(), args, environment)
|
||||||
|
|||||||
Reference in New Issue
Block a user