diff --git a/build.gradle b/build.gradle index d2a5a212..928554da 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,8 @@ dependencies { 'com.google.inject.extensions:guice-assistedinject:4.0', 'com.google.guava:guava:18.0', 'org.apache.maven:maven-model:3.3.3', - 'com.github.spullara.mustache.java:compiler:0.8.18' + 'com.github.spullara.mustache.java:compiler:0.8.18', + "io.reactivex:rxjava:1.0.14" // compile files("/Users/beust/.kobalt/repository/com/beust/kobalt-example-plugin/build/libs/kobalt-example-plugin.jar") testCompile 'org.testng:testng:6.9.6' diff --git a/kobalt.iml b/kobalt.iml index 99f9efd0..bf0ac307 100644 --- a/kobalt.iml +++ b/kobalt.iml @@ -40,5 +40,6 @@ + \ No newline at end of file diff --git a/kobalt/src/Build.kt b/kobalt/src/Build.kt index 84bfe947..71dd7370 100644 --- a/kobalt/src/Build.kt +++ b/kobalt/src/Build.kt @@ -66,7 +66,8 @@ val kobalt = kotlinProject(wrapper) { "com.google.inject.extensions:guice-assistedinject:4.0", "com.google.guava:guava:19.0-rc2", "org.apache.maven:maven-model:3.3.3", - "com.github.spullara.mustache.java:compiler:0.9.1" + "com.github.spullara.mustache.java:compiler:0.9.1", + "io.reactivex:rxjava:1.0.14" ) } } diff --git a/src/main/kotlin/com/beust/kobalt/Args.kt b/src/main/kotlin/com/beust/kobalt/Args.kt index 697aa83a..7bb16a29 100644 --- a/src/main/kotlin/com/beust/kobalt/Args.kt +++ b/src/main/kotlin/com/beust/kobalt/Args.kt @@ -13,6 +13,9 @@ class Args { "dependencies") var checkVersions = false + @Parameter(names = arrayOf("--client")) + var client: Boolean = false + @Parameter(names = arrayOf("--dev"), description = "Turn of dev mode, resulting in a more verbose log output") var dev: Boolean = false @@ -29,6 +32,16 @@ class Args { @Parameter(names = arrayOf("--log"), description = "Define the log level (1-3)") var log: Int = 1 + companion object { + const val DEFAULT_SERVER_PORT = 3867 + } + + @Parameter(names = arrayOf("--port"), description = "Port, if --server was specified") + var port: Int = DEFAULT_SERVER_PORT + + @Parameter(names = arrayOf("--server"), description = "Run in server mode") + var serverMode: Boolean = false + @Parameter(names = arrayOf("--tasks"), description = "Display the tasks available for this build") var tasks: Boolean = false diff --git a/src/main/kotlin/com/beust/kobalt/Main.kt b/src/main/kotlin/com/beust/kobalt/Main.kt index 93dae213..8117e284 100644 --- a/src/main/kotlin/com/beust/kobalt/Main.kt +++ b/src/main/kotlin/com/beust/kobalt/Main.kt @@ -47,11 +47,19 @@ private class Main @Inject constructor( val depFactory: DepFactory, val checkVersions: CheckVersions, val github: GithubApi, - val updateKobalt: UpdateKobalt) { + val updateKobalt: UpdateKobalt, + val client: KobaltClient, + val server: KobaltServer) { data class RunInfo(val jc: JCommander, val args: Args) public fun run(jc: JCommander, args: Args) : Int { + + if (args.client) { + client.run() + return 0 + } + var result = 0 val latestVersionFuture = github.latestKobaltVersion benchmark("Build", { @@ -120,7 +128,15 @@ private class Main @Inject constructor( } else { val context = KobaltContext(args) Kobalt.context = context - val allProjects = script2.create(arrayListOf(buildFile)).findProjects() + val scriptCompiler = script2.create(arrayListOf(buildFile)) + + if (args.serverMode) { + scriptCompiler.observable.subscribe { + info -> server.sendInfo(info) + } + executors.miscExecutor.submit(server) + } + val allProjects = scriptCompiler.findProjects() // // Force each project.directory to be an absolute path, if it's not already diff --git a/src/main/kotlin/com/beust/kobalt/internal/KobaltClient.kt b/src/main/kotlin/com/beust/kobalt/internal/KobaltClient.kt new file mode 100644 index 00000000..58d9286e --- /dev/null +++ b/src/main/kotlin/com/beust/kobalt/internal/KobaltClient.kt @@ -0,0 +1,52 @@ +package com.beust.kobalt.internal + +import com.beust.kobalt.Args +import com.beust.kobalt.kotlin.ScriptCompiler2 +import com.beust.kobalt.mainNoExit +import com.beust.kobalt.misc.log +import com.google.inject.Inject +import java.io.BufferedReader +import java.io.InputStreamReader +import java.io.PrintWriter +import java.net.ConnectException +import java.net.Socket +import java.util.concurrent.Executors + +public class KobaltClient @Inject constructor() : Runnable { + var outgoing: PrintWriter? = null + + override fun run() { + val portNumber = Args.DEFAULT_SERVER_PORT + + Executors.newFixedThreadPool(1).submit { + log(1, "Lauching Kobalt main") + mainNoExit(arrayOf("--dev", "--server", "assemble")) + } + + var done = false + var attempts = 1 + while (attempts < 3 && ! done) { + try { + val socket = Socket("localhost", portNumber) + val ins = BufferedReader(InputStreamReader(socket.inputStream)) + done = true + log(1, "Launching listening server") + var fromServer = ins.readLine() + while (fromServer != null) { + log(1, "From server: " + fromServer); + if (fromServer.equals("Bye.")) + break; + fromServer = ins.readLine() + } + } catch(ex: ConnectException) { + log(1, "Server not up, sleeping a bit") + Thread.sleep(2000) + attempts++ + } + } + } + + fun sendInfo(info: ScriptCompiler2.BuildScriptInfo) { + outgoing!!.println("Sending info with project count: " + info.projects.size()) + } +} diff --git a/src/main/kotlin/com/beust/kobalt/internal/KobaltServer.kt b/src/main/kotlin/com/beust/kobalt/internal/KobaltServer.kt new file mode 100644 index 00000000..2305e7ff --- /dev/null +++ b/src/main/kotlin/com/beust/kobalt/internal/KobaltServer.kt @@ -0,0 +1,50 @@ +package com.beust.kobalt.internal + +import com.beust.kobalt.Args +import com.beust.kobalt.kotlin.ScriptCompiler2 +import com.beust.kobalt.misc.log +import com.google.inject.Inject +import java.io.BufferedReader +import java.io.InputStreamReader +import java.io.PrintWriter +import java.net.ServerSocket + +public class KobaltServer @Inject constructor() : Runnable { + var outgoing: PrintWriter? = null + val pending = arrayListOf() + + override fun run() { + val portNumber = Args.DEFAULT_SERVER_PORT + + log(1, "Starting on port $portNumber") + val serverSocket = ServerSocket(portNumber) + val clientSocket = serverSocket.accept() + outgoing = PrintWriter(clientSocket.outputStream, true) + if (pending.size() > 0) { + log(1, "Emptying the queue, size $pending.size()") + synchronized(pending) { + pending.forEach { sendInfo(it) } + pending.clear() + } + } + val ins = BufferedReader(InputStreamReader(clientSocket.inputStream)) + var inputLine = ins.readLine() + while (inputLine != null) { + log(1, "Received $inputLine") + if (inputLine.equals("Bye.")) + break; + inputLine = ins.readLine() + } + } + + fun sendInfo(info: ScriptCompiler2.BuildScriptInfo) { + if (outgoing != null) { + outgoing!!.println("Sending info with project count: " + info.projects.size()) + } else { + log(1, "Queuing $info") + synchronized(pending) { + pending.add(info) + } + } + } +} diff --git a/src/main/kotlin/com/beust/kobalt/kotlin/ScriptCompiler2.kt b/src/main/kotlin/com/beust/kobalt/kotlin/ScriptCompiler2.kt index c8cec56c..52308195 100644 --- a/src/main/kotlin/com/beust/kobalt/kotlin/ScriptCompiler2.kt +++ b/src/main/kotlin/com/beust/kobalt/kotlin/ScriptCompiler2.kt @@ -11,6 +11,7 @@ import com.beust.kobalt.misc.countChar import com.beust.kobalt.misc.log import com.beust.kobalt.plugin.kotlin.kotlinCompilePrivate import com.google.inject.assistedinject.Assisted +import rx.subjects.PublishSubject import java.io.File import java.io.FileInputStream import java.io.InputStream @@ -29,6 +30,8 @@ public class ScriptCompiler2 @Inject constructor(@Assisted("buildFiles") val bui fun create(@Assisted("buildFiles") buildFiles: List) : ScriptCompiler2 } + val observable = PublishSubject.create() + private val SCRIPT_JAR = "buildScript.jar" fun findProjects(): List { @@ -38,8 +41,8 @@ public class ScriptCompiler2 @Inject constructor(@Assisted("buildFiles") val bui val buildScriptJarFile = File(KFiles.findBuildScriptLocation(buildFile, SCRIPT_JAR)) maybeCompileBuildFile(buildFile, buildScriptJarFile, pluginUrls) - val output = parseBuildScriptJarFile(buildScriptJarFile, pluginUrls) - result.addAll(output.projects) + val buildScriptInfo = parseBuildScriptJarFile(buildScriptJarFile, pluginUrls) + result.addAll(buildScriptInfo.projects) } return result } @@ -130,7 +133,7 @@ public class ScriptCompiler2 @Inject constructor(@Assisted("buildFiles") val bui class BuildScriptInfo(val projects: List, val classLoader: ClassLoader) private fun parseBuildScriptJarFile(buildScriptJarFile: File, urls: List) : BuildScriptInfo { - val result = arrayListOf() + val projects = arrayListOf() var stream : InputStream? = null val allUrls = arrayListOf().plus(urls).plus(arrayOf( buildScriptJarFile.toURI().toURL(), @@ -172,7 +175,7 @@ public class ScriptCompiler2 @Inject constructor(@Assisted("buildFiles") val bui val r = method.invoke(null) if (r is Project) { log(2, "Found project ${r} in class ${cls}") - result.add(r) + projects.add(r) } } else { val taskAnnotation = method.getAnnotation(Task::class.java) @@ -188,6 +191,8 @@ public class ScriptCompiler2 @Inject constructor(@Assisted("buildFiles") val bui } // Now that we all the projects, sort them topologically - return BuildScriptInfo(Kobalt.sortProjects(result), classLoader) + val result = BuildScriptInfo(Kobalt.sortProjects(projects), classLoader) + observable.onNext(result) + return result } }