Split to MiniDependencyTreePlugin

Ref https://github.com/sbt/sbt/pull/5880

This split the dependency-graph plugin into MiniDependencyTreePlugin and DependencyTreePlugin.
This commit is contained in:
Eugene Yokota 2020-09-22 21:21:01 -04:00
parent 54747b88bb
commit 4fe2f8eff1
14 changed files with 198 additions and 164 deletions

View File

@ -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-graph/* dependency-management/* plugins/* project-load/* java/* run/* nio/*"
- SBT_CMD="dependencyTreeProj/publishLocal; scripted dependency-graph/* dependency-management/* plugins/* project-load/* java/* run/* nio/*"
- SBT_CMD="repoOverrideTest:scripted dependency-management/*; scripted source-dependencies/* project/*"
matrix:

View File

@ -648,6 +648,15 @@ lazy val scriptedPluginProj = (project in file("scripted-plugin"))
),
)
lazy val dependencyTreeProj = (project in file("dependency-tree"))
.dependsOn(sbtProj)
.settings(
sbtPlugin := true,
baseSettings,
name := "sbt-dependency-tree",
// mimaSettings,
)
// Implementation and support code for defining actions.
lazy val actionsProj = (project in file("main-actions"))
.dependsOn(
@ -1361,6 +1370,7 @@ def allProjects =
scriptedSbtReduxProj,
scriptedSbtOldProj,
scriptedPluginProj,
dependencyTreeProj,
protocolProj,
actionsProj,
commandProj,

View File

@ -0,0 +1,26 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package plugins
object DependencyTreePlugin extends AutoPlugin {
object autoImport extends DependencyTreeKeys
override def trigger = AllRequirements
override def requires = MiniDependencyTreePlugin
val configurations = Vector(Compile, Test, IntegrationTest, Runtime, Provided, Optional)
// MiniDependencyTreePlugin provides baseBasicReportingSettings for Compile and Test
override def projectSettings: Seq[Def.Setting[_]] =
((configurations diff Vector(Compile, Test)) flatMap { config =>
inConfig(config)(DependencyTreeSettings.baseBasicReportingSettings)
}) ++
(configurations flatMap { config =>
inConfig(config)(DependencyTreeSettings.baseFullReportingSettings)
})
}

View File

@ -3983,6 +3983,16 @@ trait BuildExtra extends BuildCommon with DefExtra {
Seq(compose(onLoad, add), compose(onUnload, remove))
}
/**
* Adds Maven resolver plugin.
*/
def addDependencyTreePlugin: Setting[Seq[ModuleID]] =
libraryDependencies += sbtPluginExtra(
ModuleID("org.scala-sbt", "sbt-dependency-tree", sbtVersion.value),
sbtBinaryVersion.value,
scalaBinaryVersion.value
)
/**
* Adds Maven resolver plugin.
*/

View File

@ -52,7 +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,
"sbt.plugins.MiniDependencyTreePlugin" -> sbt.plugins.MiniDependencyTreePlugin,
)
val detectedAutoPlugins = discover[AutoPlugin](AutoPlugins)
val allAutoPlugins = (defaultAutoPlugins ++ detectedAutoPlugins.modules) map {

View File

@ -6,23 +6,36 @@
*/
package sbt
package internal
package graph
package plugins
import java.io.File
import java.net.URI
import sbt.internal.graph._
import sbt.BuildSyntax._
import sbt.librarymanagement.{ ModuleID, UpdateReport }
trait DependencyGraphKeys {
trait MiniDependencyTreeKeys {
val dependencyTreeIncludeScalaLibrary = settingKey[Boolean](
"Specifies if scala dependency should be included in dependencyTree output"
)
val dependencyTree = taskKey[Unit]("Prints an ascii tree of all the dependencies to the console")
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"
)
// 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 MiniDependencyTreeKeys extends MiniDependencyTreeKeys
abstract class DependencyTreeKeys {
val dependencyGraphMLFile =
settingKey[File]("The location the graphml file should be generated at")
val dependencyGraphML =
@ -59,37 +72,15 @@ trait DependencyGraphKeys {
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 dependencyTreeModuleGraph = taskKey[ModuleGraph]("The dependency graph for a project")
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
object DependencyTreeKeys extends DependencyTreeKeys

View File

@ -13,43 +13,26 @@ import java.io.File
import sbt.Def._
import sbt.Keys._
import sbt.SlashSyntax0._
import sbt.PluginTrigger.AllRequirements
import sbt.Project._
import sbt.internal.graph._
import sbt.internal.graph.backend.{ IvyReport, SbtUpdateReport }
import sbt.internal.graph.backend.SbtUpdateReport
import sbt.internal.graph.rendering.{ DagreHTML, TreeView }
import sbt.internal.librarymanagement._
import sbt.internal.util.complete.{ Parser, Parsers }
import sbt.io.IO
import sbt.io.syntax._
import sbt.librarymanagement._
import sbt.librarymanagement.ivy.InlineIvyConfiguration
import sbt.librarymanagement.Configurations.{
Compile,
IntegrationTest,
Optional,
Provided,
Runtime,
Test
}
// import Keys._
object DependencyGraphPlugin extends AutoPlugin {
object DependencyTreeSettings {
import sjsonnew.BasicJsonProtocol._
import MiniDependencyTreeKeys._
import DependencyTreeKeys._
object autoImport extends DependencyGraphKeys
import autoImport._
override def trigger: PluginTrigger = AllRequirements
override def globalSettings: Seq[Def.Setting[_]] = Seq(
dependencyTreeIncludeScalaLibrary := false
)
override def projectSettings: Seq[Def.Setting[_]] = graphSettings
def graphSettings = baseSettings ++ reportSettings
def baseSettings =
/**
* Core settings needed for any graphing tasks.
*/
def coreSettings =
Seq(
ivyReportFunction := ivyReportFunctionTask.value,
// disable the cached resolution engine (exposing a scoped `ivyModule` used directly by `updateTask`), as it
// generates artificial module descriptors which are internal to sbt, making it hard to reconstruct the
// dependency tree
@ -73,102 +56,94 @@ object DependencyGraphPlugin extends AutoPlugin {
},
)
def reportSettings =
Seq(Compile, Test, IntegrationTest, Runtime, Provided, Optional).flatMap(ivyReportForConfig)
val renderingAlternatives: Seq[(TaskKey[Unit], ModuleGraph => String)] =
/**
* MiniDependencyTreePlugin includes these settings for Compile and Test scopes
* to provide dependencyTree task.
*/
lazy val baseBasicReportingSettings: Seq[Def.Setting[_]] =
Seq(
dependencyTree -> rendering.AsciiTree.asciiTree _,
dependencyList -> rendering.FlatList.render(_.id.idString),
dependencyStats -> rendering.Statistics.renderModuleStatsList _,
dependencyLicenseInfo -> rendering.LicenseInfo.render _
)
crossProjectId := CrossVersion(scalaVersion.value, scalaBinaryVersion.value)(
projectID.value
),
dependencyTreeModuleGraph := {
val sv = scalaVersion.value
val g = ignoreMissingUpdate.value
.configuration(configuration.value)
.map(report => SbtUpdateReport.fromConfigurationReport(report, crossProjectId.value))
.getOrElse(ModuleGraph.empty)
if (dependencyTreeIncludeScalaLibrary.value) g
else GraphTransformations.ignoreScalaLibrary(sv, g)
},
moduleGraphStore := (dependencyTreeModuleGraph storeAs moduleGraphStore triggeredBy dependencyTreeModuleGraph).value,
) ++ renderingTaskSettings(dependencyTree, rendering.AsciiTree.asciiTree _)
def ivyReportForConfig(config: Configuration) =
inConfig(config)(
Seq(
ivyReport := {
Def
.task {
ivyReportFunction.value.apply(config.toString)
}
.dependsOn(ignoreMissingUpdate)
}.value,
crossProjectId := CrossVersion(scalaVersion.value, scalaBinaryVersion.value)(
projectID.value
),
moduleGraphSbt :=
ignoreMissingUpdate.value
.configuration(configuration.value)
.map(report => SbtUpdateReport.fromConfigurationReport(report, crossProjectId.value))
.getOrElse(ModuleGraph.empty),
moduleGraphIvyReport := IvyReport.fromReportFile(absoluteReportPath(ivyReport.value)),
moduleGraph := {
sbtVersion.value match {
case Version(0, 13, x, _) if x >= 6 => moduleGraphSbt.value
case Version(1, _, _, _) => moduleGraphSbt.value
}
},
moduleGraph := {
// FIXME: remove busywork
val sv = scalaVersion.value
val moduleGraph = DependencyGraphKeys.moduleGraph.value
if (dependencyTreeIncludeScalaLibrary.value) moduleGraph
else GraphTransformations.ignoreScalaLibrary(sv, moduleGraph)
},
moduleGraphStore := (moduleGraph storeAs moduleGraphStore triggeredBy moduleGraph).value,
// browse
dependencyBrowseGraphTarget := { target.value / "browse-dependency-graph" },
dependencyBrowseGraphHTML := browseGraphHTMLTask.value,
dependencyBrowseGraph := openBrowser(dependencyBrowseGraphHTML).value,
dependencyBrowseTreeTarget := { target.value / "browse-dependency-tree" },
dependencyBrowseTreeHTML := browseTreeHTMLTask.value,
dependencyBrowseTree := openBrowser(dependencyBrowseTreeHTML).value,
// dot support
dependencyDotFile := { target.value / "dependencies-%s.dot".format(config.toString) },
dependencyDot / asString := rendering.DOT.dotGraph(
moduleGraph.value,
dependencyDotHeader.value,
dependencyDotNodeLabel.value,
rendering.DOT.AngleBrackets
),
dependencyDot := writeToFile(dependencyDot / asString, dependencyDotFile).value,
dependencyDotHeader :=
"""|digraph "dependency-graph" {
/**
* This is the maximum strength settings for DependencyTreePlugin.
*/
lazy val baseFullReportingSettings: Seq[Def.Setting[_]] =
Seq(
// browse
dependencyBrowseGraphTarget := { target.value / "browse-dependency-graph" },
dependencyBrowseGraphHTML := browseGraphHTMLTask.value,
dependencyBrowseGraph := openBrowser(dependencyBrowseGraphHTML).value,
dependencyBrowseTreeTarget := { target.value / "browse-dependency-tree" },
dependencyBrowseTreeHTML := browseTreeHTMLTask.value,
dependencyBrowseTree := openBrowser(dependencyBrowseTreeHTML).value,
// dot support
dependencyDotFile := {
val config = configuration.value
target.value / "dependencies-%s.dot".format(config.toString)
},
dependencyDot / asString := rendering.DOT.dotGraph(
dependencyTreeModuleGraph.value,
dependencyDotHeader.value,
dependencyDotNodeLabel.value,
rendering.DOT.AngleBrackets
),
dependencyDot := writeToFile(dependencyDot / asString, dependencyDotFile).value,
dependencyDotHeader :=
"""|digraph "dependency-graph" {
| graph[rankdir="LR"]
| edge [
| arrowtail="none"
| ]""".stripMargin,
dependencyDotNodeLabel := { (organization: String, name: String, version: String) =>
"""%s<BR/><B>%s</B><BR/>%s""".format(organization, name, version)
},
// GraphML support
dependencyGraphMLFile := {
target.value / "dependencies-%s.graphml".format(config.toString)
},
dependencyGraphML := dependencyGraphMLTask.value,
whatDependsOn := {
val ArtifactPattern(org, name, versionFilter) = artifactPatternParser.parsed
val graph = moduleGraph.value
val modules =
versionFilter match {
case Some(version) => GraphModuleId(org, name, version) :: Nil
case None =>
graph.nodes.filter(m => m.id.organization == org && m.id.name == name).map(_.id)
dependencyDotNodeLabel := { (organization: String, name: String, version: String) =>
"""%s<BR/><B>%s</B><BR/>%s""".format(organization, name, version)
},
// GraphML support
dependencyGraphMLFile := {
val config = configuration.value
target.value / "dependencies-%s.graphml".format(config.toString)
},
dependencyGraphML := dependencyGraphMLTask.value,
whatDependsOn := {
val ArtifactPattern(org, name, versionFilter) = artifactPatternParser.parsed
val graph = dependencyTreeModuleGraph.value
val modules =
versionFilter match {
case Some(version) => GraphModuleId(org, name, version) :: Nil
case None =>
graph.nodes.filter(m => m.id.organization == org && m.id.name == name).map(_.id)
}
val output =
modules
.map { module =>
rendering.AsciiTree
.asciiTree(GraphTransformations.reverseGraphStartingAt(graph, module))
}
val output =
modules
.map { module =>
rendering.AsciiTree
.asciiTree(GraphTransformations.reverseGraphStartingAt(graph, module))
}
.mkString("\n")
.mkString("\n")
streams.value.log.info(output)
output
},
) ++
renderingAlternatives.flatMap((renderingTaskSettings _).tupled)
streams.value.log.info(output)
output
},
) ++
renderingAlternatives.flatMap((renderingTaskSettings _).tupled)
def renderingAlternatives: Seq[(TaskKey[Unit], ModuleGraph => String)] =
Seq(
dependencyList -> rendering.FlatList.render(_.id.idString),
dependencyStats -> rendering.Statistics.renderModuleStatsList _,
dependencyLicenseInfo -> rendering.LicenseInfo.render _
)
def renderingTaskSettings(key: TaskKey[Unit], renderer: ModuleGraph => String): Seq[Setting[_]] =
@ -178,37 +153,27 @@ object DependencyGraphPlugin extends AutoPlugin {
val str = (key / asString).value
s.log.info(str)
},
key / asString := renderer(moduleGraph.value),
key / asString := renderer(dependencyTreeModuleGraph.value),
key / toFile := {
val (targetFile, force) = targetFileAndForceParser.parsed
writeToFile(key.key.label, (asString in key).value, targetFile, force, streams.value)
},
)
def ivyReportFunctionTask = Def.task {
val ivyConfig = Keys.ivyConfiguration.value.asInstanceOf[InlineIvyConfiguration]
val projectID = Keys.projectID.value
val ivyModule = Keys.ivyModule.value
(config: String) => {
val org = projectID.organization
val name = crossName(ivyModule)
new File(ivyConfig.resolutionCacheDir.get, s"reports/$org-$name-$config.xml")
}
}
def dependencyGraphMLTask =
Def.task {
val resultFile = dependencyGraphMLFile.value
rendering.GraphML.saveAsGraphML(moduleGraph.value, resultFile.getAbsolutePath)
val graph = dependencyTreeModuleGraph.value
rendering.GraphML.saveAsGraphML(graph, resultFile.getAbsolutePath)
streams.value.log.info("Wrote dependency graph to '%s'" format resultFile)
resultFile
}
def browseGraphHTMLTask =
Def.task {
val graph = dependencyTreeModuleGraph.value
val dotGraph = rendering.DOT.dotGraph(
moduleGraph.value,
graph,
dependencyDotHeader.value,
dependencyDotNodeLabel.value,
rendering.DOT.LabelTypeHtml
@ -220,7 +185,8 @@ object DependencyGraphPlugin extends AutoPlugin {
def browseTreeHTMLTask =
Def.task {
val renderedTree = TreeView.createJson(moduleGraph.value)
val graph = dependencyTreeModuleGraph.value
val renderedTree = TreeView.createJson(graph)
val link = TreeView.createLink(renderedTree, target.value)
streams.value.log.info(s"HTML tree written to $link")
link

View File

@ -0,0 +1,27 @@
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package plugins
import sbt.PluginTrigger.AllRequirements
import sbt.Project._
import sbt.librarymanagement.Configurations.{ Compile, Test }
object MiniDependencyTreePlugin extends AutoPlugin {
object autoImport extends MiniDependencyTreeKeys
import autoImport._
override def trigger: PluginTrigger = AllRequirements
override def globalSettings: Seq[Def.Setting[_]] = Seq(
dependencyTreeIncludeScalaLibrary := false
)
override def projectSettings: Seq[Def.Setting[_]] =
DependencyTreeSettings.coreSettings ++
inConfig(Compile)(DependencyTreeSettings.baseBasicReportingSettings) ++
inConfig(Test)(DependencyTreeSettings.baseBasicReportingSettings)
}

View File

@ -4,7 +4,7 @@ libraryDependencies += "org.slf4j" % "slf4j-api" % "1.7.28"
updateOptions := updateOptions.value.withCachedResolution(true)
TaskKey[Unit]("check") := {
val report = (Test / ivyReport).value
val report = (Test / updateFull).value
val graph = (Test / dependencyTree / asString).value
def sanitize(str: String): String = str.split('\n').drop(1).mkString("\n")

View File

@ -0,0 +1 @@
addDependencyTreePlugin

View File

@ -0,0 +1 @@
addDependencyTreePlugin

View File

@ -0,0 +1 @@
addDependencyTreePlugin

View File

@ -1,3 +1,3 @@
# to initialize parser with deps
> compile:moduleGraph
> Compile/dependencyTreeModuleGraph
> check