From 70462d7a62484c415bd11fb331aaa84c6e7fa070 Mon Sep 17 00:00:00 2001 From: Gerolf Seitz Date: Thu, 1 Dec 2011 23:57:05 +0100 Subject: [PATCH 1/6] Add version to nodes. --- .../sbt/graph/IvyGraphMLDependencies.scala | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/scala/net/virtualvoid/sbt/graph/IvyGraphMLDependencies.scala b/src/main/scala/net/virtualvoid/sbt/graph/IvyGraphMLDependencies.scala index def166018..b2f07bdea 100644 --- a/src/main/scala/net/virtualvoid/sbt/graph/IvyGraphMLDependencies.scala +++ b/src/main/scala/net/virtualvoid/sbt/graph/IvyGraphMLDependencies.scala @@ -21,18 +21,18 @@ import java.io.File import xml.{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) + 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 @@ -63,8 +63,8 @@ object IvyGraphMLDependencies extends App { XML.save(outputFile, xml) } - def nodeFromElement(element: Node): Module = - Module(element.attribute("organisation").get.text, element.attribute("name").get.text) + def nodeFromElement(element: Node, version: String): Module = + Module(element.attribute("organisation").get.text, element.attribute("name").get.text, version) def die(msg: String): Nothing = { println(msg) @@ -76,4 +76,4 @@ 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) -} \ No newline at end of file +} From f69915cb8c906ebe0a335e34f74a1330b8f8b4a5 Mon Sep 17 00:00:00 2001 From: Gerolf Seitz Date: Thu, 3 May 2012 18:03:59 +0200 Subject: [PATCH 2/6] 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 --- project.sbt | 2 +- project/build.properties | 1 + .../sbt/graph/IvyGraphMLDependencies.scala | 52 +++++++++++++++---- .../net/virtualvoid/sbt/graph/Plugin.scala | 33 ++++++++++-- 4 files changed, 73 insertions(+), 15 deletions(-) mode change 100644 => 100755 project.sbt create mode 100644 project/build.properties mode change 100644 => 100755 src/main/scala/net/virtualvoid/sbt/graph/IvyGraphMLDependencies.scala mode change 100644 => 100755 src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala diff --git a/project.sbt b/project.sbt old mode 100644 new mode 100755 index f000b7925..f123069ac --- a/project.sbt +++ b/project.sbt @@ -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")) diff --git a/project/build.properties b/project/build.properties new file mode 100644 index 000000000..b6fc683c7 --- /dev/null +++ b/project/build.properties @@ -0,0 +1 @@ +sbt.version=0.12.0-Beta2 diff --git a/src/main/scala/net/virtualvoid/sbt/graph/IvyGraphMLDependencies.scala b/src/main/scala/net/virtualvoid/sbt/graph/IvyGraphMLDependencies.scala old mode 100644 new mode 100755 index b2f07bdea..e981339b3 --- a/src/main/scala/net/virtualvoid/sbt/graph/IvyGraphMLDependencies.scala +++ b/src/main/scala/net/virtualvoid/sbt/graph/IvyGraphMLDependencies.scala @@ -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) diff --git a/src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala b/src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala old mode 100644 new mode 100755 index 6d98092d2..c386a4be7 --- a/src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala +++ b/src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala @@ -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 + } ) } \ No newline at end of file From 67225369911a7c4ea8f08edd50ea59e862c5cd88 Mon Sep 17 00:00:00 2001 From: Johannes Rudolph Date: Sun, 20 May 2012 18:19:51 +0200 Subject: [PATCH 3/6] depending on update is enough for ivy report to be generated --- src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala b/src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala index 6d98092d2..514db1ba7 100644 --- a/src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala +++ b/src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala @@ -45,7 +45,7 @@ object Plugin extends sbt.Plugin { IvyGraphMLDependencies.transform(report("compile").getAbsolutePath, resultFile.getAbsolutePath) streams.log.info("Wrote dependency graph to '%s'" format resultFile) resultFile - } dependsOn(deliverLocal) + } dependsOn(update) ) def crossName(moduleId: ModuleID, scalaVersion: String) = From be22b8c66321f13071df28b7faa71b42008dabef Mon Sep 17 00:00:00 2001 From: Johannes Rudolph Date: Sun, 20 May 2012 18:21:17 +0200 Subject: [PATCH 4/6] use `scalaVersion in update` to lookup report file This will help if you use the common scalaVersion := "2.9.2" scalaVersion in update := "2.9.1" idiom. --- src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala b/src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala index 514db1ba7..4bdbc264a 100644 --- a/src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala +++ b/src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala @@ -28,7 +28,7 @@ object Plugin extends sbt.Plugin { "A task which returns the location of the ivy report file for a given configuration (default `compile`).") def graphSettings = Seq( - ivyReportF <<= (projectID, scalaVersion, appConfiguration) { (projectID, scalaVersion, config) => + ivyReportF <<= (projectID, scalaVersion in update, appConfiguration) { (projectID, scalaVersion, config) => val home = config.provider.scalaProvider.launcher.ivyHome (c: String) => file("%s/cache/%s-%s-%s.xml" format (home, projectID.organization, crossName(projectID, scalaVersion), c)) }, From 116c13e51be4c78798f9a67b62dabf4f1271d174 Mon Sep 17 00:00:00 2001 From: Johannes Rudolph Date: Mon, 21 May 2012 11:13:55 +0200 Subject: [PATCH 5/6] use sbt 0.12.0-Beta2 for building Had to disable ls for now since it is not yet supported for 0.12.0-Beta2 --- build.sbt | 2 +- project.sbt | 8 ++++---- project/build.properties | 1 + project/gpg.sbt | 2 +- project/plugins.sbt | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) create mode 100644 project/build.properties diff --git a/build.sbt b/build.sbt index a20cff214..73af45493 100644 --- a/build.sbt +++ b/build.sbt @@ -1,2 +1,2 @@ -seq(lsSettings :_*) +//seq(lsSettings :_*) diff --git a/project.sbt b/project.sbt index f000b7925..22167785e 100644 --- a/project.sbt +++ b/project.sbt @@ -10,9 +10,9 @@ homepage := Some(url("http://github.com/jrudolph/sbt-dependency-graph")) licenses in GlobalScope += "Apache License 2.0" -> url("https://github.com/jrudolph/sbt-dependency-graph/raw/master/LICENSE") -(LsKeys.tags in LsKeys.lsync) := Seq("dependency", "graph", "sbt-plugin", "sbt") +//(LsKeys.tags in LsKeys.lsync) := Seq("dependency", "graph", "sbt-plugin", "sbt") -(LsKeys.docsUrl in LsKeys.lsync) <<= homepage +//(LsKeys.docsUrl in LsKeys.lsync) <<= homepage -(description in LsKeys.lsync) := - "An sbt plugin which allows to create a graphml file from the dependencies of the project." +//(description in LsKeys.lsync) := +// "An sbt plugin which allows to create a graphml file from the dependencies of the project." diff --git a/project/build.properties b/project/build.properties new file mode 100644 index 000000000..82c888f3c --- /dev/null +++ b/project/build.properties @@ -0,0 +1 @@ +sbt.version=0.12.0-Beta2 \ No newline at end of file diff --git a/project/gpg.sbt b/project/gpg.sbt index b53697a4d..5f5af3d10 100644 --- a/project/gpg.sbt +++ b/project/gpg.sbt @@ -1,3 +1,3 @@ resolvers += Resolver.url("scalasbt", new URL("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-releases"))(Resolver.ivyStylePatterns) -addSbtPlugin("com.jsuereth" % "xsbt-gpg-plugin" % "0.5") \ No newline at end of file +addSbtPlugin("com.jsuereth" % "xsbt-gpg-plugin" % "0.6", sbtVersion = "0.12.0-Beta2") \ No newline at end of file diff --git a/project/plugins.sbt b/project/plugins.sbt index e62de8942..e3e0db641 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,5 +1,5 @@ resolvers += "less is" at "http://repo.lessis.me" -addSbtPlugin("me.lessis" % "ls-sbt" % "0.1.0") +//addSbtPlugin("me.lessis" % "ls-sbt" % "0.1.1", sbtVersion = "0.12.0-Beta2") resolvers += "Coda Hale's Repo" at "http://repo.codahale.com" \ No newline at end of file From d5ffe97485278738642e39e5c19587cfd65889c5 Mon Sep 17 00:00:00 2001 From: Johannes Rudolph Date: Mon, 21 May 2012 11:31:28 +0200 Subject: [PATCH 6/6] improve DRYness --- .../net/virtualvoid/sbt/graph/Plugin.scala | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala b/src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala index 04ffe7427..e51f1fc9b 100755 --- a/src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala +++ b/src/main/scala/net/virtualvoid/sbt/graph/Plugin.scala @@ -31,7 +31,7 @@ object Plugin extends sbt.Plugin { val ivyReport = InputKey[File]("ivy-report", "A task which returns the location of the ivy report file for a given configuration (default `compile`).") - def graphSettings = Seq( + def graphSettings: Seq[Setting[_]] = seq( ivyReportF <<= (projectID, scalaVersion in update, appConfiguration) { (projectID, scalaVersion, config) => val home = config.provider.scalaProvider.launcher.ivyHome (c: String) => file("%s/cache/%s-%s-%s.xml" format (home, projectID.organization, crossName(projectID, scalaVersion), c)) @@ -45,25 +45,19 @@ object Plugin extends sbt.Plugin { } }, - 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) streams.log.info("Wrote dependency graph to '%s'" format resultFile) resultFile } dependsOn(update) - ) + ) ++ 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)