From 8eacf476a141778e78fbc2d38e8d73edfd2e15d2 Mon Sep 17 00:00:00 2001 From: Cedric Beust Date: Sun, 20 Dec 2015 10:27:53 +0400 Subject: [PATCH] More incremental task work. --- .../com/beust/kobalt/IncrementalTaskInfo.kt | 10 ++- .../main/kotlin/com/beust/kobalt/Plugins.kt | 72 ++++++++++++------- .../com/beust/kobalt/internal/TaskManager.kt | 53 ++++++++------ src/main/kotlin/com/beust/kobalt/Main.kt | 2 +- .../com/beust/kobalt/app/BuildScriptUtil.kt | 9 ++- 5 files changed, 97 insertions(+), 49 deletions(-) diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/IncrementalTaskInfo.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/IncrementalTaskInfo.kt index c5c56a8a..6e07fa49 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/IncrementalTaskInfo.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/IncrementalTaskInfo.kt @@ -2,4 +2,12 @@ package com.beust.kobalt import com.beust.kobalt.api.Project -class IncrementalTaskInfo(val inputChecksum: String, val outputChecksum: String, task: (Project) -> TaskResult) +/** + * @param inputChecksum The checksum for the input to this task. It gets compared against the checksum of + * if they differ, the task gets run. If they are equal, outputChecksums are then compared. + * @param outputChecksum The checksum for the output of this task. If null, the output is absent + * and the task will be run. If non null, it gets compared against the checksum of the previous run and + * if they differ, the task gets run. + * @param task The task to run. + */ +class IncrementalTaskInfo(val inputChecksum: String, val outputChecksum: String?, val task: (Project) -> TaskResult) diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/Plugins.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/Plugins.kt index cb9a199c..2f63f1c2 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/Plugins.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/Plugins.kt @@ -1,6 +1,7 @@ package com.beust.kobalt import com.beust.kobalt.api.* +import com.beust.kobalt.api.annotation.IncrementalTask import com.beust.kobalt.api.annotation.Task import com.beust.kobalt.internal.PluginInfo import com.beust.kobalt.internal.TaskManager @@ -63,31 +64,13 @@ public class Plugins @Inject constructor (val taskManagerProvider : Provider = plugin.javaClass - - // Tasks can come from two different places: plugin classes and build files. - // When a task is read from a build file, ScriptCompiler adds it right away to plugin.methodTasks. - // The following loop introspects the current plugin, finds all the tasks using the @Task annotation - // and adds them to plugin.methodTasks - while (! (currentClass.equals(Any::class.java))) { - currentClass.declaredMethods.map { - Pair(it, it.getAnnotation(Task::class.java)) - }.filter { - it.second != null - }.filter { - isValidTaskMethod(it.first) - }.forEach { - if (Modifier.isPrivate(it.first.modifiers)) { - throw KobaltException("A task method cannot be private: ${it.first}") - } - val annotation = it.second - - taskManager.addStaticTask(plugin, it.first, annotation) - } - - currentClass = currentClass.superclass + findStaticTasks(plugin, Task::class.java, { method -> isValidTaskMethod(method)}).forEach { + taskManager.addAnnotationTask(plugin, it.first, it.second) + } + findStaticTasks(plugin, IncrementalTask::class.java, + { method -> isValidIncrementalTaskMethod(method)}).forEach { + taskManager.addIncrementalTask(plugin, it.first, it.second) } - } // Collect all the tasks from the task contributors @@ -99,6 +82,47 @@ public class Plugins @Inject constructor (val taskManagerProvider : Provider findStaticTasks(plugin: IPlugin, klass: Class, validate: (Method) -> Boolean) + : List> { + val result = arrayListOf>() + + var currentClass : Class = plugin.javaClass + + // Tasks can come from two different places: plugin classes and build files. + // When a task is read from a build file, ScriptCompiler adds it right away to plugin.methodTasks. + // The following loop introspects the current plugin, finds all the tasks using the @Task annotation + // and adds them to plugin.methodTasks + while (currentClass != null && ! (klass.equals(currentClass))) { + currentClass.declaredMethods.map { + Pair(it, it.getAnnotation(klass)) + }.filter { + it.second != null + }.filter { + validate(it.first) + }.forEach { + if (Modifier.isPrivate(it.first.modifiers)) { + throw KobaltException("A task method cannot be private: ${it.first}") + } + result.add(it) + } + + currentClass = currentClass.superclass + } + return result + } + + /** + * Make sure this task method has the right signature. + */ + private fun isValidIncrementalTaskMethod(method: Method): Boolean { + val t = "Task ${method.declaringClass.simpleName}.${method.name}: " + + if (method.returnType != IncrementalTaskInfo::class.java) { + throw IllegalArgumentException("${t}should return a IncrementalTaskInfo") + } + return true + } + /** * Make sure this task method has the right signature. */ 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 5fe99237..edbff9d9 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 @@ -53,7 +53,7 @@ public class TaskManager @Inject constructor(val args: Args) { // There can be multiple tasks by the same name (e.g. PackagingPlugin and AndroidPlugin both // define "install"), so use a multimap val tasksByNames = ArrayListMultimap.create() - tasks.filter { + annotationTasks.filter { it.project.name == project.name }.forEach { tasksByNames.put(it.name, it) @@ -221,33 +221,53 @@ public class TaskManager @Inject constructor(val args: Args) { // Manage the tasks // - // Both @Task and @IncrementalTask get stored as a TaskAnnotation so they can be treated uniformly + // Both @Task and @IncrementalTask get stored as a TaskAnnotation so they can be treated uniformly. + // They only differ in the way they are invoked (see below) private val taskAnnotations = arrayListOf() class TaskAnnotation(val method: Method, val plugin: IPlugin, val name: String, val description: String, - val runBefore: Array, val runAfter: Array, val alwaysRunAfter: Array) + val runBefore: Array, val runAfter: Array, val alwaysRunAfter: Array, + val callable: (Project) -> TaskResult) + /** + * Invoking a @Task means simply calling the method and returning its result TaskResult. + */ fun toTaskAnnotation(method: Method, plugin: IPlugin, ta: Task) - = TaskAnnotation(method, plugin, ta.name, ta.description, ta.runBefore, ta.runAfter, ta.alwaysRunAfter) + = TaskAnnotation(method, plugin, ta.name, ta.description, ta.runBefore, ta.runAfter, ta.alwaysRunAfter, + { project -> + method.invoke(plugin, project) as TaskResult + }) + + /** + * Invoking an @IncrementalTask means invoking the method and then deciding what to do based on the content + * of the returned IncrementalTaskInfo. + */ fun toTaskAnnotation(method: Method, plugin: IPlugin, ta: IncrementalTask) - = TaskAnnotation(method, plugin, ta.name, ta.description, ta.runBefore, ta.runAfter, ta.alwaysRunAfter) + = TaskAnnotation(method, plugin, ta.name, ta.description, ta.runBefore, ta.runAfter, ta.alwaysRunAfter, + { project -> + val iit = method.invoke(plugin, project) as IncrementalTaskInfo + iit.task(project) + }) class PluginDynamicTask(val plugin: IPlugin, val task: DynamicTask) - val tasks = arrayListOf() + /** Tasks annotated with @Task or @IncrementalTask */ + val annotationTasks = arrayListOf() + + /** Tasks provided by ITaskContributors */ val dynamicTasks = arrayListOf() - fun addStaticTask(plugin: IPlugin, method: Method, annotation: Task) = + fun addAnnotationTask(plugin: IPlugin, method: Method, annotation: Task) = taskAnnotations.add(toTaskAnnotation(method, plugin, annotation)) fun addIncrementalTask(plugin: IPlugin, method: Method, annotation: IncrementalTask) = - taskAnnotations.add(toTaskAnnotation(method, plugin, annotation)) + taskAnnotations.add(toTaskAnnotation(method, plugin, annotation)) /** * Turn all the static and dynamic tasks into plug-in tasks, which are then suitable to be executed. */ fun computePluginTasks(plugins: List, projects: List) { - addStaticTasks(projects) + installAnnotationTasks(projects) addDynamicTasks(projects) } @@ -261,28 +281,21 @@ public class TaskManager @Inject constructor(val args: Args) { } } - private fun addStaticTasks(projects: List) { + private fun installAnnotationTasks(projects: List) { taskAnnotations.forEach { staticTask -> val method = staticTask.method val methodName = method.declaringClass.toString() + "." + method.name log(3, " Found task:${staticTask.name} method: $methodName") - fun toTask(m: Method, project: Project, plugin: IPlugin): (Project) -> TaskResult { - val result: (Project) -> TaskResult = { - m.invoke(plugin, project) as TaskResult - } - return result - } - val plugin = staticTask.plugin projects.filter { plugin.accept(it) }.forEach { project -> - addStaticTask(plugin, project, staticTask, toTask(method, project, plugin)) + addAnnotationTask(plugin, project, staticTask, staticTask.callable) } } } - private fun addStaticTask(plugin: IPlugin, project: Project, annotation: TaskAnnotation, + private fun addAnnotationTask(plugin: IPlugin, project: Project, annotation: TaskAnnotation, task: (Project) -> TaskResult) { addTask(plugin, project, annotation.name, annotation.description, annotation.runBefore.toList(), annotation.runAfter.toList(), annotation.alwaysRunAfter.toList(), task) @@ -293,7 +306,7 @@ public class TaskManager @Inject constructor(val args: Args) { runAfter: List = listOf(), alwaysRunAfter: List = listOf(), task: (Project) -> TaskResult) { - tasks.add( + annotationTasks.add( object : BasePluginTask(plugin, name, description, project) { override fun call(): TaskResult2 { val taskResult = task(project) diff --git a/src/main/kotlin/com/beust/kobalt/Main.kt b/src/main/kotlin/com/beust/kobalt/Main.kt index fceeb162..6de5f819 100644 --- a/src/main/kotlin/com/beust/kobalt/Main.kt +++ b/src/main/kotlin/com/beust/kobalt/Main.kt @@ -214,7 +214,7 @@ private class Main @Inject constructor( // List of tasks, --tasks // val tasksByPlugins = HashMultimap.create() - taskManager.tasks.forEach { + taskManager.annotationTasks.forEach { tasksByPlugins.put(it.plugin.name, it) } val sb = StringBuffer("List of tasks\n") diff --git a/src/main/kotlin/com/beust/kobalt/app/BuildScriptUtil.kt b/src/main/kotlin/com/beust/kobalt/app/BuildScriptUtil.kt index e6b49b32..1a6bfd7c 100644 --- a/src/main/kotlin/com/beust/kobalt/app/BuildScriptUtil.kt +++ b/src/main/kotlin/com/beust/kobalt/app/BuildScriptUtil.kt @@ -5,6 +5,7 @@ import com.beust.kobalt.Plugins import com.beust.kobalt.api.IPlugin import com.beust.kobalt.api.KobaltContext import com.beust.kobalt.api.Project +import com.beust.kobalt.api.annotation.IncrementalTask import com.beust.kobalt.api.annotation.Task import com.beust.kobalt.internal.TaskManager import com.beust.kobalt.internal.build.BuildFile @@ -85,9 +86,11 @@ class BuildScriptUtil @Inject constructor(val plugins: Plugins, val files: KFile throw ex.cause ?: KobaltException(ex) } } else { - val taskAnnotation = method.getAnnotation(Task::class.java) - if (taskAnnotation != null) { - taskManager.addStaticTask(defaultPlugin, method, taskAnnotation) + method.getAnnotation(Task::class.java)?.let { + taskManager.addAnnotationTask(defaultPlugin, method, it) + } + method.getAnnotation(IncrementalTask::class.java)?.let { + taskManager.addIncrementalTask(defaultPlugin, method, it) } }}