Merge commit 'pull/25'

Conflicts:
	src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala
This commit is contained in:
Johannes Rudolph 2013-01-22 14:34:40 +01:00
commit f28b009b02
8 changed files with 135 additions and 7 deletions

View File

@ -44,6 +44,8 @@ Tasks & Settings
* `dependency-graph`: Shows an ASCII graph of the project's dependencies on the sbt console
* `dependency-graph-ml`: Generates a .graphml file with the project's dependencies to `target/dependencies-<config>.graphml`.
Use e.g. [yEd](http://www.yworks.com/en/products_yed_about.html) to format the graph to your needs.
* `dependency-dot`: Generates a .dot file with the project's dependencies to `target/dependencies-<config>.dot`.
Use [graphviz](http://www.graphviz.org/) to render it to your preferred graphic format.
* `dependency-tree`: Shows an ASCII tree representation of the project's dependencies
* `what-depends-on <organization> <module> <revision>`: Find out what depends on an artifact. Shows a reverse dependency
tree for the selected module.
@ -52,6 +54,10 @@ Tasks & Settings
If `true`, instead of showing the dependency `"[S]"` is appended to the artifact name. Set to `false` if
you want the scala-library dependency to appear in the output. (default: true)
* `dependency-graph-ml-file`: a setting which allows configuring the output path of `dependency-graph-ml`.
* `dependency-dot-file`: a setting which allows configuring the output path of `dependency-dot`.
* `dependency-dot-header`: a setting to customize the header of the dot file (e.g. to set your preferred node shapes).
* `dependency-dot-nodes-label`: defines the formation of a node label
(default set to `[organisation]<BR/><B>[name]</B><BR/>[version]`)
* `ivy-report`: let's ivy generate the resolution report for you project. Use
`show ivy-report` for the filename of the generated report
@ -95,4 +101,4 @@ Copyright (c) 2011, 2012 Johannes Rudolph
Published under the [Apache License 2.0](http://en.wikipedia.org/wiki/Apache_license).
[example project]: https://gist.github.com/3106492
[example project]: https://gist.github.com/3106492

View File

@ -195,6 +195,29 @@ object IvyGraphMLDependencies extends App {
XML.save(outputFile, xml)
}
def saveAsDot(graph: ModuleGraph,
dotHead: String,
nodeFormation: (String, String, String) => String,
outputFile: File): File = {
val nodes = {
for (n <- graph.nodes)
yield
""" "%s"[label=%s]""".format(n.id.idString,
nodeFormation(n.id.organisation, n.id.name, n.id.version))
}.mkString("\n")
val edges = {
for ( e <- graph.edges)
yield
""" "%s" -> "%s"""".format(e._1.idString, e._2.idString)
}.mkString("\n")
val dot = "%s\n%s\n%s\n}".format(dotHead, nodes, edges)
sbt.IO.write(outputFile, dot)
outputFile
}
def moduleIdFromElement(element: Node, version: String): ModuleId =
ModuleId(element.attribute("organisation").get.text, element.attribute("name").get.text, version)

View File

@ -28,6 +28,14 @@ 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 dependencyDotFile = SettingKey[File]("dependency-dot-file",
"The location the dot file should be generated at")
val dependencyDotNodeLabel = SettingKey[(String,String,String) => String]("dependency-dot-node-label",
"Returns a formated string of a dependency. Takes organisation, name and version as parameters")
val dependencyDotHeader = SettingKey[String]("dependency-dot-header",
"The header of the dot file. (e.g. to set your preferred node shapes)")
val dependencyDot = TaskKey[File]("dependency-dot",
"Creates a dot file containing the dpendency-graph for a project")
val moduleGraph = TaskKey[IvyGraphMLDependencies.ModuleGraph]("module-graph",
"The dependency graph for a project")
val asciiGraph = TaskKey[String]("dependency-graph-string",
@ -104,6 +112,19 @@ object Plugin extends sbt.Plugin {
dependencyTree <<= print(asciiTree),
dependencyGraphMLFile <<= target / "dependencies-%s.graphml".format(config.toString),
dependencyGraphML <<= dependencyGraphMLTask,
dependencyDotFile <<= target / "dependencies-%s.dot".format(config.toString),
dependencyDot <<= dependencyDotTask,
dependencyDotHeader := """digraph "dependency-graph" {
| graph[rankdir="LR"]
| node [
| shape="record"
| ]
| edge [
| arrowtail="none"
| ]""".stripMargin,
dependencyDotNodeLabel := { (organisation: String, name: String, version: String) =>
"""<%s<BR/><B>%s</B><BR/>%s>""".format(organisation, name, version)
},
whatDependsOn <<= InputTask(artifactIdParser) { module =>
(module, streams, moduleGraph) map { (module, streams, graph) =>
streams.log.info(IvyGraphMLDependencies.asciiTree(IvyGraphMLDependencies.reverseGraphStartingAt(graph, module)))
@ -121,7 +142,14 @@ object Plugin extends sbt.Plugin {
streams.log.info("Wrote dependency graph to '%s'" format resultFile)
resultFile
}
def dependencyDotTask =
(moduleGraph, dependencyDotHeader, dependencyDotNodeLabel, dependencyDotFile, streams).map {
(graph, dotHead, nodeLabel, outFile, streams) =>
val resultFile = IvyGraphMLDependencies.saveAsDot(graph, dotHead, nodeLabel, outFile)
streams.log.info("Wrote dependency graph to '%s'" format resultFile)
resultFile
}
def absoluteReportPath = (file: File) => file.getAbsolutePath
def print(key: TaskKey[String]) =

View File

@ -6,8 +6,8 @@ scalaVersion := "2.9.2"
libraryDependencies ++= Seq(
"org.slf4j" % "slf4j-api" % "1.7.2",
"ch.qos.logback" % "logback-classic" % "1.0.7",
"com.typesafe.akka" % "akka-actor" % "2.0.3")
"ch.qos.logback" % "logback-classic" % "1.0.7"
)
TaskKey[Unit]("check") <<= (ivyReport in Test, asciiTree in Test) map { (report, graph) =>
def sanitize(str: String): String = str.split('\n').drop(1).map(_.trim).mkString("\n")
@ -17,9 +17,6 @@ TaskKey[Unit]("check") <<= (ivyReport in Test, asciiTree in Test) map { (report,
| | +-ch.qos.logback:logback-core:1.0.7
| | +-org.slf4j:slf4j-api:1.6.6 (evicted by: 1.7.2)
| |
| +-com.typesafe.akka:akka-actor:2.0.3 [S]
| | +-com.typesafe:config:0.3.1
| |
| +-org.slf4j:slf4j-api:1.7.2
| """.stripMargin
IO.writeLines(file("/tmp/blib"), sanitize(graph).split("\n"))

View File

@ -1 +1 @@
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.7.0-RC3")
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.7.1-SNAPSHOT")

View File

@ -0,0 +1,71 @@
import collection.mutable.ListBuffer
import net.virtualvoid.sbt.graph.Plugin._
import sbt._
import sbt.Keys._
object Build extends sbt.Build {
def defaultSettings =
seq(scalaVersion := "2.9.2")
lazy val justATransiviteDependencyEndpointProject =
Project("just-a-transitive-dependency-endpoint", file("."))
.settings(defaultSettings: _*)
lazy val justATransitiveDependencyProject =
Project("just-a-transitive-dependency", file("."))
.settings(defaultSettings: _*)
.dependsOn(justATransiviteDependencyEndpointProject)
lazy val justADependencyProject =
Project("just-a-dependency", file("."))
.settings(defaultSettings: _*)
lazy val test_project =
Project("test-dot-file-generation", file("."))
.settings(graphSettings: _*)
.settings(defaultSettings: _*)
.settings(
TaskKey[Unit]("check") <<= (dependencyDot in Compile) map { (dotFile) =>
val expectedGraph =
"""digraph "dependency-graph" {
| graph[rankdir="LR"]
| node [
| shape="record"
| ]
| edge [
| arrowtail="none"
| ]
| "test-dot-file-generation:test-dot-file-generation_2.9.2:0.1-SNAPSHOT"[label=<test-dot-file-generation<BR/><B>test-dot-file-generation_2.9.2</B><BR/>0.1-SNAPSHOT>]
| "just-a-transitive-dependency:just-a-transitive-dependency_2.9.2:0.1-SNAPSHOT"[label=<just-a-transitive-dependency<BR/><B>just-a-transitive-dependency_2.9.2</B><BR/>0.1-SNAPSHOT>]
| "just-a-transitive-dependency-endpoint:just-a-transitive-dependency-endpoint_2.9.2:0.1-SNAPSHOT"[label=<just-a-transitive-dependency-endpoint<BR/><B>just-a-transitive-dependency-endpoint_2.9.2</B><BR/>0.1-SNAPSHOT>]
| "just-a-dependency:just-a-dependency_2.9.2:0.1-SNAPSHOT"[label=<just-a-dependency<BR/><B>just-a-dependency_2.9.2</B><BR/>0.1-SNAPSHOT>]
| "test-dot-file-generation:test-dot-file-generation_2.9.2:0.1-SNAPSHOT" -> "just-a-transitive-dependency:just-a-transitive-dependency_2.9.2:0.1-SNAPSHOT"
| "just-a-transitive-dependency:just-a-transitive-dependency_2.9.2:0.1-SNAPSHOT" -> "just-a-transitive-dependency-endpoint:just-a-transitive-dependency-endpoint_2.9.2:0.1-SNAPSHOT"
| "test-dot-file-generation:test-dot-file-generation_2.9.2:0.1-SNAPSHOT" -> "just-a-dependency:just-a-dependency_2.9.2:0.1-SNAPSHOT"
|}
""".stripMargin
val graph : String = scala.io.Source.fromFile(dotFile.getAbsolutePath).mkString
val errors = compareByLine(graph, expectedGraph)
require(errors.isEmpty , errors.mkString("\n"))
()
}
)
.dependsOn(justADependencyProject, justATransitiveDependencyProject)
def compareByLine(got : String, expected : String) : Seq[String] = {
val errors = ListBuffer[String]()
got.split("\n").zip(expected.split("\n").toSeq).zipWithIndex.foreach { case((got_line : String, expected_line : String), i : Int) =>
if(got_line != expected_line) {
errors.append(
"""not matching lines at line %s
|expected: %s
|got: %s
|""".stripMargin.format(i,expected_line, got_line))
}
}
errors
}
}

View File

@ -0,0 +1 @@
../../plugins.sbt

View File

@ -0,0 +1,2 @@
> project test-dot-file-generation
> check