Skip to content

Commit

Permalink
In-source sbt-dependency-graph
Browse files Browse the repository at this point in the history
Ref sbt/sbt-dependency-graph#178

This in-sources sbt-dependency-graph.
  • Loading branch information
eed3si9n committed Sep 22, 2020
1 parent 894d005 commit 17b7545
Show file tree
Hide file tree
Showing 97 changed files with 1,235 additions and 2,368 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ env:
matrix:
- SBT_CMD="mimaReportBinaryIssues ; javafmtCheck ; Test / javafmtCheck; scalafmtCheckAll ; scalafmtSbtCheck; serverTestProj/scalafmtCheckAll; headerCheck ;test:headerCheck ;whitesourceOnPush ;test:compile; publishLocal; test; serverTestProj/test; doc; $UTIL_TESTS; ++$SCALA_213; $UTIL_TESTS"
- SBT_CMD="scripted actions/* apiinfo/* compiler-project/* ivy-deps-management/* reporter/* tests/* watch/* classloader-cache/* package/*"
- SBT_CMD="scripted dependency-management/* plugins/* project-load/* java/* run/* nio/*"
- SBT_CMD="scripted dependency-graph/* dependency-management/* plugins/* project-load/* java/* run/* nio/*"
- SBT_CMD="repoOverrideTest:scripted dependency-management/*; scripted source-dependencies/* project/*"

matrix:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,10 @@ object Terminal {
private[this] lazy val isColorEnabledProp: Option[Boolean] =
sys.props.get("sbt.color").orElse(sys.props.get("sbt.colour")).flatMap(parseLogOption)

private[sbt] def red(str: String, doRed: Boolean): String =
if (formatEnabledInEnv && doRed) Console.RED + str + Console.RESET
else str

/**
*
* @param isServer toggles whether or not this is a server of client process
Expand Down
2 changes: 1 addition & 1 deletion main/src/main/scala/sbt/Defaults.scala
Original file line number Diff line number Diff line change
Expand Up @@ -880,7 +880,7 @@ object Defaults extends BuildCommon {
Seq(
initialCommands :== "",
cleanupCommands :== "",
asciiGraphWidth :== 40
asciiGraphWidth :== 80
)
)

Expand Down
1 change: 1 addition & 0 deletions main/src/main/scala/sbt/internal/PluginDiscovery.scala
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ object PluginDiscovery {
"sbt.plugins.SemanticdbPlugin" -> sbt.plugins.SemanticdbPlugin,
"sbt.plugins.JUnitXmlReportPlugin" -> sbt.plugins.JUnitXmlReportPlugin,
"sbt.plugins.Giter8TemplatePlugin" -> sbt.plugins.Giter8TemplatePlugin,
"sbt.plugins.DependencyGraphPlugin" -> sbt.plugins.DependencyGraphPlugin,
)
val detectedAutoPlugins = discover[AutoPlugin](AutoPlugins)
val allAutoPlugins = (defaultAutoPlugins ++ detectedAutoPlugins.modules) map {
Expand Down
48 changes: 32 additions & 16 deletions main/src/main/scala/sbt/internal/SettingGraph.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,12 @@ object Graph {
// [info] | +-baz
// [info] |
// [info] +-quux
def toAscii[A](top: A, children: A => Seq[A], display: A => String, defaultWidth: Int): String = {
val maxColumn = math.max(Terminal.get.getWidth, defaultWidth) - 8
def toAscii[A](
top: A,
children: A => Seq[A],
display: A => String,
maxColumn: Int
): String = {
val twoSpaces = " " + " " // prevent accidentally being converted into a tab
def limitLine(s: String): String =
if (s.length > maxColumn) s.slice(0, maxColumn - 2) + ".."
Expand All @@ -96,21 +100,33 @@ object Graph {
}) +
s.slice(at + 1, s.length)
else s
def toAsciiLines(node: A, level: Int): (String, 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 ((line, withBar), pos) if pos < (cs.size - 1) =>
(line +: withBar) map { insertBar(_, 2 * (level + 1)) }
case ((line, withBar), _) if withBar.lastOption.getOrElse(line).trim != "" =>
(line +: withBar) ++ Vector(twoSpaces * (level + 1))
case ((line, withBar), _) => line +: withBar
def toAsciiLines(node: A, level: Int, parents: Set[A]): Vector[String] =
if (parents contains node) // cycle
Vector(limitLine((twoSpaces * level) + "#-" + display(node) + " (cycle)"))
else {
val line = limitLine((twoSpaces * level) + (if (level == 0) "" else "+-") + display(node))
val cs = Vector(children(node): _*)
val childLines = cs map {
toAsciiLines(_, level + 1, parents + node)
}
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
}
(line, withBar)
}

val (line, withBar) = toAsciiLines(top, 0)
(line +: withBar).mkString("\n")
toAsciiLines(top, 0, Set.empty).mkString("\n")
}

def defaultColumnSize: Int = {
val termWidth = Terminal.console.getWidth
if (termWidth > 20) termWidth - 8
else 80 // ignore termWidth
}
}
95 changes: 95 additions & 0 deletions main/src/main/scala/sbt/internal/graph/DependencyGraphKeys.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/

package sbt
package internal
package graph

import java.io.File
import java.net.URI
import sbt.BuildSyntax._
import sbt.librarymanagement.{ ModuleID, UpdateReport }

trait DependencyGraphKeys {
val asString = taskKey[String]("Provides the string value for the task it is scoped for")
// val printToConsole = TaskKey[Unit]("printToConsole", "Prints the tasks value to the console")
val toFile = inputKey[File]("Writes the task value to the given file")

val dependencyTreeIncludeScalaLibrary = settingKey[Boolean](
"Specifies if scala dependency should be included in dependencyTree output"
)

val dependencyGraphMLFile =
settingKey[File]("The location the graphml file should be generated at")
val dependencyGraphML =
taskKey[File]("Creates a graphml file containing the dependency-graph for a project")
val dependencyDotFile =
settingKey[File]("The location the dot file should be generated at")
val dependencyDotNodeLabel = settingKey[(String, String, String) => String](
"Returns a formated string of a dependency. Takes organization, name and version as parameters"
)
val dependencyDotHeader = settingKey[String](
"The header of the dot file. (e.g. to set your preferred node shapes)"
)
val dependencyDot = taskKey[File](
"Creates a dot file containing the dependency-graph for a project"
)
val dependencyDotString = taskKey[String](
"Creates a String containing the dependency-graph for a project in dot format"
)
val dependencyBrowseGraphTarget = settingKey[File](
"The location dependency browse graph files should be put."
)
val dependencyBrowseGraphHTML = taskKey[URI](
"Creates an HTML page that can be used to view the graph."
)
val dependencyBrowseGraph = taskKey[URI](
"Opens an HTML page that can be used to view the graph."
)
val dependencyBrowseTreeTarget = settingKey[File](
"The location dependency browse tree files should be put."
)
val dependencyBrowseTreeHTML = taskKey[URI](
"Creates an HTML page that can be used to view the dependency tree"
)
val dependencyBrowseTree = taskKey[URI](
"Opens an HTML page that can be used to view the dependency tree"
)
val moduleGraph = taskKey[ModuleGraph]("The dependency graph for a project")
val moduleGraphIvyReport = taskKey[ModuleGraph](
"The dependency graph for a project as generated from an Ivy Report XML"
)
val moduleGraphSbt = taskKey[ModuleGraph](
"The dependency graph for a project as generated from SBT data structures."
)
val dependencyGraph = inputKey[Unit]("Prints the ascii graph to the console")
val dependencyTree = taskKey[Unit]("Prints an ascii tree of all the dependencies to the console")
val dependencyList =
taskKey[Unit]("Prints a list of all dependencies to the console")
val dependencyStats =
taskKey[Unit]("Prints statistics for all dependencies to the console")
val ivyReportFunction = taskKey[String => File](
"A function which returns the file containing the ivy report from the ivy cache for a given configuration"
)
val ivyReport = taskKey[File](
"A task which returns the location of the ivy report file for a given configuration (default `compile`)."
)
val dependencyLicenseInfo = taskKey[Unit](
"Aggregates and shows information about the licenses of dependencies"
)

// internal
private[sbt] val ignoreMissingUpdate =
TaskKey[UpdateReport]("dependencyUpdate", "sbt-dependency-graph version of update")
private[sbt] val moduleGraphStore =
TaskKey[ModuleGraph]("module-graph-store", "The stored module-graph from the last run")
val whatDependsOn =
InputKey[String]("what-depends-on", "Shows information about what depends on the given module")
private[sbt] val crossProjectId = SettingKey[ModuleID]("dependency-graph-cross-project-id")
}

object DependencyGraphKeys extends DependencyGraphKeys
58 changes: 58 additions & 0 deletions main/src/main/scala/sbt/internal/graph/GraphTransformations.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/

package sbt
package internal
package graph

object GraphTransformations {
def reverseGraphStartingAt(graph: ModuleGraph, root: GraphModuleId): ModuleGraph = {
val deps = graph.reverseDependencyMap

def visit(
module: GraphModuleId,
visited: Set[GraphModuleId]
): Seq[(GraphModuleId, GraphModuleId)] =
if (visited(module))
Nil
else
deps.get(module) match {
case Some(deps) =>
deps.flatMap { to =>
(module, to.id) +: visit(to.id, visited + module)
}
case None => Nil
}

val edges = visit(root, Set.empty)
val nodes =
edges
.foldLeft(Set.empty[GraphModuleId])((set, edge) => set + edge._1 + edge._2)
.map(graph.module)
ModuleGraph(nodes.toSeq, edges)
}

def ignoreScalaLibrary(scalaVersion: String, graph: ModuleGraph): ModuleGraph = {
def isScalaLibrary(m: Module) = isScalaLibraryId(m.id)
def isScalaLibraryId(id: GraphModuleId) =
id.organization == "org.scala-lang" && id.name == "scala-library"

def dependsOnScalaLibrary(m: Module): Boolean =
graph.dependencyMap(m.id).exists(isScalaLibrary)

def addScalaLibraryAnnotation(m: Module): Module = {
if (dependsOnScalaLibrary(m))
m.copy(extraInfo = m.extraInfo + " [S]")
else
m
}

val newNodes = graph.nodes.map(addScalaLibraryAnnotation).filterNot(isScalaLibrary)
val newEdges = graph.edges.filterNot(e => isScalaLibraryId(e._2))
ModuleGraph(newNodes, newEdges)
}
}
67 changes: 67 additions & 0 deletions main/src/main/scala/sbt/internal/graph/backend/IvyReport.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/

package sbt
package internal
package graph
package backend

import scala.xml.{ NodeSeq, Document, Node }
import scala.xml.parsing.ConstructingParser

object IvyReport {
def fromReportFile(ivyReportFile: String): ModuleGraph =
fromReportXML(loadXML(ivyReportFile))

def fromReportXML(doc: Document): ModuleGraph = {
def edgesForModule(id: GraphModuleId, revision: NodeSeq): Seq[Edge] =
for {
caller revision \ "caller"
callerModule = moduleIdFromElement(caller, caller.attribute("callerrev").get.text)
} yield (moduleIdFromElement(caller, caller.attribute("callerrev").get.text), id)

val moduleEdges: Seq[(Module, Seq[Edge])] = for {
mod doc \ "dependencies" \ "module"
revision mod \ "revision"
rev = revision.attribute("name").get.text
moduleId = moduleIdFromElement(mod, rev)
module = Module(
moduleId,
(revision \ "license").headOption.flatMap(_.attribute("name")).map(_.text),
evictedByVersion =
(revision \ "evicted-by").headOption.flatMap(_.attribute("rev").map(_.text)),
error = revision.attribute("error").map(_.text)
)
} yield (module, edgesForModule(moduleId, revision))

val (nodes, edges) = moduleEdges.unzip

val info = (doc \ "info").head
def infoAttr(name: String): String =
info
.attribute(name)
.getOrElse(throw new IllegalArgumentException("Missing attribute " + name))
.text
val rootModule = Module(
GraphModuleId(infoAttr("organization"), infoAttr("module"), infoAttr("revision"))
)

ModuleGraph(rootModule +: nodes, edges.flatten)
}

private def moduleIdFromElement(element: Node, version: String): GraphModuleId =
GraphModuleId(
element.attribute("organization").get.text,
element.attribute("name").get.text,
version
)

private def loadXML(ivyReportFile: String) =
ConstructingParser
.fromSource(scala.io.Source.fromFile(ivyReportFile), preserveWS = false)
.document()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/

package sbt
package internal
package graph
package backend

import scala.language.implicitConversions
import scala.language.reflectiveCalls
import sbt.librarymanagement.{ ModuleID, ModuleReport, ConfigurationReport }

object SbtUpdateReport {
type OrganizationArtifactReport = {
def modules: Seq[ModuleReport]
}

def fromConfigurationReport(report: ConfigurationReport, rootInfo: ModuleID): ModuleGraph = {
implicit def id(sbtId: ModuleID): GraphModuleId =
GraphModuleId(sbtId.organization, sbtId.name, sbtId.revision)

def moduleEdges(orgArt: OrganizationArtifactReport): Seq[(Module, Seq[Edge])] = {
val chosenVersion = orgArt.modules.find(!_.evicted).map(_.module.revision)
orgArt.modules.map(moduleEdge(chosenVersion))
}

def moduleEdge(chosenVersion: Option[String])(report: ModuleReport): (Module, Seq[Edge]) = {
val evictedByVersion = if (report.evicted) chosenVersion else None
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))
)
}

val (nodes, edges) = report.details.flatMap(moduleEdges).unzip
val root = Module(rootInfo)

ModuleGraph(root +: nodes, edges.flatten)
}
}
Loading

0 comments on commit 17b7545

Please sign in to comment.