/* sbt -- Simple Build Tool * Copyright 2008, 2009, 2010 Mark Harrah */ package sbt import java.io.{File, Writer} import inc.Relations object DotGraph { private def fToString(roots: Iterable[File]): (File => String) = (x: File) => sourceToString(roots, x) def sources(relations: Relations, outputDirectory: File, sourceRoots: Iterable[File]) { val toString = fToString(sourceRoots) apply(relations, outputDirectory, toString, toString) } def packages(relations: Relations, outputDirectory: File, sourceRoots: Iterable[File]) { val packageOnly = (path: String) => { val last = path.lastIndexOf(File.separatorChar) val packagePath = (if(last > 0) path.substring(0, last) else path).trim if(packagePath.isEmpty) "" else packagePath.replace(File.separatorChar, '.') } val toString = packageOnly compose fToString(sourceRoots) apply(relations, outputDirectory, toString, toString) } def apply(relations: Relations, outputDir: File, sourceToString: File => String, externalToString: File => String) { def file(name: String) = new File(outputDir, name) IO.createDirectory(outputDir) generateGraph(file("int-source-deps"), "dependencies", relations.internalSrcDep, sourceToString, sourceToString) generateGraph(file("binary-dependencies"), "externalDependencies", relations.binaryDep, externalToString, sourceToString) } def generateGraph[Key, Value](file: File, graphName: String, relation: Relation[Key, Value], keyToString: Key => String, valueToString: Value => String) { import scala.collection.mutable.{HashMap, HashSet} val mappedGraph = new HashMap[String, HashSet[String]] for( (key, values) <- relation.forwardMap; keyString = keyToString(key); value <- values) mappedGraph.getOrElseUpdate(keyString, new HashSet[String]) += valueToString(value) val mappings = for { (dependsOn, dependants) <- mappedGraph.toSeq dependant <- dependants if dependant != dependsOn && !dependsOn.isEmpty && !dependant.isEmpty } yield "\"" + dependant + "\" -> \"" + dependsOn + "\"" val lines = ("digraph " + graphName + " {") +: mappings :+ "}" IO.writeLines(file, lines) } def sourceToString(roots: Iterable[File], source: File) = { val rawName = relativized(roots, source).trim if(rawName.endsWith(".scala")) rawName.substring(0, rawName.length - ".scala".length) else rawName } private def relativized(roots: Iterable[File], path: File): String = { val relativized = roots.flatMap(root => Path.relativize(root, path)) val shortest = (Int.MaxValue /: relativized)(_ min _.length) relativized.find(_.length == shortest).getOrElse(path.getName) } }