mirror of https://github.com/sbt/sbt.git
added `dependencyStats`: a tabular console output showing an overview of jar-sizes, fixes #83
This commit is contained in:
parent
f7e3a49e93
commit
c9d1f81149
|
|
@ -29,6 +29,7 @@ This plugin is an auto-plugin which will be automatically enabled starting from
|
|||
* `whatDependsOn <organization> <module> <revision>`: Find out what depends on an artifact. Shows a reverse dependency
|
||||
tree for the selected module.
|
||||
* `dependencyLicenseInfo`: show dependencies grouped by declared license
|
||||
* `dependencyStats`: Shows a table with each module a row with (transitive) Jar sizes and number of dependencies
|
||||
* `dependencyGraphMl`: 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.
|
||||
* `dependencyDot`: Generates a .dot file with the project's dependencies to `target/dependencies-<config>.dot`.
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ trait DependencyGraphKeys {
|
|||
"Prints an ascii tree of all the dependencies to the console")
|
||||
val dependencyList = TaskKey[Unit]("dependency-list",
|
||||
"Prints a list of all dependencies to the console")
|
||||
val dependencyStats = TaskKey[Unit]("dependency-stats",
|
||||
"Prints statistics for all dependencies to the console")
|
||||
val ivyReportFunction = 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 = TaskKey[File]("ivy-report",
|
||||
|
|
|
|||
|
|
@ -86,7 +86,8 @@ object DependencyGraphSettings {
|
|||
java.awt.Desktop.getDesktop.browse(uri)
|
||||
uri
|
||||
},
|
||||
dependencyList <<= (moduleGraph, streams).map((graph, streams) ⇒ streams.log.info(rendering.FlatList.render(graph, _.id.idString))),
|
||||
dependencyList <<= printFromGraph(rendering.FlatList.render(_, _.id.idString)),
|
||||
dependencyStats <<= printFromGraph(rendering.Statistics.renderModuleStatsList),
|
||||
dependencyDotHeader := """digraph "dependency-graph" {
|
||||
| graph[rankdir="LR"]
|
||||
| edge [
|
||||
|
|
@ -159,6 +160,9 @@ object DependencyGraphSettings {
|
|||
def print(key: TaskKey[String]) =
|
||||
(streams, key) map (_.log.info(_))
|
||||
|
||||
def printFromGraph(f: ModuleGraph ⇒ String) =
|
||||
(streams, moduleGraph) map ((streams, graph) ⇒ streams.log.info(f(graph)))
|
||||
|
||||
def showLicenseInfo(graph: ModuleGraph, streams: TaskStreams) {
|
||||
val output =
|
||||
graph.nodes.filter(_.isUsed).groupBy(_.license).toSeq.sortBy(_._1).map {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,13 @@ object SbtUpdateReport {
|
|||
|
||||
def moduleEdge(chosenVersion: Option[String])(report: ModuleReport): (Module, Seq[Edge]) = {
|
||||
val evictedByVersion = if (report.evicted) chosenVersion else None
|
||||
(Module(report.module, license = report.licenses.headOption.map(_._1), evictedByVersion = evictedByVersion, error = report.problem),
|
||||
val jarFile = report.artifacts.find(_._1.`type` == "jar").orElse(report.artifacts.find(_._1.extension == "jar")).map(_._2)
|
||||
(Module(
|
||||
id = report.module,
|
||||
license = report.licenses.headOption.map(_._1),
|
||||
evictedByVersion = evictedByVersion,
|
||||
jarFile = jarFile,
|
||||
error = report.problem),
|
||||
report.callers.map(caller ⇒ Edge(caller.caller, report.module)))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package net.virtualvoid.sbt.graph
|
||||
|
||||
import java.io.File
|
||||
|
||||
import scala.collection.mutable.{ MultiMap, HashMap, Set }
|
||||
|
||||
case class ModuleId(organisation: String,
|
||||
|
|
@ -27,6 +29,7 @@ case class Module(id: ModuleId,
|
|||
license: Option[String] = None,
|
||||
extraInfo: String = "",
|
||||
evictedByVersion: Option[String] = None,
|
||||
jarFile: Option[File] = None,
|
||||
error: Option[String] = None) {
|
||||
def hadError: Boolean = error.isDefined
|
||||
def isUsed: Boolean = !isEvicted
|
||||
|
|
@ -53,12 +56,15 @@ case class ModuleGraph(nodes: Seq[Module], edges: Seq[Edge]) {
|
|||
}
|
||||
m.toMap.mapValues(_.toSeq.sortBy(_.id.idString)).withDefaultValue(Nil)
|
||||
}
|
||||
|
||||
def roots: Seq[Module] =
|
||||
nodes.filter(n ⇒ !edges.exists(_._2 == n.id)).sortBy(_.id.idString)
|
||||
}
|
||||
|
||||
import sbinary.{ Format, DefaultProtocol }
|
||||
object ModuleGraphProtocol extends DefaultProtocol {
|
||||
implicit def seqFormat[T: Format]: Format[Seq[T]] = wrap[Seq[T], List[T]](_.toList, _.toSeq)
|
||||
implicit val ModuleIdFormat: Format[ModuleId] = asProduct3(ModuleId)(ModuleId.unapply(_).get)
|
||||
implicit val ModuleFormat: Format[Module] = asProduct5(Module)(Module.unapply(_).get)
|
||||
implicit val ModuleFormat: Format[Module] = asProduct6(Module)(Module.unapply(_).get)
|
||||
implicit val ModuleGraphFormat: Format[ModuleGraph] = asProduct2(ModuleGraph.apply _)(ModuleGraph.unapply(_).get)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ object AsciiTree {
|
|||
val deps = graph.dependencyMap
|
||||
|
||||
// there should only be one root node (the project itself)
|
||||
val roots = graph.nodes.filter(n ⇒ !graph.edges.exists(_._2 == n.id)).sortBy(_.id.idString)
|
||||
val roots = graph.roots
|
||||
roots.map { root ⇒
|
||||
AsciiTreeLayout.toAscii[Module](root, node ⇒ deps.getOrElse(node.id, Seq.empty[Module]), displayModule)
|
||||
}.mkString("\n")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright 2016 Johannes Rudolph
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.virtualvoid.sbt.graph
|
||||
package rendering
|
||||
|
||||
object Statistics {
|
||||
def renderModuleStatsList(graph: ModuleGraph): String = {
|
||||
case class ModuleStats(
|
||||
id: ModuleId,
|
||||
numDirectDependencies: Int,
|
||||
numTransitiveDependencies: Int,
|
||||
selfSize: Option[Long],
|
||||
transitiveSize: Long,
|
||||
transitiveDependencyStats: Map[ModuleId, ModuleStats]) {
|
||||
def transitiveStatsWithSelf: Map[ModuleId, ModuleStats] = transitiveDependencyStats + (id -> this)
|
||||
}
|
||||
|
||||
def statsFor(moduleId: ModuleId): ModuleStats = {
|
||||
val directDependencies = graph.dependencyMap(moduleId).filterNot(_.isEvicted).map(_.id)
|
||||
val dependencyStats = directDependencies.map(statsFor).flatMap(_.transitiveStatsWithSelf).toMap
|
||||
val selfSize = graph.module(moduleId).jarFile.filter(_.exists).map(_.length)
|
||||
val numDirectDependencies = directDependencies.size
|
||||
val numTransitiveDependencies = dependencyStats.size
|
||||
val transitiveSize = selfSize.getOrElse(0L) + dependencyStats.map(_._2.selfSize.getOrElse(0L)).sum
|
||||
|
||||
ModuleStats(moduleId, numDirectDependencies, numTransitiveDependencies, selfSize, transitiveSize, dependencyStats)
|
||||
}
|
||||
|
||||
def format(stats: ModuleStats): String = {
|
||||
import stats._
|
||||
def mb(bytes: Long): Double = bytes.toDouble / 1000000
|
||||
val selfSize =
|
||||
stats.selfSize match {
|
||||
case Some(size) ⇒ f"${mb(size)}%7.3f"
|
||||
case None ⇒ "-------"
|
||||
}
|
||||
f"${mb(transitiveSize)}%7.3f MB $selfSize MB $numTransitiveDependencies%4d $numDirectDependencies%4d ${id.idString}%s"
|
||||
}
|
||||
|
||||
val allStats =
|
||||
graph.roots.flatMap(r ⇒ statsFor(r.id).transitiveStatsWithSelf).toMap.values.toSeq
|
||||
.sortBy(s ⇒ (-s.transitiveSize, -s.numTransitiveDependencies))
|
||||
|
||||
val header = " TotSize JarSize #TDe #Dep Module\n"
|
||||
|
||||
header +
|
||||
allStats.map(format).mkString("\n") +
|
||||
"""
|
||||
|
|
||||
|Columns are
|
||||
| - Jar-Size including dependencies
|
||||
| - Jar-Size
|
||||
| - Number of transitive dependencies
|
||||
| - Number of direct dependencies
|
||||
| - ModuleID""".stripMargin
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue