diff --git a/src/main/kotlin/com/beust/kobalt/api/IRunContributor.kt b/src/main/kotlin/com/beust/kobalt/api/IRunContributor.kt new file mode 100644 index 00000000..e5dcecad --- /dev/null +++ b/src/main/kotlin/com/beust/kobalt/api/IRunContributor.kt @@ -0,0 +1,25 @@ +package com.beust.kobalt.api + +import com.beust.kobalt.TaskResult + +/** + * Plugins that can implement the "task" run should implement this interface. + */ +interface IRunContributor : IContributor { + companion object { + const val DEFAULT_POSITIVE_AFFINITY = 100 + } + + /** + * @return an integer indicating your affinity for running the current project. The runner with + * the highest affinity is selected to run it. + */ + fun runAffinity(project: Project, context: KobaltContext) : Int + + /** + * Run the project. + */ + fun run(project: Project, context: KobaltContext) : TaskResult +} + + diff --git a/src/main/kotlin/com/beust/kobalt/internal/KobaltPluginXml.kt b/src/main/kotlin/com/beust/kobalt/internal/KobaltPluginXml.kt index de971a55..b1bc5f0b 100644 --- a/src/main/kotlin/com/beust/kobalt/internal/KobaltPluginXml.kt +++ b/src/main/kotlin/com/beust/kobalt/internal/KobaltPluginXml.kt @@ -61,6 +61,9 @@ class KobaltPluginXml { @XmlElement(name = "build-directory-interceptors") @JvmField var buildDirectoryInterceptors: ClassNameXml? = null + + @XmlElement(name = "run-contributors") @JvmField + var runContributors: ClassNameXml? = null } class ContributorXml { @@ -88,6 +91,7 @@ class PluginInfo(val xml: KobaltPluginXml, val classLoader: ClassLoader?) { val compilerInterceptors = arrayListOf() val sourceDirectoriesInterceptors = arrayListOf() val buildDirectoryInterceptors = arrayListOf() + val runContributors = arrayListOf() // Future contributors: // source files @@ -161,6 +165,9 @@ class PluginInfo(val xml: KobaltPluginXml, val classLoader: ClassLoader?) { xml.buildDirectoryInterceptors?.className?.forEach { buildDirectoryInterceptors.add(factory.instanceOf(forName(it)) as IBuildDirectoryIncerceptor) } + xml.runContributors?.className?.forEach { + runContributors.add(factory.instanceOf(forName(it)) as IRunContributor) + } } fun addPluginInfo(pluginInfo: PluginInfo) { diff --git a/src/main/kotlin/com/beust/kobalt/plugin/application/ApplicationPlugin.kt b/src/main/kotlin/com/beust/kobalt/plugin/application/ApplicationPlugin.kt index d8b7be66..aa4e6d4d 100644 --- a/src/main/kotlin/com/beust/kobalt/plugin/application/ApplicationPlugin.kt +++ b/src/main/kotlin/com/beust/kobalt/plugin/application/ApplicationPlugin.kt @@ -1,16 +1,14 @@ package com.beust.kobalt.plugin.application import com.beust.kobalt.* -import com.beust.kobalt.api.ConfigPlugin -import com.beust.kobalt.api.KobaltContext -import com.beust.kobalt.api.Project -import com.beust.kobalt.api.ProjectDescription +import com.beust.kobalt.api.* import com.beust.kobalt.api.annotation.Directive import com.beust.kobalt.api.annotation.Task import com.beust.kobalt.internal.JvmCompilerPlugin import com.beust.kobalt.maven.DependencyManager import com.beust.kobalt.misc.KobaltExecutors import com.beust.kobalt.misc.RunCommand +import com.beust.kobalt.misc.warn import com.beust.kobalt.plugin.packaging.PackageConfig import com.beust.kobalt.plugin.packaging.PackagingPlugin import com.google.inject.Inject @@ -35,7 +33,7 @@ fun Project.application(init: ApplicationConfig.() -> Unit) { @Singleton class ApplicationPlugin @Inject constructor(val executors: KobaltExecutors, - val dependencyManager: DependencyManager) : ConfigPlugin() { + val dependencyManager: DependencyManager) : ConfigPlugin(), IRunContributor { companion object { const val NAME = "application" @@ -50,40 +48,13 @@ class ApplicationPlugin @Inject constructor(val executors: KobaltExecutors, @Task(name = "run", description = "Run the main class", runAfter = arrayOf("assemble")) fun taskRun(project: Project): TaskResult { - var result = TaskResult() - configurationFor(project)?.let { config -> - val java = JavaInfo.create(File(SystemProperties.javaBase)).javaExecutable!! - if (config.mainClass != null) { - val jarName = project.projectProperties.get(PackagingPlugin.JAR_NAME) as String - val packages = project.projectProperties.get(PackagingPlugin.PACKAGES) as List - val allDeps = arrayListOf(jarName) - if (! isFatJar(packages, jarName)) { - val projDeps = project.projectProperties.get(JvmCompilerPlugin.DEPENDENT_PROJECTS) - as List - // If the jar file is not fat, we need to add the transitive closure of all dependencies - // on the classpath - val allTheDependencies = - dependencyManager.calculateDependencies(project, context, projDeps, - allDependencies = project.compileDependencies).map { it.jarFile.get().path } - allDeps.addAll(allTheDependencies) - } - val allDepsJoined = allDeps.joinToString(File.pathSeparator) - val args = listOf("-classpath", allDepsJoined) + config.jvmArgs + config.mainClass!! - val exitCode = RunCommand(java.absolutePath).run(args, - successCallback = { output: List -> - println(output.joinToString("\n")) - }, - errorCallback = { output: List -> - println("ERROR") - println(output.joinToString("\n")) - } - ) - result = TaskResult(exitCode == 0) - } else { - throw KobaltException("No \"mainClass\" specified in the application{} part of project ${project.name}") - } + val runContributor = context.pluginInfo.runContributors.maxBy { it.runAffinity(project, context)} + if (runContributor != null && runContributor.runAffinity(project, context) > 0) { + return runContributor.run(project, context) + } else { + warn("Couldn't find a runner for project ${project.name}") + return TaskResult() } - return result } private fun isFatJar(packages: List, jarName: String): Boolean { @@ -96,5 +67,53 @@ class ApplicationPlugin @Inject constructor(val executors: KobaltExecutors, } return false } + + // IRunContributor + + override fun runAffinity(project: Project, context: KobaltContext): Int { + return if (configurationFor(project) != null) IRunContributor.DEFAULT_POSITIVE_AFFINITY else 0 + } + + override fun run(project: Project, context: KobaltContext): TaskResult { + var result = TaskResult() + configurationFor(project)?.let { config -> + if (config.mainClass != null) { + result = runJarFile(project, config) + } else { + throw KobaltException("No \"mainClass\" specified in the application{} part of project ${project.name}") + } + } + return result + } + + private fun runJarFile(project: Project, config: ApplicationConfig) : TaskResult { + val jarName = project.projectProperties.get(PackagingPlugin.JAR_NAME) as String + val packages = project.projectProperties.get(PackagingPlugin.PACKAGES) as List + val allDeps = arrayListOf(jarName) + val java = JavaInfo.create(File(SystemProperties.javaBase)).javaExecutable!! + if (! isFatJar(packages, jarName)) { + val projDeps = project.projectProperties.get(JvmCompilerPlugin.DEPENDENT_PROJECTS) + as List + // If the jar file is not fat, we need to add the transitive closure of all dependencies + // on the classpath + val allTheDependencies = + dependencyManager.calculateDependencies(project, context, projDeps, + allDependencies = project.compileDependencies).map { it.jarFile.get().path } + allDeps.addAll(allTheDependencies) + } + val allDepsJoined = allDeps.joinToString(File.pathSeparator) + val args = listOf("-classpath", allDepsJoined) + config.jvmArgs + config.mainClass!! + val exitCode = RunCommand(java.absolutePath).run(args, + successCallback = { output: List -> + println(output.joinToString("\n")) + }, + errorCallback = { output: List -> + println("ERROR") + println(output.joinToString("\n")) + } + ) + return TaskResult(exitCode == 0) + } + } diff --git a/src/main/resources/META-INF/kobalt-plugin.xml b/src/main/resources/META-INF/kobalt-plugin.xml index cb3e9fe1..be248baa 100644 --- a/src/main/resources/META-INF/kobalt-plugin.xml +++ b/src/main/resources/META-INF/kobalt-plugin.xml @@ -37,4 +37,7 @@ com.beust.kobalt.plugin.android.AndroidPlugin + + com.beust.kobalt.plugin.application.ApplicationPlugin + \ No newline at end of file