mirror of
https://github.com/ethauvin/kobalt.git
synced 2025-04-26 00:17:11 -07:00
Target engine refactor.
This commit is contained in:
parent
952464e3fa
commit
0fe686122f
1 changed files with 127 additions and 109 deletions
|
@ -6,9 +6,11 @@ import com.beust.kobalt.api.PluginTask
|
|||
import com.beust.kobalt.api.Project
|
||||
import com.beust.kobalt.misc.KobaltLogger
|
||||
import com.beust.kobalt.maven.KobaltException
|
||||
import com.beust.kobalt.plugins
|
||||
import com.google.common.collect.HashMultimap
|
||||
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.Singleton
|
||||
|
||||
|
@ -34,141 +36,157 @@ public class TaskManager @Inject constructor(val plugins: Plugins, val args: Arg
|
|||
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?
|
||||
get() = if (id.contains(":")) id.split(":").get(0) else null
|
||||
val task: String
|
||||
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>) {
|
||||
val tasksByNames = HashMultimap.create<String, PluginTask>()
|
||||
|
||||
val tasksAlreadyRun = hashSetOf<String>()
|
||||
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, " Building project ${project.name}")
|
||||
log(1, "")
|
||||
|
||||
val allTasksByNames = hashMapOf<String, PluginTask>()
|
||||
plugins.allTasks.forEach { allTasksByNames.put(TaskInfo(it.name).task, it)}
|
||||
targets.forEach { target ->
|
||||
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 graph = DynamicGraph<PluginTask>()
|
||||
|
||||
val freeTaskMap = hashMapOf<String, PluginTask>()
|
||||
tasksByNames.keySet().forEach {
|
||||
if (! runBefore.containsKey(it)) freeTaskMap.put(it, tasksByNames.get(it).elementAt(0))
|
||||
}
|
||||
val ti = TaskInfo(target)
|
||||
if (ti.matches(projectName)) {
|
||||
val task = tasksByNames.get(ti.task)
|
||||
if (task != null && task.plugin.accept(project)) {
|
||||
//
|
||||
// Add free tasks as nodes to the graph
|
||||
//
|
||||
calculateFreeTasks(tasksByNames).forEach {
|
||||
val thisTaskInfo = TaskInfo(projectName, it.name)
|
||||
if (! tasksAlreadyRun.contains(thisTaskInfo.id)) {
|
||||
graph.addNode(it)
|
||||
tasksAlreadyRun.add(thisTaskInfo.id)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
//
|
||||
// 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 {
|
||||
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)
|
||||
}
|
||||
throw KobaltException("Should have found $it")
|
||||
}
|
||||
}
|
||||
} 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>()
|
||||
freeTaskMap.values().filter { transitiveClosure.contains(it.name) } forEach { graph.addNode(it) }
|
||||
runBefore.entries().filter {
|
||||
transitiveClosure.contains(it.key)
|
||||
}.forEach { entry ->
|
||||
plugins.findTasks(entry.key).filter { it.project.name == project.name }.forEach { from ->
|
||||
plugins.findTasks(entry.value).filter { it.project.name == project.name }.forEach { to ->
|
||||
if (from.project.name == to.project.name) {
|
||||
graph.addEdge(from, to)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Run the dynamic graph
|
||||
//
|
||||
val factory = object : IThreadWorkerFactory<PluginTask> {
|
||||
override public fun createWorkers(nodes: List<PluginTask>): List<IWorker<PluginTask>> {
|
||||
val result = arrayListOf<IWorker<PluginTask>>()
|
||||
nodes.forEach {
|
||||
result.add(TaskWorker(arrayListOf(it), args.dryRun))
|
||||
//
|
||||
// Now that we have a full graph, run it
|
||||
//
|
||||
val factory = object : IThreadWorkerFactory<PluginTask> {
|
||||
override public fun createWorkers(nodes: List<PluginTask>): List<IWorker<PluginTask>> {
|
||||
val result = arrayListOf<IWorker<PluginTask>>()
|
||||
nodes.forEach {
|
||||
result.add(TaskWorker(arrayListOf(it), args.dryRun))
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
val executor = DynamicGraphExecutor(graph, factory)
|
||||
executor.run()
|
||||
}
|
||||
}
|
||||
|
||||
val executor = DynamicGraphExecutor(graph, factory)
|
||||
executor.run()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue