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

GITHUB-230: Fail build if invalid project requested.

Fixes https://github.com/cbeust/kobalt/issues/230
This commit is contained in:
Cedric Beust 2016-06-06 22:08:30 -08:00
parent 5b2ed94655
commit ff1e3c2d13
3 changed files with 58 additions and 21 deletions

View file

@ -19,16 +19,16 @@ import java.nio.file.Files
import java.nio.file.Paths import java.nio.file.Paths
import java.util.* import java.util.*
data class TaskInfo(val taskName: String, var inputChecksum: String? = null, var outputChecksum: String? = null)
class BuildInfo(var tasks: List<TaskInfo>)
/** /**
* Manage the file .kobalt/buildInfo.json, which keeps track of input and output checksums to manage * Manage the file .kobalt/buildInfo.json, which keeps track of input and output checksums to manage
* incremental builds. * incremental builds.
*/ */
class IncrementalManager @Inject constructor(val args: Args, @Assisted val fileName : String) { class IncrementalManager @Inject constructor(val args: Args, @Assisted val fileName : String) {
private data class TaskInfo(val taskName: String, var inputChecksum: String? = null, var outputChecksum: String? = null)
private class BuildInfo(var tasks: List<TaskInfo>)
interface IFactory { interface IFactory {
fun create(@Assisted fileName: String = IncrementalManager.BUILD_INFO_FILE) : IncrementalManager fun create(@Assisted fileName: String = IncrementalManager.BUILD_INFO_FILE) : IncrementalManager
} }

View file

@ -81,11 +81,44 @@ class TaskManager @Inject constructor(val args: Args,
} }
} }
fun runTargets(passedTaskNames: List<String>, projects: List<Project>): RunTargetResult { fun runTargets(passedTaskNames: List<String>, allProjects: List<Project>): RunTargetResult {
val taskInfos = calculateDependentTaskNames(passedTaskNames, allProjects)
val projectsToRun = findProjectsToRun(passedTaskNames, allProjects)
return runProjects(taskInfos, projectsToRun)
}
/**
* Determine which projects to run based on the request tasks. Also make sure that all the requested projects
* exist.
*/
private fun findProjectsToRun(passedTaskNames: List<String>, projects: List<Project>) : List<Project> {
// Validate projects
val result = arrayListOf<Project>()
val projectMap = HashMap<String, Project>().apply {
projects.forEach { put(it.name, it)}
}
// Extract all the projects we need to run from the tasks
val taskInfos = calculateDependentTaskNames(passedTaskNames, projects)
taskInfos.forEach {
val p = it.project
if (p != null) {
if (! projectMap.containsKey(p)) {
throw KobaltException("Unknown project: ${it.project}")
}
result.add(projectMap[it.project]!!)
}
}
// If at least one task didn't specify a project, run them all
return if (result.any()) result else projects
}
private fun runProjects(taskInfos: List<TaskInfo>, projects: List<Project>) : RunTargetResult {
var result = 0 var result = 0
val failedProjects = hashSetOf<String>() val failedProjects = hashSetOf<String>()
val messages = Collections.synchronizedList(arrayListOf<String>()) val messages = Collections.synchronizedList(arrayListOf<String>())
val taskNames = calculateDependentTaskNames(passedTaskNames, projects)
projects.forEach { project -> projects.forEach { project ->
AsciiArt.logBox("Building ${project.name}") AsciiArt.logBox("Building ${project.name}")
@ -112,7 +145,7 @@ class TaskManager @Inject constructor(val args: Args,
log(3, " $it: " + tasksByNames.get(it)) log(3, " $it: " + tasksByNames.get(it))
} }
val graph = createGraph2(project.name, taskNames, tasksByNames, val graph = createGraph2(project.name, taskInfos, tasksByNames,
dependsOn, reverseDependsOn, runBefore, runAfter, alwaysRunAfter, dependsOn, reverseDependsOn, runBefore, runAfter, alwaysRunAfter,
{ task: ITask -> task.name }, { task: ITask -> task.name },
{ task: ITask -> task.plugin.accept(project) }) { task: ITask -> task.plugin.accept(project) })
@ -138,6 +171,7 @@ class TaskManager @Inject constructor(val args: Args,
} }
} }
} }
return RunTargetResult(result, messages) return RunTargetResult(result, messages)
} }
@ -146,11 +180,11 @@ class TaskManager @Inject constructor(val args: Args,
* see if that project depends on others and if it does, invoke these tasks on all of them. This * see if that project depends on others and if it does, invoke these tasks on all of them. This
* function returns all these task names (including dependent). * function returns all these task names (including dependent).
*/ */
private fun calculateDependentTaskNames(taskNames: List<String>, projects: List<Project>): List<String> { private fun calculateDependentTaskNames(taskNames: List<String>, projects: List<Project>): List<TaskInfo> {
val projectMap = hashMapOf<String, Project>().apply { val projectMap = hashMapOf<String, Project>().apply {
projects.forEach { put(it.name, it)} projects.forEach { put(it.name, it)}
} }
val result = ArrayList(taskNames) val result = ArrayList(taskNames.map { TaskInfo(it) })
val toProcess = ArrayList(taskNames) val toProcess = ArrayList(taskNames)
val newToProcess = arrayListOf<String>() val newToProcess = arrayListOf<String>()
val seen = hashSetOf<String>() val seen = hashSetOf<String>()
@ -161,7 +195,7 @@ class TaskManager @Inject constructor(val args: Args,
projectMap[ti.project]?.let { project -> projectMap[ti.project]?.let { project ->
project.projectExtra.dependsOn.forEach { dp -> project.projectExtra.dependsOn.forEach { dp ->
val newTask = TaskInfo(dp.projectName, ti.taskName).id val newTask = TaskInfo(dp.projectName, ti.taskName).id
result.add(newTask) result.add(TaskInfo(newTask))
if (! seen.contains(newTask)) { if (! seen.contains(newTask)) {
newToProcess.add(newTask) newToProcess.add(newTask)
seen.add(newTask) seen.add(newTask)
@ -188,7 +222,8 @@ class TaskManager @Inject constructor(val args: Args,
* we'll be adding to the graph while @toName extracts the name of a node. * we'll be adding to the graph while @toName extracts the name of a node.
*/ */
@VisibleForTesting @VisibleForTesting
fun <T> createGraph2(projectName: String, taskNames: List<String>, nodeMap: Multimap<String, T>, fun <T> createGraph2(projectName: String, passedTasks: List<TaskInfo>,
nodeMap: Multimap<String, T>,
dependsOn: Multimap<String, String>, dependsOn: Multimap<String, String>,
reverseDependsOn: Multimap<String, String>, reverseDependsOn: Multimap<String, String>,
runBefore: Multimap<String, String>, runBefore: Multimap<String, String>,
@ -214,9 +249,9 @@ class TaskManager @Inject constructor(val args: Args,
} }
// //
// Turn the task names into the more useful TaskInfo and do some sanity checking on the way // Keep only the tasks we need to run.
// //
val taskInfos = taskNames.map { TaskInfo(it) }.filter { val taskInfos = passedTasks.filter {
if (!nodeMap.keys().contains(it.taskName)) { if (!nodeMap.keys().contains(it.taskName)) {
throw KobaltException("Unknown task: $it") throw KobaltException("Unknown task: $it")
} }
@ -300,6 +335,7 @@ class TaskManager @Inject constructor(val args: Args,
// runBefore and runAfter (task ordering) are only considered for explicit tasks (tasks that were // runBefore and runAfter (task ordering) are only considered for explicit tasks (tasks that were
// explicitly requested by the user) // explicitly requested by the user)
// //
passedTasks.map { it.id }.let { taskNames ->
runBefore[taskName].forEach { from -> runBefore[taskName].forEach { from ->
if (taskNames.contains(from)) { if (taskNames.contains(from)) {
addEdge(result, from, taskName, newToProcess, "runBefore") addEdge(result, from, taskName, newToProcess, "runBefore")
@ -310,6 +346,7 @@ class TaskManager @Inject constructor(val args: Args,
addEdge(result, taskName, to, newToProcess, "runAfter") addEdge(result, taskName, to, newToProcess, "runAfter")
} }
} }
}
seen.add(taskName) seen.add(taskName)
} }

View file

@ -80,7 +80,7 @@ class TaskManagerTest @Inject constructor(val taskManager: TaskManager) {
} }
} }
val graph = taskManager.createGraph2("", tasks, dependencies, val graph = taskManager.createGraph2("", tasks.map { TaskManager.TaskInfo(it) }, dependencies,
dependsOn, reverseDependsOn, runBefore, runAfter, alwaysRunAfter, dependsOn, reverseDependsOn, runBefore, runAfter, alwaysRunAfter,
{ it }, { t -> true }) { it }, { t -> true })
val result = DryRunGraphExecutor(graph).run() val result = DryRunGraphExecutor(graph).run()