1
0
Fork 0
mirror of https://github.com/ethauvin/kobalt.git synced 2025-04-26 08:27:12 -07:00

More incremental task work.

This commit is contained in:
Cedric Beust 2015-12-20 10:27:53 +04:00
parent 8883055337
commit 8eacf476a1
5 changed files with 97 additions and 49 deletions

View file

@ -2,4 +2,12 @@ package com.beust.kobalt
import com.beust.kobalt.api.Project 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)

View file

@ -1,6 +1,7 @@
package com.beust.kobalt package com.beust.kobalt
import com.beust.kobalt.api.* import com.beust.kobalt.api.*
import com.beust.kobalt.api.annotation.IncrementalTask
import com.beust.kobalt.api.annotation.Task import com.beust.kobalt.api.annotation.Task
import com.beust.kobalt.internal.PluginInfo import com.beust.kobalt.internal.PluginInfo
import com.beust.kobalt.internal.TaskManager import com.beust.kobalt.internal.TaskManager
@ -63,31 +64,13 @@ public class Plugins @Inject constructor (val taskManagerProvider : Provider<Tas
plugin.apply(project, context) plugin.apply(project, context)
} }
var currentClass : Class<in Any> = plugin.javaClass findStaticTasks(plugin, Task::class.java, { method -> isValidTaskMethod(method)}).forEach {
taskManager.addAnnotationTask(plugin, it.first, it.second)
// 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. findStaticTasks(plugin, IncrementalTask::class.java,
// The following loop introspects the current plugin, finds all the tasks using the @Task annotation { method -> isValidIncrementalTaskMethod(method)}).forEach {
// and adds them to plugin.methodTasks taskManager.addIncrementalTask(plugin, it.first, it.second)
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
} }
} }
// Collect all the tasks from the task contributors // Collect all the tasks from the task contributors
@ -99,6 +82,47 @@ public class Plugins @Inject constructor (val taskManagerProvider : Provider<Tas
taskManager.computePluginTasks(plugins, projects) taskManager.computePluginTasks(plugins, projects)
} }
private fun <T: Annotation> findStaticTasks(plugin: IPlugin, klass: Class<T>, validate: (Method) -> Boolean)
: List<Pair<Method, T>> {
val result = arrayListOf<Pair<Method, T>>()
var currentClass : Class<in Any> = 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. * Make sure this task method has the right signature.
*/ */

View file

@ -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 // There can be multiple tasks by the same name (e.g. PackagingPlugin and AndroidPlugin both
// define "install"), so use a multimap // define "install"), so use a multimap
val tasksByNames = ArrayListMultimap.create<String, PluginTask>() val tasksByNames = ArrayListMultimap.create<String, PluginTask>()
tasks.filter { annotationTasks.filter {
it.project.name == project.name it.project.name == project.name
}.forEach { }.forEach {
tasksByNames.put(it.name, it) tasksByNames.put(it.name, it)
@ -221,33 +221,53 @@ public class TaskManager @Inject constructor(val args: Args) {
// Manage the tasks // 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<TaskAnnotation>() private val taskAnnotations = arrayListOf<TaskAnnotation>()
class TaskAnnotation(val method: Method, val plugin: IPlugin, val name: String, val description: String, class TaskAnnotation(val method: Method, val plugin: IPlugin, val name: String, val description: String,
val runBefore: Array<String>, val runAfter: Array<String>, val alwaysRunAfter: Array<String>) val runBefore: Array<String>, val runAfter: Array<String>, val alwaysRunAfter: Array<String>,
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) 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) 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) class PluginDynamicTask(val plugin: IPlugin, val task: DynamicTask)
val tasks = arrayListOf<PluginTask>() /** Tasks annotated with @Task or @IncrementalTask */
val annotationTasks = arrayListOf<PluginTask>()
/** Tasks provided by ITaskContributors */
val dynamicTasks = arrayListOf<PluginDynamicTask>() val dynamicTasks = arrayListOf<PluginDynamicTask>()
fun addStaticTask(plugin: IPlugin, method: Method, annotation: Task) = fun addAnnotationTask(plugin: IPlugin, method: Method, annotation: Task) =
taskAnnotations.add(toTaskAnnotation(method, plugin, annotation)) taskAnnotations.add(toTaskAnnotation(method, plugin, annotation))
fun addIncrementalTask(plugin: IPlugin, method: Method, annotation: IncrementalTask) = 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. * Turn all the static and dynamic tasks into plug-in tasks, which are then suitable to be executed.
*/ */
fun computePluginTasks(plugins: List<IPlugin>, projects: List<Project>) { fun computePluginTasks(plugins: List<IPlugin>, projects: List<Project>) {
addStaticTasks(projects) installAnnotationTasks(projects)
addDynamicTasks(projects) addDynamicTasks(projects)
} }
@ -261,28 +281,21 @@ public class TaskManager @Inject constructor(val args: Args) {
} }
} }
private fun addStaticTasks(projects: List<Project>) { private fun installAnnotationTasks(projects: List<Project>) {
taskAnnotations.forEach { staticTask -> taskAnnotations.forEach { staticTask ->
val method = staticTask.method val method = staticTask.method
val methodName = method.declaringClass.toString() + "." + method.name val methodName = method.declaringClass.toString() + "." + method.name
log(3, " Found task:${staticTask.name} method: $methodName") 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 val plugin = staticTask.plugin
projects.filter { plugin.accept(it) }.forEach { project -> 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) { task: (Project) -> TaskResult) {
addTask(plugin, project, annotation.name, annotation.description, annotation.runBefore.toList(), addTask(plugin, project, annotation.name, annotation.description, annotation.runBefore.toList(),
annotation.runAfter.toList(), annotation.alwaysRunAfter.toList(), task) annotation.runAfter.toList(), annotation.alwaysRunAfter.toList(), task)
@ -293,7 +306,7 @@ public class TaskManager @Inject constructor(val args: Args) {
runAfter: List<String> = listOf<String>(), runAfter: List<String> = listOf<String>(),
alwaysRunAfter: List<String> = listOf<String>(), alwaysRunAfter: List<String> = listOf<String>(),
task: (Project) -> TaskResult) { task: (Project) -> TaskResult) {
tasks.add( annotationTasks.add(
object : BasePluginTask(plugin, name, description, project) { object : BasePluginTask(plugin, name, description, project) {
override fun call(): TaskResult2<PluginTask> { override fun call(): TaskResult2<PluginTask> {
val taskResult = task(project) val taskResult = task(project)

View file

@ -214,7 +214,7 @@ private class Main @Inject constructor(
// List of tasks, --tasks // List of tasks, --tasks
// //
val tasksByPlugins = HashMultimap.create<String, PluginTask>() val tasksByPlugins = HashMultimap.create<String, PluginTask>()
taskManager.tasks.forEach { taskManager.annotationTasks.forEach {
tasksByPlugins.put(it.plugin.name, it) tasksByPlugins.put(it.plugin.name, it)
} }
val sb = StringBuffer("List of tasks\n") val sb = StringBuffer("List of tasks\n")

View file

@ -5,6 +5,7 @@ import com.beust.kobalt.Plugins
import com.beust.kobalt.api.IPlugin import com.beust.kobalt.api.IPlugin
import com.beust.kobalt.api.KobaltContext import com.beust.kobalt.api.KobaltContext
import com.beust.kobalt.api.Project import com.beust.kobalt.api.Project
import com.beust.kobalt.api.annotation.IncrementalTask
import com.beust.kobalt.api.annotation.Task import com.beust.kobalt.api.annotation.Task
import com.beust.kobalt.internal.TaskManager import com.beust.kobalt.internal.TaskManager
import com.beust.kobalt.internal.build.BuildFile 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) throw ex.cause ?: KobaltException(ex)
} }
} else { } else {
val taskAnnotation = method.getAnnotation(Task::class.java) method.getAnnotation(Task::class.java)?.let {
if (taskAnnotation != null) { taskManager.addAnnotationTask(defaultPlugin, method, it)
taskManager.addStaticTask(defaultPlugin, method, taskAnnotation) }
method.getAnnotation(IncrementalTask::class.java)?.let {
taskManager.addIncrementalTask(defaultPlugin, method, it)
} }
}} }}