From 007bb31ee31b933092f16fedef739c8eb7b760b0 Mon Sep 17 00:00:00 2001 From: Cedric Beust Date: Sat, 30 Jul 2016 07:36:03 -0700 Subject: [PATCH] Introduce build listeners and build report contributors. --- .../com/beust/kobalt/api/IBuildListener.kt | 9 +++++ .../kobalt/api/IBuildReportContributor.kt | 8 ++++ .../com/beust/kobalt/api/IPluginActor.kt | 3 ++ .../beust/kobalt/internal/BuildListeners.kt | 39 +++++++++++++++++++ .../beust/kobalt/internal/KobaltPluginXml.kt | 9 ++++- .../com/beust/kobalt/internal/TaskManager.kt | 26 ++++++++----- src/main/kotlin/com/beust/kobalt/Main.kt | 11 ++---- 7 files changed, 86 insertions(+), 19 deletions(-) create mode 100644 modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/IBuildListener.kt create mode 100644 modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/IBuildReportContributor.kt create mode 100644 modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/BuildListeners.kt diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/IBuildListener.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/IBuildListener.kt new file mode 100644 index 00000000..bba9a57b --- /dev/null +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/IBuildListener.kt @@ -0,0 +1,9 @@ +package com.beust.kobalt.api + +/** + * Plug-ins that listen to build events. + */ +interface IBuildListener : IListener { + fun taskStart(project: Project, context: KobaltContext, taskName: String) + fun taskEnd(project: Project, context: KobaltContext, taskName: String) +} diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/IBuildReportContributor.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/IBuildReportContributor.kt new file mode 100644 index 00000000..130c50d2 --- /dev/null +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/IBuildReportContributor.kt @@ -0,0 +1,8 @@ +package com.beust.kobalt.api + +/** + * Plug-ins that produce build reports. + */ +interface IBuildReportContributor : IContributor { + fun generateReport(context: KobaltContext) +} diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/IPluginActor.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/IPluginActor.kt index 5cf11f87..96d54218 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/IPluginActor.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/IPluginActor.kt @@ -10,3 +10,6 @@ interface IPluginActor { interface IContributor : IPluginActor interface IInterceptor : IPluginActor + +interface IListener : IPluginActor + diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/BuildListeners.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/BuildListeners.kt new file mode 100644 index 00000000..535f16e1 --- /dev/null +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/BuildListeners.kt @@ -0,0 +1,39 @@ +package com.beust.kobalt.internal + +import com.beust.kobalt.Args +import com.beust.kobalt.AsciiArt +import com.beust.kobalt.api.* +import com.beust.kobalt.misc.log +import java.util.concurrent.ConcurrentHashMap + +/** + * Record timings and display them at the end of the build. + */ +class BuildListeners : IBuildListener, IBuildReportContributor { + class ProfilerInfo(val taskName: String, val durationMillis: Long) + + private val startTimes = ConcurrentHashMap() + private val timings = arrayListOf() + + override fun taskStart(project: Project, context: KobaltContext, taskName: String) { + startTimes.put(taskName, System.currentTimeMillis()) + } + + override fun taskEnd(project: Project, context: KobaltContext, taskName: String) { + startTimes[taskName]?.let { + timings.add(ProfilerInfo(taskName, System.currentTimeMillis() - it)) + } + } + + override fun generateReport(context: KobaltContext) { + val profiling = Kobalt.INJECTOR.getInstance(Args::class.java).profiling + if (profiling) { + log(1, "\n" + AsciiArt.horizontalSingleLine + " Timings (in seconds)") + timings.sortedByDescending { it.durationMillis }.forEach { + log(1, String.format("%1$10.2f", it.durationMillis.toDouble() / 1000) + + " " + it.taskName) + } + log(1, "\n") + } + } +} diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/KobaltPluginXml.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/KobaltPluginXml.kt index 9ba885a8..1454f0fd 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/KobaltPluginXml.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/KobaltPluginXml.kt @@ -96,6 +96,8 @@ class PluginInfo(val xml: KobaltPluginXml, val pluginClassLoader: ClassLoader?, val mavenIdInterceptors = arrayListOf() val jvmFlagContributors = arrayListOf() val localMavenRepoPathInterceptors = arrayListOf() + val buildListeners = arrayListOf() + val buildReportContributors = arrayListOf() // Note: intentionally repeating them here even though they are defined by our base class so // that this class always contains the full list of contributors and interceptors @@ -212,6 +214,8 @@ class PluginInfo(val xml: KobaltPluginXml, val pluginClassLoader: ClassLoader?, if (this is ITestJvmFlagInterceptor) testJvmFlagInterceptors.add(this) if (this is IJvmFlagContributor) jvmFlagContributors.add(this) if (this is ILocalMavenRepoPathInterceptor) localMavenRepoPathInterceptors.add(this) + if (this is IBuildListener) buildListeners.add(this) + if (this is IBuildReportContributor) buildReportContributors.add(this) } } } @@ -225,7 +229,8 @@ class PluginInfo(val xml: KobaltPluginXml, val pluginClassLoader: ClassLoader?, testSourceDirContributors, buildConfigFieldContributors, taskContributors, incrementalTaskContributors, assemblyContributors, incrementalAssemblyContributors, testJvmFlagInterceptors, - jvmFlagContributors, localMavenRepoPathInterceptors + jvmFlagContributors, localMavenRepoPathInterceptors, buildListeners, + buildReportContributors ).forEach { it.forEach { it.cleanUpActors() @@ -266,6 +271,8 @@ class PluginInfo(val xml: KobaltPluginXml, val pluginClassLoader: ClassLoader?, testJvmFlagInterceptors.addAll(pluginInfo.testJvmFlagInterceptors) jvmFlagContributors.addAll(pluginInfo.jvmFlagContributors) localMavenRepoPathInterceptors.addAll(pluginInfo.localMavenRepoPathInterceptors) + buildListeners.addAll(pluginInfo.buildListeners) + buildReportContributors.addAll(pluginInfo.buildReportContributors) } } diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/TaskManager.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/TaskManager.kt index 26f39a0c..cd9a79e0 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/TaskManager.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/TaskManager.kt @@ -17,7 +17,8 @@ import javax.inject.Singleton @Singleton class TaskManager @Inject constructor(val args: Args, - val incrementalManagerFactory: IncrementalManager.IFactory) { + val incrementalManagerFactory: IncrementalManager.IFactory, + val pluginInfo: PluginInfo) { private val dependsOn = TreeMultimap.create() private val reverseDependsOn = TreeMultimap.create() private val runBefore = TreeMultimap.create() @@ -168,7 +169,7 @@ class TaskManager @Inject constructor(val args: Args, val factory = object : IThreadWorkerFactory { override fun createWorkers(nodes: Collection) - = nodes.map { TaskWorker(listOf(it), args.dryRun, messages) } + = nodes.map { TaskWorker(listOf(it), args.dryRun, messages, pluginInfo) } } val executor = DynamicGraphExecutor(graph, factory) @@ -534,8 +535,13 @@ class TaskManager @Inject constructor(val args: Args, ///// } -class TaskWorker(val tasks: List, val dryRun: Boolean, val timings: MutableList) - : IWorker { +class TaskWorker(val tasks: List, val dryRun: Boolean, val pluginInfo: PluginInfo) : IWorker { + + private fun runBuildListeners(project: Project, context: KobaltContext, taskName: String, start: Boolean) { + context.pluginInfo.buildListeners.forEach { + if (start) it.taskStart(project, context, taskName) else it.taskEnd(project, context, taskName) + } + } override fun call() : TaskResult2 { if (tasks.size > 0) { @@ -545,14 +551,14 @@ class TaskWorker(val tasks: List, val dryRun: Boolean, val timings: Mutab } var success = true val errorMessages = arrayListOf() + val context = Kobalt.context!! tasks.forEach { val name = it.project.name + ":" + it.name - val time = benchmarkMillis { - val tr = if (dryRun) TaskResult() else it.call() - success = success and tr.success - if (tr.errorMessage != null) errorMessages.add(tr.errorMessage) - } - timings.add(TaskManager.ProfilerInfo(name, time.first)) + runBuildListeners(it.project, context, name, start = true) + val tr = if (dryRun) TaskResult() else it.call() + runBuildListeners(it.project, context, name, start = false) + success = success and tr.success + if (tr.errorMessage != null) errorMessages.add(tr.errorMessage) } return TaskResult2(success, errorMessages.joinToString("\n"), tasks[0]) } diff --git a/src/main/kotlin/com/beust/kobalt/Main.kt b/src/main/kotlin/com/beust/kobalt/Main.kt index 4bdc5547..0c1117bb 100644 --- a/src/main/kotlin/com/beust/kobalt/Main.kt +++ b/src/main/kotlin/com/beust/kobalt/Main.kt @@ -213,14 +213,9 @@ private class Main @Inject constructor( // Shutdown all plug-ins plugins.shutdownPlugins() - // Display timings if requested - if (args.profiling) { - log(1, "\n" + AsciiArt.horizontalSingleLine + " Timings (in seconds)") - runTargetResult.timings.sortedByDescending { it.durationMillis }.forEach { - log(1, String.format("%1$10.2f", it.durationMillis.toDouble() / 1000) - + " " + it.taskName) - } - log(1, "\n") + // Run the build report contributors + pluginInfo.buildReportContributors.forEach { + it.generateReport(Kobalt.context!!) } } }