Some visual improvements

+ Display nodes as groupId:artifact:version
+ Provide tasks for printing ascii representations
  of the dependency graphs for the various
  configurations:
  compile, test, runtime, optional, provided
This commit is contained in:
Gerolf Seitz 2012-05-03 18:03:59 +02:00
parent 70462d7a62
commit f69915cb8c
4 changed files with 73 additions and 15 deletions

2
project.sbt Normal file → Executable file
View File

@ -4,7 +4,7 @@ name := "sbt-dependency-graph"
organization := "net.virtual-void"
version := "0.5.2"
version := "0.5.3-SNAPSHOT"
homepage := Some(url("http://github.com/jrudolph/sbt-dependency-graph"))

1
project/build.properties Normal file
View File

@ -0,0 +1 @@
sbt.version=0.12.0-Beta2

View File

@ -18,24 +18,54 @@ package net.virtualvoid.sbt.graph
import xml.parsing.ConstructingParser
import java.io.File
import xml.{XML, Node}
import collection.mutable.HashMap
import collection.mutable.MultiMap
import collection.mutable.{Set => MSet}
import sbt.Graph
import xml.{Document, XML, Node}
object IvyGraphMLDependencies extends App {
case class Module(organisation: String, name: String, version: String) {
def id: String = organisation+"."+name+"-"+version
def id: String = organisation+":"+name+":"+version
}
def transform(ivyReportFile: String, outputFile: String) {
val doc = ConstructingParser.fromSource(io.Source.fromFile(ivyReportFile), false).document
case class ModuleGraph(nodes: Seq[Module], edges: Seq[(Module, Module)])
def buildGraph(doc: Document): ModuleGraph = {
val edges = for {
mod <- doc \ "dependencies" \ "module"
caller <- mod \ "revision" \ "caller"
callerModule = nodeFromElement(caller, caller.attribute("callerrev").get.text)
depModule = nodeFromElement(mod, caller.attribute("rev").get.text)
} yield (callerModule, depModule)
mod <- doc \ "dependencies" \ "module"
caller <- mod \ "revision" \ "caller"
callerModule = nodeFromElement(caller, caller.attribute("callerrev").get.text)
depModule = nodeFromElement(mod, caller.attribute("rev").get.text)
} yield (callerModule, depModule)
val nodes = edges.flatMap(e => Seq(e._1, e._2)).distinct
ModuleGraph(nodes, edges)
}
def ascii(ivyReportFile: String): String = {
val doc = buildDoc(ivyReportFile)
val graph = buildGraph(doc)
import graph._
val deps = {
val m = new HashMap[Module, MSet[Module]] with MultiMap[Module, Module]
edges.foreach { case (from, to) => m.addBinding(from, to) }
m.toMap.mapValues(_.toSeq.sortBy(_.id))
}
// there should only be one root node (the project itself)
val roots = nodes.filter(n => !edges.exists(_._2 == n)).sortBy(_.id)
roots.map(root =>
Graph.toAscii[Module](root, node => deps.getOrElse(node, Seq.empty[Module]), _.id)
).mkString("\n")
}
def transform(ivyReportFile: String, outputFile: String) {
val doc = buildDoc(ivyReportFile)
val graph = buildGraph(doc)
import graph._
val nodesXml =
for (n <- nodes)
yield
@ -63,9 +93,11 @@ object IvyGraphMLDependencies extends App {
XML.save(outputFile, xml)
}
def nodeFromElement(element: Node, version: String): Module =
private def nodeFromElement(element: Node, version: String): Module =
Module(element.attribute("organisation").get.text, element.attribute("name").get.text, version)
private def buildDoc(ivyReportFile: String) = ConstructingParser.fromSource(io.Source.fromFile(ivyReportFile), false).document
def die(msg: String): Nothing = {
println(msg)
sys.exit(1)

33
src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala Normal file → Executable file
View File

@ -22,6 +22,10 @@ import Keys._
object Plugin extends sbt.Plugin {
val dependencyGraphTask = TaskKey[File]("dependency-graph",
"Creates a graphml file containing the dependency-graph for a project")
val asciiGraph = TaskKey[String]("ascii-graph",
"Returns a string containing the ascii representation of the dependency graph for a project")
val printAsciiGraph = TaskKey[Unit]("print-ascii-graph",
"Prints the ascii graph to the console")
val ivyReportF = SettingKey[String => File]("ivy-report-function",
"A function which returns the file containing the ivy report from the ivy cache for a given configuration")
val ivyReport = InputKey[File]("ivy-report",
@ -40,6 +44,19 @@ object Plugin extends sbt.Plugin {
report(args(0))
}
},
asciiGraph in Compile <<= asciiGraphTask(Compile),
asciiGraph in Test <<= asciiGraphTask(Test),
asciiGraph in Runtime <<= asciiGraphTask(Runtime),
asciiGraph in Provided <<= asciiGraphTask(Provided),
asciiGraph in Optional <<= asciiGraphTask(Optional),
printAsciiGraph in Compile <<= printAsciiGraphTask(Compile),
printAsciiGraph in Test <<= printAsciiGraphTask(Test),
printAsciiGraph in Runtime <<= printAsciiGraphTask(Runtime),
printAsciiGraph in Provided <<= printAsciiGraphTask(Provided),
printAsciiGraph in Optional <<= printAsciiGraphTask(Optional),
dependencyGraphTask <<= (ivyReportF, target, streams) map { (report, target, streams) =>
val resultFile = target / "dependencies.graphml"
IvyGraphMLDependencies.transform(report("compile").getAbsolutePath, resultFile.getAbsolutePath)
@ -48,11 +65,19 @@ object Plugin extends sbt.Plugin {
} dependsOn(deliverLocal)
)
def asciiGraphTask(conf: Configuration) = (ivyReportF in conf) map { (report) =>
IvyGraphMLDependencies.ascii(report(conf.name).getAbsolutePath)
} dependsOn(deliverLocal)
def printAsciiGraphTask(conf: Configuration) = (asciiGraph in conf, streams in conf) map { (graph, streams) =>
streams.log.info(graph)
}
def crossName(moduleId: ModuleID, scalaVersion: String) =
moduleId.name + (
if (moduleId.crossVersion)
"_"+scalaVersion
else
""
moduleId.crossVersion match {
case CrossVersion.Disabled => ""
case _ => "_"+scalaVersion
}
)
}