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

Added command "/v1/getDependencyGraph" for the IDEA plug-in.

This commit is contained in:
Cedric Beust 2016-07-31 23:11:26 -08:00
parent 5577c47185
commit c0b685a044
8 changed files with 232 additions and 35 deletions

View file

@ -2,7 +2,9 @@ package com.beust.kobalt.app.remote
import com.beust.kobalt.Args
import com.beust.kobalt.api.IClasspathDependency
import com.beust.kobalt.api.Project
import com.beust.kobalt.app.BuildFileCompiler
import com.beust.kobalt.internal.DynamicGraph
import com.beust.kobalt.internal.PluginInfo
import com.beust.kobalt.internal.TaskManager
import com.beust.kobalt.internal.build.BuildFile
@ -24,8 +26,9 @@ interface IProgressListener {
class DependencyData @Inject constructor(val executors: KobaltExecutors, val dependencyManager: DependencyManager,
val buildFileCompilerFactory: BuildFileCompiler.IFactory, val pluginInfo: PluginInfo,
val taskManager: TaskManager) {
fun dependenciesDataFor(buildFilePath: String, args: Args, progressListener: IProgressListener? = null)
: GetDependenciesData {
fun dependenciesDataFor(buildFilePath: String, args: Args, progressListener: IProgressListener? = null,
useGraph : Boolean = false): GetDependenciesData {
val projectDatas = arrayListOf<ProjectData>()
fun toDependencyData(d: IClasspathDependency, scope: String): DependencyData {
@ -44,16 +47,48 @@ class DependencyData @Inject constructor(val executors: KobaltExecutors, val dep
FileDependency(it.absolutePath)
}
fun compileDependencies(project: Project, name: String): List<DependencyData> {
val result =
(pluginDependencies +
allDeps(project.compileDependencies, name) +
allDeps(project.compileProvidedDependencies, name))
.map { toDependencyData(it, "compile") }
return result
}
fun toDependencyData2(scope: String, node: DynamicGraph.Companion.Node<IClasspathDependency>): DependencyData {
val d = node.value
val dep = dependencyManager.create(d.id)
return DependencyData(d.id, scope, dep.jarFile.get().absolutePath,
node.children.map { toDependencyData2(scope, it) })
}
fun compileDependenciesGraph(project: Project, name: String): List<DependencyData> {
val depLambda = { dep : IClasspathDependency -> dep.directDependencies() }
val result =
(DynamicGraph.Companion.transitiveClosureGraph(pluginDependencies, depLambda) +
DynamicGraph.Companion.transitiveClosureGraph(project.compileDependencies, depLambda) +
DynamicGraph.Companion.transitiveClosureGraph(project.compileProvidedDependencies, depLambda))
.map { toDependencyData2("compile", it)}
return result
}
fun testDependencies(project: Project, name: String): List<DependencyData> {
return allDeps(project.testDependencies, name).map { toDependencyData(it, "testCompile") }
}
fun testDependenciesGraph(project: Project, name: String): List<DependencyData> {
val depLambda = { dep : IClasspathDependency -> dep.directDependencies() }
return DynamicGraph.Companion.transitiveClosureGraph(project.testDependencies, depLambda)
.map { toDependencyData2("compile", it)}
}
val allTasks = hashSetOf<TaskData>()
projectResult.projects.withIndex().forEach { wi ->
val project = wi.value
val name = project.name
progressListener?.onProgress(message = "Synchronizing project ${project.name} "
+ (wi.index + 1) + "/" + projectResult.projects.size)
val compileDependencies = pluginDependencies.map { toDependencyData(it, "compile") } +
allDeps(project.compileDependencies, name).map { toDependencyData(it, "compile") } +
allDeps(project.compileProvidedDependencies, name).map { toDependencyData(it, "compile") }
val testDependencies = allDeps(project.testDependencies, name).map { toDependencyData(it, "testCompile") }
val dependentProjects = project.dependsOn.map { it.name }
@ -64,6 +99,13 @@ class DependencyData @Inject constructor(val executors: KobaltExecutors, val dep
TaskData(it.name, it.doc, it.group)
}
allTasks.addAll(projectTasks)
val compileDependencies =
if (useGraph) compileDependenciesGraph(project, project.name)
else compileDependencies(project, project.name)
val testDependencies =
if (useGraph) testDependenciesGraph(project, project.name)
else testDependencies(project, project.name)
projectDatas.add(ProjectData(project.name, project.directory, dependentProjects,
compileDependencies, testDependencies,
sources.second.toSet(), tests.second.toSet(), sources.first.toSet(), tests.first.toSet(),
@ -77,7 +119,8 @@ class DependencyData @Inject constructor(val executors: KobaltExecutors, val dep
// use these same classes.
//
class DependencyData(val id: String, val scope: String, val path: String)
class DependencyData(val id: String, val scope: String, val path: String,
val children: List<DependencyData> = emptyList())
data class TaskData(val name: String, val description: String, val group: String) {
override fun toString() = name
}

View file

@ -14,6 +14,7 @@ import javax.inject.Inject
* { "name" : "getDependencies", "buildFile": "/Users/beust/kotlin/kobalt/kobalt/src/Build.kt" }
* The response is a GetDependenciesData.
*/
@Deprecated(message = "Only used by old server, to be deleted")
class GetDependenciesCommand @Inject constructor(val args: Args, val dependencyData: DependencyData) : ICommand {
override val name = "getDependencies"

View file

@ -0,0 +1,100 @@
package com.beust.kobalt.app.remote
import com.beust.kobalt.Args
import com.beust.kobalt.api.Kobalt
import com.beust.kobalt.app.ProjectFinder
import com.beust.kobalt.internal.build.BuildFile
import com.beust.kobalt.internal.eventbus.ArtifactDownloadedEvent
import com.google.common.eventbus.EventBus
import com.google.common.eventbus.Subscribe
import com.google.gson.Gson
import org.eclipse.jetty.websocket.api.RemoteEndpoint
import org.eclipse.jetty.websocket.api.Session
import org.eclipse.jetty.websocket.api.WebSocketListener
import java.nio.file.Paths
/**
* Manage the websocket endpoint "/v1/getDependencyGraph".
*/
class GetDependencyGraphHandler : WebSocketListener {
// The SparkJava project refused to merge https://github.com/perwendel/spark/pull/383
// so I have to do dependency injections manually :-(
val projectFinder = Kobalt.INJECTOR.getInstance(ProjectFinder::class.java)
var session: Session? = null
override fun onWebSocketClose(code: Int, reason: String?) {
println("ON CLOSE $code reason: $reason")
}
override fun onWebSocketError(cause: Throwable?) {
cause?.printStackTrace()
throw UnsupportedOperationException()
}
fun <T> sendWebsocketCommand(endpoint: RemoteEndpoint, commandName: String, payload: T) {
endpoint.sendString(Gson().toJson(WebSocketCommand(commandName, payload = Gson().toJson(payload))))
}
override fun onWebSocketConnect(s: Session) {
session = s
val buildFileParams = s.upgradeRequest.parameterMap["buildFile"]
if (buildFileParams != null) {
val buildFile = buildFileParams[0]
fun <T> getInstance(cls: Class<T>) : T = Kobalt.INJECTOR.getInstance(cls)
val result = if (buildFile != null) {
// Track all the downloads that this dependency call might trigger and
// send them as a progress message to the web socket
val eventBus = getInstance(EventBus::class.java)
val busListener = object {
@Subscribe
fun onArtifactDownloaded(event: ArtifactDownloadedEvent) {
sendWebsocketCommand(s.remote, ProgressCommand.NAME,
ProgressCommand(null, "Downloaded " + event.artifactId))
}
}
eventBus.register(busListener)
// Get the dependencies for the requested build file and send progress to the web
// socket for each project
try {
val dependencyData = getInstance(DependencyData::class.java)
val args = getInstance(Args::class.java)
val allProjects = projectFinder.initForBuildFile(BuildFile(Paths.get(buildFile), buildFile),
args)
dependencyData.dependenciesDataFor(buildFile, args, object : IProgressListener {
override fun onProgress(progress: Int?, message: String?) {
sendWebsocketCommand(s.remote, ProgressCommand.NAME, ProgressCommand(progress, message))
}
}, useGraph = true)
} catch(ex: Throwable) {
ex.printStackTrace()
val errorMessage = ex.stackTrace.map { it.toString() }.joinToString("\n<p>")
DependencyData.GetDependenciesData(errorMessage = errorMessage)
} finally {
SparkServer.cleanUpCallback()
eventBus.unregister(busListener)
}
} else {
DependencyData.GetDependenciesData(
errorMessage = "buildFile wasn't passed in the query parameter")
}
sendWebsocketCommand(s.remote, DependencyData.GetDependenciesData.NAME, result)
s.close()
}
}
override fun onWebSocketText(message: String?) {
println("RECEIVED TEXT: $message")
session?.remote?.sendString("Response: $message")
}
override fun onWebSocketBinary(payload: ByteArray?, offset: Int, len: Int) {
println("RECEIVED BINARY: $payload")
}
}

View file

@ -4,16 +4,16 @@ import com.beust.kobalt.Args
import com.beust.kobalt.api.Kobalt
import com.beust.kobalt.app.MainModule
import com.beust.kobalt.internal.KobaltSettings
import com.beust.kobalt.internal.remote.ICommand
import com.google.gson.Gson
enum class Command(val n: Int, val command: ICommand) {
GET_DEPENDENCIES(1, Kobalt.INJECTOR.getInstance(GetDependenciesCommand::class.java));
companion object {
val commandMap = hashMapOf<Int, ICommand>()
fun commandFor(n: Int) = values().filter { it.n == n }[0].command
}
}
//enum class Command(val n: Int, val command: ICommand) {
// GET_DEPENDENCIES(1, Kobalt.INJECTOR.getInstance(GetDependenciesCommand::class.java)),
// GET_DEPENDENCIES_GRAPH(2, Kobalt.INJECTOR.getInstance(GetDependenciesGraphCommand::class.java));
// companion object {
// val commandMap = hashMapOf<Int, ICommand>()
// fun commandFor(n: Int) = values().filter { it.n == n }[0].command
// }
//}
class KobaltHub(val dependencyData: DependencyData) {
val args = Args()
@ -23,6 +23,9 @@ class KobaltHub(val dependencyData: DependencyData) {
when(n) {
1 -> Gson().toJson(
dependencyData.dependenciesDataFor("/Users/beust/kotlin/klaxon/kobalt/src/Build.kt", args))
2 -> Gson().toJson(
dependencyData.dependenciesDataFor("/Users/beust/kotlin/klaxon/kobalt/src/Build.kt", args,
useGraph = true))
else -> throw RuntimeException("Unknown command")
}
println("Data: $data")

View file

@ -15,6 +15,7 @@ import java.io.PrintWriter
import java.net.ServerSocket
import java.net.SocketException
@Deprecated(message = "Replaced by Websocket server, to be deleted")
class OldServer(val initCallback: (String) -> List<Project>, val cleanUpCallback: () -> Unit)
: KobaltServer.IServer, ICommandSender {
val pending = arrayListOf<CommandData>()

View file

@ -49,6 +49,7 @@ class SparkServer(val initCallback: (String) -> List<Project>, val cleanUpCallba
log.debug("RUNNING")
Spark.port(port)
Spark.webSocket("/v1/getDependencies", GetDependenciesHandler::class.java)
Spark.webSocket("/v1/getDependencyGraph", GetDependencyGraphHandler::class.java)
Spark.get("/ping", { req, res -> """ { "result" : "ok" } """ })
Spark.get("/quit", { req, res ->
Executors.newFixedThreadPool(1).let { executor ->
@ -96,6 +97,7 @@ class SparkServer(val initCallback: (String) -> List<Project>, val cleanUpCallba
/**
* Manage the websocket endpoint "/v1/getDependencies".
*/
@Deprecated(message = "Replaced with GetDependencyGraphHandler")
class GetDependenciesHandler : WebSocketListener {
// The SparkJava project refused to merge https://github.com/perwendel/spark/pull/383
// so I have to do dependency injections manually :-(

View file

@ -185,4 +185,23 @@ class DynamicGraphTest {
}
}
@Test
fun transitiveClosureGraphTest() {
val graph = DynamicGraph<String>().apply {
// a -> b
// b -> c, d
// e
addEdge("a", "b")
addEdge("b", "c")
addEdge("b", "d")
addNode("e")
}
val closure = DynamicGraph.transitiveClosureGraph("a", { s -> graph.childrenOf(s).toList() } )
assertThat(closure.value).isEqualTo("a")
val ca = closure.children
assertThat(ca.map { it.value }).isEqualTo(listOf("b"))
val cb = ca[0].children
assertThat(cb.map { it.value }).isEqualTo(listOf("d", "c"))
}
}