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

Target engine refactor.

This commit is contained in:
Cedric Beust 2015-10-10 07:36:51 -07:00
parent 952464e3fa
commit 0fe686122f

View file

@ -6,9 +6,11 @@ import com.beust.kobalt.api.PluginTask
import com.beust.kobalt.api.Project import com.beust.kobalt.api.Project
import com.beust.kobalt.misc.KobaltLogger import com.beust.kobalt.misc.KobaltLogger
import com.beust.kobalt.maven.KobaltException import com.beust.kobalt.maven.KobaltException
import com.beust.kobalt.plugins
import com.google.common.collect.HashMultimap import com.google.common.collect.HashMultimap
import com.google.common.collect.TreeMultimap import com.google.common.collect.TreeMultimap
import java.util.HashSet import org.jetbrains.kotlin.cfg.pseudocode.or
import java.util.*
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -34,126 +36,73 @@ public class TaskManager @Inject constructor(val plugins: Plugins, val args: Arg
wrapAfter.put(task1, task2) wrapAfter.put(task1, task2)
} }
class TaskInfo(val id: String) { data class TaskInfo(val id: String) {
constructor(project: String, task: String) : this(project + ":" + task)
val project: String? val project: String?
get() = if (id.contains(":")) id.split(":").get(0) else null get() = if (id.contains(":")) id.split(":").get(0) else null
val task: String val task: String
get() = if (id.contains(":")) id.split(":").get(1) else id get() = if (id.contains(":")) id.split(":").get(1) else id
fun matches(projectName: String) = project == null || project == projectName
} }
public fun runTargets(targets: List<String>, projects: List<Project>) { public fun runTargets(targets: List<String>, projects: List<Project>) {
val tasksByNames = HashMultimap.create<String, PluginTask>() val tasksAlreadyRun = hashSetOf<String>()
projects.forEach { project -> projects.forEach { project ->
val projectName = project.name!!
val tasksByNames = hashMapOf<String, PluginTask>()
plugins.allTasks.filter {
it.project.name == project.name
}.forEach {
tasksByNames.put(it.name, it)
}
log(1, "") log(1, "")
log(1, " Building project ${project.name}") log(1, " Building project ${project.name}")
log(1, "") log(1, "")
val allTasksByNames = hashMapOf<String, PluginTask>() targets.forEach { target ->
plugins.allTasks.forEach { allTasksByNames.put(TaskInfo(it.name).task, it)} tasksAlreadyRun.add(TaskInfo(projectName, target).id)
//
// Locate all the tasks
//
plugins.allTasks.filter { it.project.name == project.name }.forEach { rt ->
tasksByNames.put(rt.name, rt)
if (rt.runBefore.size() > 0) {
rt.runBefore.forEach { d ->
runBefore.put(rt.name, d)
}
}
}
val freeTaskMap = hashMapOf<String, PluginTask>()
tasksByNames.keySet().forEach {
if (! runBefore.containsKey(it)) freeTaskMap.put(it, tasksByNames.get(it).elementAt(0))
}
log(2, "Free tasks: ${freeTaskMap.keySet()}")
log(2, "Dependent tasks:")
runBefore.keySet().forEach { t ->
log(2, " ${t} -> ${runBefore.get(t)}}")
}
//
// Find the tasks required to run the targets and add them to the dynamic graph
//
val transitiveClosure = hashSetOf<String>()
val seen = HashSet(targets)
val toProcess = HashSet(targets)
var done = false
while (!done) {
val newToProcess = hashSetOf<String>()
log(3, "toProcess size: " + toProcess.size())
toProcess.forEach { target ->
val pluginTask = allTasksByNames.get(target)
// Only calculate the transitive closure for this target if its plug-in accepts the
// current project
if (pluginTask != null && pluginTask.plugin.accept(project)) {
log(3, "Processing ${target}")
val actualTarget =
if (target.contains(":")) {
// The target specifies a project explicitly
target.split(":").let {
val projectName = it[0]
if (projectName == project.name) {
it[1]
} else {
null
}
}
} else {
target
}
if (actualTarget != null) {
wrapAfter.get(actualTarget).let {
newToProcess.addAll(it)
}
transitiveClosure.add(actualTarget)
val tasks = tasksByNames.get(actualTarget)
if (tasks.isEmpty()) {
throw KobaltException("Unknown task: ${target}")
}
tasks.forEach { task ->
val dependencyNames = runBefore.get(task.name)
dependencyNames.forEach { dependencyName ->
if (!seen.contains(dependencyName)) {
newToProcess.add(dependencyName)
seen.add(dependencyName)
}
}
}
} else {
log(2, "Target ${target} specified so not running it for project ${project.name}")
}
} else if (pluginTask == null) {
throw AssertionError("Should have found the task for $target")
}
}
done = newToProcess.isEmpty()
toProcess.clear()
toProcess.addAll(newToProcess)
}
//
// Create a dynamic graph with the transitive closure
//
val graph = DynamicGraph<PluginTask>() val graph = DynamicGraph<PluginTask>()
freeTaskMap.values().filter { transitiveClosure.contains(it.name) } forEach { graph.addNode(it) }
runBefore.entries().filter { val ti = TaskInfo(target)
transitiveClosure.contains(it.key) if (ti.matches(projectName)) {
}.forEach { entry -> val task = tasksByNames.get(ti.task)
plugins.findTasks(entry.key).filter { it.project.name == project.name }.forEach { from -> if (task != null && task.plugin.accept(project)) {
plugins.findTasks(entry.value).filter { it.project.name == project.name }.forEach { to -> //
if (from.project.name == to.project.name) { // Add free tasks as nodes to the graph
graph.addEdge(from, to) //
calculateFreeTasks(tasksByNames).forEach {
val thisTaskInfo = TaskInfo(projectName, it.name)
if (! tasksAlreadyRun.contains(thisTaskInfo.id)) {
graph.addNode(it)
tasksAlreadyRun.add(thisTaskInfo.id)
}
}
//
// Add the transitive closure of the current task as edges to the graph
//
calculateTransitiveClosure(project, tasksByNames, ti, task).forEach { pluginTask ->
val rb = runBefore.get(pluginTask.name)
rb.forEach {
val to = tasksByNames.get(it)
if (to != null) {
val taskInfo = TaskInfo(projectName, to.name)
if (! tasksAlreadyRun.contains(taskInfo.id)) {
graph.addEdge(pluginTask, to)
tasksAlreadyRun.add(taskInfo.id)
}
} else {
throw KobaltException("Should have found $it")
} }
} }
} }
} }
// //
// Run the dynamic graph // Now that we have a full graph, run it
// //
val factory = object : IThreadWorkerFactory<PluginTask> { val factory = object : IThreadWorkerFactory<PluginTask> {
override public fun createWorkers(nodes: List<PluginTask>): List<IWorker<PluginTask>> { override public fun createWorkers(nodes: List<PluginTask>): List<IWorker<PluginTask>> {
@ -170,6 +119,75 @@ public class TaskManager @Inject constructor(val plugins: Plugins, val args: Arg
} }
} }
} }
}
/**
* Find the free tasks of the graph.
*/
private fun calculateFreeTasks(tasksByNames: Map<String, PluginTask>): Collection<PluginTask> {
val freeTaskMap = hashMapOf<String, PluginTask>()
tasksByNames.keySet().forEach {
if (! runBefore.containsKey(it)) {
freeTaskMap.put(it, tasksByNames.get(it)!!)
}
}
log(2, "Free tasks: ${freeTaskMap.keySet()}")
log(2, "Dependent tasks:")
runBefore.keySet().forEach { t ->
log(2, " ${t} -> ${runBefore.get(t)}}")
}
return freeTaskMap.values()
}
/**
* Find the transitive closure for the given TaskInfo
*/
private fun calculateTransitiveClosure(project: Project, tasksByNames: Map<String, PluginTask>, ti: TaskInfo,
task: PluginTask): HashSet<PluginTask> {
log(3, "Processing ${ti.task}")
val transitiveClosure = hashSetOf<PluginTask>()
val seen = hashSetOf(ti.task)
val toProcess = hashSetOf(ti)
var done = false
while (! done) {
val newToProcess = hashSetOf<TaskInfo>()
log(3, "toProcess size: " + toProcess.size())
toProcess.forEach { target ->
// wrapAfter.get(ti.id).let {
// newToProcess.addAll(it)
// }
val currentTask = TaskInfo(project.name!!, target.task)
transitiveClosure.add(tasksByNames.get(currentTask.task)!!)
val task = tasksByNames.get(target.task)
if (task == null) {
throw KobaltException("Unknown task: ${target}")
} else {
val dependencyNames = runBefore.get(task.name)
dependencyNames.forEach { dependencyName ->
if (! seen.contains(dependencyName)) {
newToProcess.add(currentTask)
seen.add(dependencyName)
}
}
runBefore.get(task.name).forEach {
newToProcess.add(TaskInfo(project.name!!, it))
}
}
}
done = newToProcess.isEmpty()
toProcess.clear()
toProcess.addAll(newToProcess)
}
return transitiveClosure
}
}
class TaskWorker(val tasks: List<PluginTask>, val dryRun: Boolean) : IWorker<PluginTask>, KobaltLogger { class TaskWorker(val tasks: List<PluginTask>, val dryRun: Boolean) : IWorker<PluginTask>, KobaltLogger {
// override fun compareTo(other: IWorker2<PluginTask>): Int { // override fun compareTo(other: IWorker2<PluginTask>): Int {