1
0
Fork 0
mirror of https://github.com/ethauvin/kobalt.git synced 2025-04-26 00:17:11 -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.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
* incremental builds.
*/
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 {
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
val failedProjects = hashSetOf<String>()
val messages = Collections.synchronizedList(arrayListOf<String>())
val taskNames = calculateDependentTaskNames(passedTaskNames, projects)
projects.forEach { project ->
AsciiArt.logBox("Building ${project.name}")
@ -112,7 +145,7 @@ class TaskManager @Inject constructor(val args: Args,
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,
{ task: ITask -> task.name },
{ task: ITask -> task.plugin.accept(project) })
@ -138,6 +171,7 @@ class TaskManager @Inject constructor(val args: Args,
}
}
}
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
* 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 {
projects.forEach { put(it.name, it)}
}
val result = ArrayList(taskNames)
val result = ArrayList(taskNames.map { TaskInfo(it) })
val toProcess = ArrayList(taskNames)
val newToProcess = arrayListOf<String>()
val seen = hashSetOf<String>()
@ -161,7 +195,7 @@ class TaskManager @Inject constructor(val args: Args,
projectMap[ti.project]?.let { project ->
project.projectExtra.dependsOn.forEach { dp ->
val newTask = TaskInfo(dp.projectName, ti.taskName).id
result.add(newTask)
result.add(TaskInfo(newTask))
if (! seen.contains(newTask)) {
newToProcess.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.
*/
@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>,
reverseDependsOn: 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)) {
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
// explicitly requested by the user)
//
passedTasks.map { it.id }.let { taskNames ->
runBefore[taskName].forEach { from ->
if (taskNames.contains(from)) {
addEdge(result, from, taskName, newToProcess, "runBefore")
@ -310,6 +346,7 @@ class TaskManager @Inject constructor(val args: Args,
addEdge(result, taskName, to, newToProcess, "runAfter")
}
}
}
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,
{ it }, { t -> true })
val result = DryRunGraphExecutor(graph).run()