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:
parent
5577c47185
commit
c0b685a044
8 changed files with 232 additions and 35 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
||||
}
|
|
@ -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")
|
||||
|
|
|
@ -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>()
|
||||
|
|
|
@ -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 :-(
|
||||
|
|
|
@ -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"))
|
||||
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue