dry up graph structure generation and expose graph structure for other consumers as sbt `module-graph` task

This commit is contained in:
Johannes Rudolph 2012-10-21 19:12:35 +02:00
parent 445d53a583
commit fb3d99e5e0
2 changed files with 36 additions and 46 deletions

View File

@ -33,6 +33,9 @@ object IvyGraphMLDependencies extends App {
case class ModuleGraph(nodes: Seq[Module], edges: Seq[(Module, Module)])
def graph(ivyReportFile: String): ModuleGraph =
buildGraph(buildDoc(ivyReportFile))
def buildGraph(doc: Document): ModuleGraph = {
val edges = for {
mod <- doc \ "dependencies" \ "module"
@ -46,7 +49,23 @@ object IvyGraphMLDependencies extends App {
ModuleGraph(nodes, edges)
}
private def asciiGraph(moduleGraph: ModuleGraph): layout.Graph[String] = {
def asciiGraph(graph: ModuleGraph): String =
Layouter.renderGraph(buildAsciiGraph(graph))
def asciiTree(graph: ModuleGraph): String = {
val deps = {
val m = new HashMap[String, MSet[Module]] with MultiMap[String, Module]
graph.edges.foreach { case (from, to) => m.addBinding(from.id, to) }
m.toMap.mapValues(_.toSeq.sortBy(_.id))
}
// there should only be one root node (the project itself)
val roots = graph.nodes.filter(n => !graph.edges.exists(_._2 == n)).sortBy(_.id)
roots.map { root =>
Graph.toAscii[Module](root, node => deps.getOrElse(node.id, Seq.empty[Module]), x => x.id + x.error.map(" (error: "+_+")").getOrElse(""))
}.mkString("\n")
}
private def buildAsciiGraph(moduleGraph: ModuleGraph): layout.Graph[String] = {
def renderVertex(module: Module): String = {
module.name + "\n" + module.organisation + "\n" + module.version
}
@ -55,35 +74,9 @@ object IvyGraphMLDependencies extends App {
layout.Graph(vertices, edges)
}
def asciiGraph(ivyReportFile: String): String = {
val doc = buildDoc(ivyReportFile)
val graph = buildGraph(doc)
Layouter.renderGraph(asciiGraph(graph))
}
def asciiTree(ivyReportFile: String): String = {
val doc = buildDoc(ivyReportFile)
val graph = buildGraph(doc)
import graph._
val deps = {
val m = new HashMap[String, MSet[Module]] with MultiMap[String, Module]
edges.foreach { case (from, to) => m.addBinding(from.id, 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.id, Seq.empty[Module]), x => x.id + x.error.map(" (error: "+_+")").getOrElse(""))
).mkString("\n")
}
def transform(ivyReportFile: String, outputFile: String) {
val doc = buildDoc(ivyReportFile)
val graph = buildGraph(doc)
import graph._
def saveAsGraphML(graph: ModuleGraph, outputFile: String) {
val nodesXml =
for (n <- nodes)
for (n <- graph.nodes)
yield
<node id={n.id}><data key="d0">
<y:ShapeNode>
@ -92,7 +85,7 @@ object IvyGraphMLDependencies extends App {
</data></node>
val edgesXml =
for (e <- edges)
for (e <- graph.edges)
yield <edge source={e._1.id} target={e._2.id} />
val xml =
@ -123,5 +116,5 @@ object IvyGraphMLDependencies extends App {
val file = args.lift(0).filter(f => new File(f).exists).getOrElse(die(usage))
val inputFile = args.lift(1).getOrElse(die(usage))
transform(file, inputFile)
saveAsGraphML(graph(file), inputFile)
}

View File

@ -25,6 +25,8 @@ object Plugin extends sbt.Plugin {
"The location the graphml file should be generated at")
val dependencyGraphML = TaskKey[File]("dependency-graph-ml",
"Creates a graphml file containing the dependency-graph for a project")
val moduleGraph = TaskKey[IvyGraphMLDependencies.ModuleGraph]("module-graph",
"The dependency graph for a project")
val asciiGraph = TaskKey[String]("dependency-graph-string",
"Returns a string containing the ascii representation of the dependency graph for a project")
val dependencyGraph = TaskKey[Unit]("dependency-graph",
@ -57,35 +59,30 @@ object Plugin extends sbt.Plugin {
def ivyReportForConfig(config: Configuration) = inConfig(config)(seq(
ivyReport <<= ivyReportFunction map (_(config.toString)) dependsOn(ignoreMissingUpdate),
asciiGraph <<= asciiGraphTask,
dependencyGraph <<= printAsciiGraphTask,
asciiTree <<= asciiTreeTask,
dependencyTree <<= printAsciiTreeTask,
moduleGraph <<= ivyReport map (absoluteReportPath.andThen(IvyGraphMLDependencies.graph)),
asciiGraph <<= moduleGraph map IvyGraphMLDependencies.asciiGraph,
dependencyGraph <<= print(asciiGraph),
asciiTree <<= moduleGraph map IvyGraphMLDependencies.asciiTree,
dependencyTree <<= print(asciiTree),
dependencyGraphMLFile <<= target / "dependencies-%s.graphml".format(config.toString),
dependencyGraphML <<= dependencyGraphMLTask,
Compat.ignoreMissingUpdateT
))
def asciiGraphTask = (ivyReport) map { report =>
IvyGraphMLDependencies.asciiGraph(report.getAbsolutePath)
}
def printAsciiGraphTask =
(streams, asciiGraph) map (_.log.info(_))
def dependencyGraphMLTask =
(ivyReport, dependencyGraphMLFile, streams) map { (report, resultFile, streams) =>
IvyGraphMLDependencies.transform(report.getAbsolutePath, resultFile.getAbsolutePath)
(moduleGraph, dependencyGraphMLFile, streams) map { (graph, resultFile, streams) =>
IvyGraphMLDependencies.saveAsGraphML(graph, resultFile.getAbsolutePath)
streams.log.info("Wrote dependency graph to '%s'" format resultFile)
resultFile
}
def asciiTreeTask = (ivyReport) map { report =>
IvyGraphMLDependencies.asciiTree(report.getAbsolutePath)
}
def absoluteReportPath = (file: File) => file.getAbsolutePath
def printAsciiTreeTask =
(streams, asciiTree) map (_.log.info(_))
def print(key: TaskKey[String]) =
(streams, key) map (_.log.info(_))
def crossName(ivyModule: IvySbt#Module) =
ivyModule.moduleSettings match {