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

If a dependent project fails, fail the ones that follow.

This commit is contained in:
Cedric Beust 2016-01-15 01:51:59 +04:00
parent 23032f52d7
commit 60036dcb43
13 changed files with 144 additions and 119 deletions

View file

@ -12,14 +12,6 @@ abstract public class BasePlugin : IPlugin {
this.context = context
}
/**
* The list of projects depended upon (e.g. val p = javaProject(dependentProject)).
*/
protected val projects = arrayListOf<ProjectDescription>()
fun addProject(project: Project, dependsOn: Array<out Project>) =
projects.add(ProjectDescription(project, dependsOn.toList()))
override lateinit var taskManager: TaskManager
lateinit var plugins: Plugins
}

View file

@ -75,8 +75,7 @@ abstract class JvmCompilerPlugin @Inject constructor(
val runContributor = ActorUtils.selectAffinityActor(project, context,
context.pluginInfo.testRunnerContributors)
if (runContributor != null && runContributor.affinity(project, context) > 0) {
return runContributor.run(project, context, dependencyManager.testDependencies(project, context,
projects()))
return runContributor.run(project, context, dependencyManager.testDependencies(project, context))
} else {
log(1, "Couldn't find a test runner for project ${project.name}, not running any tests")
return TaskResult()
@ -198,7 +197,18 @@ abstract class JvmCompilerPlugin @Inject constructor(
}
}
override fun projects() = projects
val allProjects = arrayListOf<ProjectDescription>()
fun addDependentProjects(project: Project, dependents: List<Project>) {
project.projectInfo.dependsOn.addAll(dependents)
with(ProjectDescription(project, dependents)) {
allProjects.add(this)
}
}
override fun projects() : List<ProjectDescription> {
return allProjects
}
@Task(name = "doc", description = "Generate the documentation for the project")
fun taskJavadoc(project: Project): TaskResult {
@ -229,9 +239,9 @@ abstract class JvmCompilerPlugin @Inject constructor(
copyResources(project, JvmCompilerPlugin.SOURCE_SET_MAIN)
val fullClasspath = if (isTest)
dependencyManager.testDependencies(project, context, projects)
dependencyManager.testDependencies(project, context)
else
dependencyManager.dependencies(project, context, projects)
dependencyManager.dependencies(project, context)
// Remove all the excluded dependencies from the classpath
val classpath = fullClasspath.filter {

View file

@ -23,10 +23,16 @@ interface IProjectInfo {
*/
fun generateBuildConfig(project: Project, context: KobaltContext, packageName: String, variant: Variant,
buildConfigs: List<BuildConfig>) : String
/**
* The list of projects that this project depends on
*/
val dependsOn: ArrayList<Project>
}
interface BaseProjectInfo : IProjectInfo {
fun generate(field: BuildConfigField) : String
abstract class BaseProjectInfo : IProjectInfo {
abstract fun generate(field: BuildConfigField) : String
fun generate(type: String, name: String, value: Any) = generate(BuildConfigField(type, name, value))
fun generateFieldsFromContributors(project: Project, context: KobaltContext)
@ -35,4 +41,6 @@ interface BaseProjectInfo : IProjectInfo {
}.map {
generate(it)
}
override val dependsOn = arrayListOf<Project>()
}

View file

@ -8,6 +8,7 @@ import com.beust.kobalt.api.Project
import com.beust.kobalt.api.annotation.IncrementalTask
import com.beust.kobalt.api.annotation.Task
import com.beust.kobalt.misc.benchmarkMillis
import com.beust.kobalt.misc.kobaltError
import com.beust.kobalt.misc.log
import com.google.common.collect.ArrayListMultimap
import com.google.common.collect.Multimap
@ -51,116 +52,129 @@ public class TaskManager @Inject constructor(val args: Args, val incrementalMana
public fun runTargets(taskNames: List<String>, projects: List<Project>) : RunTargetResult {
var result = 0
val failedProjects = hashSetOf<String>()
val messages = Collections.synchronizedList(arrayListOf<String>())
projects.forEach { project ->
val projectName = project.name
// There can be multiple tasks by the same name (e.g. PackagingPlugin and AndroidPlugin both
// define "install"), so use a multimap
val tasksByNames = ArrayListMultimap.create<String, PluginTask>()
annotationTasks.filter {
it.project.name == project.name
}.forEach {
tasksByNames.put(it.name, it)
}
AsciiArt.logBox("Building ${project.name}")
log(3, "Tasks:")
tasksByNames.keys().forEach {
log(3, " $it: " + tasksByNames.get(it))
// Does the current project depend on any failed projects?
val fp = project.projectInfo.dependsOn.filter {
failedProjects.contains(it.name)
}
val graph = DynamicGraph<PluginTask>()
taskNames.forEach { taskName ->
val ti = TaskInfo(taskName)
if (! tasksByNames.keys().contains(ti.taskName)) {
throw KobaltException("Unknown task: $taskName")
if (fp.size > 0) {
failedProjects.add(project.name)
kobaltError("Not building project ${project.name} since it depends on failed projects "
+ fp.joinToString(","))
} else {
val projectName = project.name
// There can be multiple tasks by the same name (e.g. PackagingPlugin and AndroidPlugin both
// define "install"), so use a multimap
val tasksByNames = ArrayListMultimap.create<String, PluginTask>()
annotationTasks.filter {
it.project.name == project.name
}.forEach {
tasksByNames.put(it.name, it)
}
if (ti.matches(projectName)) {
tasksByNames[ti.taskName].forEach { task ->
if (task != null && task.plugin.accept(project)) {
val reverseAfter = hashMapOf<String, String>()
alwaysRunAfter.keys().forEach { from ->
val tasks = alwaysRunAfter.get(from)
tasks.forEach {
reverseAfter.put(it, from)
}
}
log(3, "Tasks:")
tasksByNames.keys().forEach {
log(3, " $it: " + tasksByNames.get(it))
}
val graph = DynamicGraph<PluginTask>()
taskNames.forEach { taskName ->
val ti = TaskInfo(taskName)
if (!tasksByNames.keys().contains(ti.taskName)) {
throw KobaltException("Unknown task: $taskName")
}
//
// If the current target is free, add it as a single node to the graph
//
val allFreeTasks = calculateFreeTasks(tasksByNames, reverseAfter)
val currentFreeTask = allFreeTasks.filter {
TaskInfo(projectName, it.name).taskName == task.name
}
if (currentFreeTask.size == 1) {
currentFreeTask[0].let {
graph.addNode(it)
}
}
//
// Add the transitive closure of the current task as edges to the graph
//
val transitiveClosure = calculateTransitiveClosure(project, tasksByNames, ti)
transitiveClosure.forEach { pluginTask ->
val rb = runBefore.get(pluginTask.name)
rb.forEach {
val tos = tasksByNames[it]
if (tos != null && tos.size > 0) {
tos.forEach { to ->
graph.addEdge(pluginTask, to)
}
} else {
log(1, "Couldn't find node $it: not applicable to project ${project.name}")
if (ti.matches(projectName)) {
tasksByNames[ti.taskName].forEach { task ->
if (task != null && task.plugin.accept(project)) {
val reverseAfter = hashMapOf<String, String>()
alwaysRunAfter.keys().forEach { from ->
val tasks = alwaysRunAfter.get(from)
tasks.forEach {
reverseAfter.put(it, from)
}
}
}
//
// If any of the nodes in the graph has an "alwaysRunAfter", add that edge too
//
val allNodes = arrayListOf<PluginTask>()
allNodes.addAll(graph.nodes)
allNodes.forEach { node ->
val other = alwaysRunAfter.get(node.name)
other?.forEach { o ->
tasksByNames[o]?.forEach {
graph.addEdge(it, node)
//
// If the current target is free, add it as a single node to the graph
//
val allFreeTasks = calculateFreeTasks(tasksByNames, reverseAfter)
val currentFreeTask = allFreeTasks.filter {
TaskInfo(projectName, it.name).taskName == task.name
}
if (currentFreeTask.size == 1) {
currentFreeTask[0].let {
graph.addNode(it)
}
}
//
// Add the transitive closure of the current task as edges to the graph
//
val transitiveClosure = calculateTransitiveClosure(project, tasksByNames, ti)
transitiveClosure.forEach { pluginTask ->
val rb = runBefore.get(pluginTask.name)
rb.forEach {
val tos = tasksByNames[it]
if (tos != null && tos.size > 0) {
tos.forEach { to ->
graph.addEdge(pluginTask, to)
}
} else {
log(1, "Couldn't find node $it: not applicable to project ${project.name}")
}
}
}
//
// If any of the nodes in the graph has an "alwaysRunAfter", add that edge too
//
val allNodes = arrayListOf<PluginTask>()
allNodes.addAll(graph.nodes)
allNodes.forEach { node ->
val other = alwaysRunAfter.get(node.name)
other?.forEach { o ->
tasksByNames[o]?.forEach {
graph.addEdge(it, node)
}
}
}
}
}
}
}
}
//
// Now that we have a full graph, run it
//
log(3, "About to run graph:\n ${graph.dump()} ")
//
// Now that we have a full graph, run it
//
log(3, "About to run graph:\n ${graph.dump()} ")
val factory = object : IThreadWorkerFactory<PluginTask> {
override public fun createWorkers(nodes: List<PluginTask>): List<IWorker<PluginTask>> {
val factory = object : IThreadWorkerFactory<PluginTask> {
override public fun createWorkers(nodes: List<PluginTask>): List<IWorker<PluginTask>> {
// val tr = nodes.reduce { workers: List<TaskWorker>, node: PluginTask ->
// val result: List<TaskWorker> = workers + TaskWorker(listOf(node), args.dryRun, messages)
// result
// }
val thisResult = arrayListOf<IWorker<PluginTask>>()
nodes.forEach {
thisResult.add(TaskWorker(listOf(it), args.dryRun, messages))
val thisResult = arrayListOf<IWorker<PluginTask>>()
nodes.forEach {
thisResult.add(TaskWorker(listOf(it), args.dryRun, messages))
}
return thisResult
}
return thisResult
}
val executor = DynamicGraphExecutor(graph, factory)
val thisResult = executor.run()
if (result == 0) {
result = thisResult
}
if (result != 0) {
failedProjects.add(project.name)
}
}
val executor = DynamicGraphExecutor(graph, factory)
val thisResult = executor.run()
if (result == 0) {
result = thisResult
}
}
return RunTargetResult(result, messages)
}

View file

@ -114,9 +114,9 @@ public class DependencyManager @Inject constructor(val executors: KobaltExecutor
/**
* @return the compile dependencies for this project, including the contributors.
*/
fun dependencies(project: Project, context: KobaltContext,
projects: List<ProjectDescription>) : List<IClasspathDependency> {
fun dependencies(project: Project, context: KobaltContext) : List<IClasspathDependency> {
val result = arrayListOf<IClasspathDependency>()
val projects = listOf(ProjectDescription(project, project.projectInfo.dependsOn))
result.add(FileDependency(KFiles.makeOutputDir(project).absolutePath))
result.add(FileDependency(KFiles.makeOutputTestDir(project).absolutePath))
with(project) {
@ -131,9 +131,9 @@ public class DependencyManager @Inject constructor(val executors: KobaltExecutor
/**
* @return the test dependencies for this project, including the contributors.
*/
fun testDependencies(project: Project, context: KobaltContext,
projects: List<ProjectDescription>) : List<IClasspathDependency> {
fun testDependencies(project: Project, context: KobaltContext) : List<IClasspathDependency> {
val result = arrayListOf<IClasspathDependency>()
val projects = listOf(ProjectDescription(project, project.projectInfo.dependsOn))
result.add(FileDependency(KFiles.makeOutputDir(project).absolutePath))
result.add(FileDependency(KFiles.makeOutputTestDir(project).absolutePath))
with(project) {

View file

@ -30,6 +30,8 @@ fun Any.warn(text: String) {
KobaltLogger.logger.warn(javaClass.simpleName, text)
}
fun Any.kobaltError(text: String, e: Throwable? = null) = error(text, e)
fun Any.error(text: String, e: Throwable? = null) {
KobaltLogger.logger.error(javaClass.simpleName, text, e)
}