diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/CollectionUtils.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/CollectionUtils.kt new file mode 100644 index 00000000..62258d62 --- /dev/null +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/CollectionUtils.kt @@ -0,0 +1,14 @@ +package com.beust.kobalt.internal + +fun Collection.doWhile(condition: (T) -> Boolean, action: (T) -> Unit) { + var i = 0 + var done = false + while (i < size && ! done) { + elementAt(i).let { element -> + if (! condition(element)) done = true + else action(element) + } + i++ + } +} + diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/DynamicGraph.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/DynamicGraph.kt index b02b8b22..92004419 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/DynamicGraph.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/DynamicGraph.kt @@ -129,7 +129,7 @@ class DynamicGraphExecutor(val graph : DynamicGraph, val factory: IThreadW val executor = Executors.newFixedThreadPool(5, NamedThreadFactory("DynamicGraphExecutor")) val completion = ExecutorCompletionService>(executor) - fun run() : Int { + fun run() : TaskResult { try { return run2() } finally { @@ -137,12 +137,12 @@ class DynamicGraphExecutor(val graph : DynamicGraph, val factory: IThreadW } } - private fun run2() : Int { + private fun run2() : TaskResult { var running = 0 - var gotError = false val nodesRun = hashSetOf() - var newFreeNodes = HashSet(graph.freeNodes) - while (! gotError && (running > 0 || newFreeNodes.size > 0)) { + var failedResult: TaskResult? = null + val newFreeNodes = HashSet(graph.freeNodes) + while (failedResult == null && (running > 0 || newFreeNodes.size > 0)) { nodesRun.addAll(newFreeNodes) val callables : List> = factory.createWorkers(newFreeNodes) callables.forEach { completion.submit(it) } @@ -161,7 +161,9 @@ class DynamicGraphExecutor(val graph : DynamicGraph, val factory: IThreadW } else { log(2, "Task failed: $taskResult") newFreeNodes.clear() - gotError = true + if (failedResult == null) { + failedResult = taskResult + } } } catch(ex: TimeoutException) { log(2, "Time out") @@ -172,15 +174,15 @@ class DynamicGraphExecutor(val graph : DynamicGraph, val factory: IThreadW throw (ex.cause as InvocationTargetException).targetException } else { error("Error: ${ite.cause?.message}", ite.cause) - gotError = true + failedResult = TaskResult(success = false, errorMessage = ite.cause?.message) } } else { error("Error: ${ex.message}", ex) - gotError = true + failedResult = TaskResult(success = false, errorMessage = ex.message) } } } - return if (gotError) 1 else 0 + return if (failedResult != null) failedResult else TaskResult() } } diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JvmCompilerPlugin.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JvmCompilerPlugin.kt index 7dbde4c4..a7dab585 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JvmCompilerPlugin.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JvmCompilerPlugin.kt @@ -11,10 +11,7 @@ import com.beust.kobalt.api.annotation.Task import com.beust.kobalt.maven.DependencyManager import com.beust.kobalt.maven.LocalRepo import com.beust.kobalt.maven.Md5 -import com.beust.kobalt.misc.KFiles -import com.beust.kobalt.misc.KobaltExecutors -import com.beust.kobalt.misc.log -import com.beust.kobalt.misc.warn +import com.beust.kobalt.misc.* import java.io.File import java.util.* import javax.inject.Inject @@ -180,12 +177,19 @@ open class JvmCompilerPlugin @Inject constructor( // If this project has a kapt{} directive, we want to run the Java compiler first val hasKapt = project.projectProperties.get("kaptConfig") != null - val finalAllCompilers = if (hasKapt) swapJavaAndKotlin(allCompilers) else allCompilers - finalAllCompilers.forEach { compiler -> + val allCompilersSorted = if (hasKapt) swapJavaAndKotlin(allCompilers) else allCompilers + var done = false + allCompilersSorted.doWhile({ ! done }) { compiler -> val compilerResults = compilerUtils.invokeCompiler(project, context, compiler, sourceDirectories(project, context), isTest) results.addAll(compilerResults.successResults) if (failedResult == null) failedResult = compilerResults.failedResult + compilerResults.failedResult?.let { failedResult -> + done = true + failedResult.errorMessage?.let { errorMessage -> + error(text = errorMessage) + } + } } return if (failedResult != null) failedResult!! diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/TaskManager.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/TaskManager.kt index 676e826c..2a534d71 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/TaskManager.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/TaskManager.kt @@ -60,7 +60,7 @@ class TaskManager @Inject constructor(val args: Args, fun matches(projectName: String) = project == null || project == projectName } - class RunTargetResult(val exitCode: Int, val messages: List) + class RunTargetResult(val taskResult: TaskResult, val messages: List) /** * @return the list of tasks available for the given project. @@ -113,7 +113,7 @@ class TaskManager @Inject constructor(val args: Args, } private fun runProjects(taskInfos: List, projects: List) : RunTargetResult { - var result = 0 + var result = TaskResult() val failedProjects = hashSetOf() val messages = Collections.synchronizedList(arrayListOf()) projects.forEach { project -> @@ -159,11 +159,11 @@ class TaskManager @Inject constructor(val args: Args, val executor = DynamicGraphExecutor(graph, factory) val thisResult = executor.run() - if (thisResult != 0) { + if (! thisResult.success) { log(2, "Marking project ${project.name} as failed") failedProjects.add(project.name) } - if (result == 0) { + if (result.success) { result = thisResult } } diff --git a/src/main/kotlin/com/beust/kobalt/plugin/groovy/GroovyPlugin.kt b/src/main/kotlin/com/beust/kobalt/plugin/groovy/GroovyPlugin.kt new file mode 100644 index 00000000..0e35d50d --- /dev/null +++ b/src/main/kotlin/com/beust/kobalt/plugin/groovy/GroovyPlugin.kt @@ -0,0 +1,79 @@ +package com.beust.kobalt.plugin.groovy + +import com.beust.kobalt.TaskResult +import com.beust.kobalt.api.* +import com.beust.kobalt.homeDir +import com.beust.kobalt.maven.DependencyManager +import com.beust.kobalt.misc.KFiles +import com.beust.kobalt.misc.Strings +import com.beust.kobalt.misc.log +import com.beust.kobalt.misc.warn +import com.google.inject.Inject +import com.google.inject.Singleton +import java.io.File +import java.net.URLClassLoader + +@Singleton +class GroovyPlugin @Inject constructor(val groovyCompiler: GroovyCompiler) : ICompilerContributor { + override fun affinity(project: Project, context: KobaltContext) = + if (hasSourceFiles(project)) 1 else 0 + + // ICompilerContributor + val compiler = object: ICompiler { + override val sourceSuffixes = GroovyCompiler.SUFFIXES + + override val sourceDirectory = "groovy" + + override val priority = 1 + + override fun compile(project: Project, context: KobaltContext, info: CompilerActionInfo): TaskResult { + val result = + if (info.sourceFiles.size > 0) { + groovyCompiler.compile(project, context, info) + } else { + warn("Couldn't find any source files to compile") + TaskResult() + } + return result + } + } + + override fun compilersFor(project: Project, context: KobaltContext) = listOf(compiler) + + private fun hasSourceFiles(project: Project) + = KFiles.findSourceFiles(project.directory, project.sourceDirectories, GroovyCompiler.SUFFIXES).size > 0 +} + +class GroovyCompiler @Inject constructor(dependencyManager: DependencyManager){ + companion object { + val SUFFIXES = listOf("groovy") + val GROOVY_HOME = homeDir("java/groovy-2.4.7") + val GROOVYC = KFiles.joinDir(GROOVY_HOME, "bin/groovyc") + } + + private val groovyCompilerClass: Class<*> by lazy { + val jarFile = dependencyManager.create("org.codehaus.groovy:groovy:2.4.7").jarFile.get() + val classLoader = URLClassLoader(arrayOf(jarFile.toURI().toURL())) + classLoader.loadClass("org.codehaus.groovy.tools.FileSystemCompiler") + } + + private fun invokeGroovyCompiler(info: CompilerActionInfo) : TaskResult { + val cls = groovyCompilerClass + val main = cls.getMethod("commandLineCompile", Array::class.java) + val classpath = info.dependencies.map { it.jarFile.get() }.joinToString(File.pathSeparator) + try { + main.invoke(null, arrayOf("-classpath", classpath, "-d", info.outputDir.path, + *info.sourceFiles.toTypedArray())) + return TaskResult() + } catch(ex: Exception) { + return TaskResult(success = false, errorMessage = ex.cause.toString()) + } + } + + fun compile(project: Project, context: KobaltContext, info: CompilerActionInfo): TaskResult { + val size = info.sourceFiles.size + log(1, "Groovy compiling " + size + " " + Strings.pluralize(size, "file")) + val result = invokeGroovyCompiler(info) + return result + } +} diff --git a/src/main/resources/META-INF/kobalt-core-plugin.xml b/src/main/resources/META-INF/kobalt-core-plugin.xml index 3186b3f5..4824d5cf 100644 --- a/src/main/resources/META-INF/kobalt-core-plugin.xml +++ b/src/main/resources/META-INF/kobalt-core-plugin.xml @@ -13,6 +13,7 @@ com.beust.kobalt.plugin.publish.PublishPlugin com.beust.kobalt.plugin.apt.AptPlugin com.beust.kobalt.internal.JvmCompilerPlugin + com.beust.kobalt.plugin.groovy.GroovyPlugin com.beust.kobalt.app.Templates