mirror of https://github.com/sbt/sbt.git
Merge branch 'sbt-0.12'
We can now support the previously 0.12 only features by explicitly including the Graph class in 0.11 builds. Conflicts: project.sbt project/build.properties project/gpg.sbt project/plugins.sbt src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala
This commit is contained in:
commit
9f4852ac64
|
|
@ -0,0 +1,43 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2011 Mark Harrah, Eugene Yokota
|
||||
*
|
||||
* Copied from sbt 0.12 source code
|
||||
*/
|
||||
package sbt
|
||||
|
||||
object Graph
|
||||
{
|
||||
// [info] foo
|
||||
// [info] +-bar
|
||||
// [info] | +-baz
|
||||
// [info] |
|
||||
// [info] +-quux
|
||||
def toAscii[A](top: A, children: A => Seq[A], display: A => String): String = {
|
||||
val maxColumn = jline.Terminal.getTerminal.getTerminalWidth - 8
|
||||
val twoSpaces = " " + " " // prevent accidentally being converted into a tab
|
||||
def limitLine(s: String): String =
|
||||
if (s.length > maxColumn) s.slice(0, maxColumn - 2) + ".."
|
||||
else s
|
||||
def insertBar(s: String, at: Int): String =
|
||||
s.slice(0, at) +
|
||||
(s(at).toString match {
|
||||
case " " => "|"
|
||||
case x => x
|
||||
}) +
|
||||
s.slice(at + 1, s.length)
|
||||
def toAsciiLines(node: A, level: Int): Vector[String] = {
|
||||
val line = limitLine((twoSpaces * level) + (if (level == 0) "" else "+-") + display(node))
|
||||
val cs = Vector(children(node): _*)
|
||||
val childLines = cs map {toAsciiLines(_, level + 1)}
|
||||
val withBar = childLines.zipWithIndex flatMap {
|
||||
case (lines, pos) if pos < (cs.size - 1) => lines map {insertBar(_, 2 * (level + 1))}
|
||||
case (lines, pos) =>
|
||||
if (lines.last.trim != "") lines ++ Vector(twoSpaces * (level + 1))
|
||||
else lines
|
||||
}
|
||||
line +: withBar
|
||||
}
|
||||
|
||||
toAsciiLines(top, 0).mkString("\n")
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
def id: String = organisation+"."+name
|
||||
case class Module(organisation: String, name: String, version: String) {
|
||||
def id: String = organisation+":"+name+":"+version
|
||||
}
|
||||
def transform(ivyReportFile: String, outputFile: String) {
|
||||
val doc = ConstructingParser.fromSource(io.Source.fromFile(ivyReportFile), false).document
|
||||
|
||||
val edges =
|
||||
for (mod <- doc \ "dependencies" \ "module";
|
||||
depModule = nodeFromElement(mod);
|
||||
caller <- mod \ "revision" \ "caller";
|
||||
callerModule = nodeFromElement(caller))
|
||||
yield (callerModule, depModule)
|
||||
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)
|
||||
|
||||
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,8 +93,10 @@ object IvyGraphMLDependencies extends App {
|
|||
|
||||
XML.save(outputFile, xml)
|
||||
}
|
||||
def nodeFromElement(element: Node): Module =
|
||||
Module(element.attribute("organisation").get.text, element.attribute("name").get.text)
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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 = TaskKey[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",
|
||||
|
|
@ -46,7 +50,21 @@ object Plugin extends sbt.Plugin {
|
|||
streams.log.info("Wrote dependency graph to '%s'" format resultFile)
|
||||
resultFile
|
||||
}
|
||||
)
|
||||
) ++ Seq(Compile, Test, Runtime, Provided, Optional).flatMap(asciiGraphSettings)
|
||||
|
||||
def asciiGraphSettings(config: Configuration) =
|
||||
seq(
|
||||
asciiGraph in config <<= asciiGraphTask(config),
|
||||
printAsciiGraph in config <<= printAsciiGraphTask(config)
|
||||
)
|
||||
|
||||
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(ivyModule: IvySbt#Module) =
|
||||
ivyModule.moduleSettings match {
|
||||
|
|
|
|||
Loading…
Reference in New Issue