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: 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="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 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/*" - SBT_CMD="repoOverrideTest:scripted dependency-management/*; scripted source-dependencies/* project/*"
matrix: 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. // Implementation and support code for defining actions.
lazy val actionsProj = (project in file("main-actions")) lazy val actionsProj = (project in file("main-actions"))
.dependsOn( .dependsOn(
@ -1361,6 +1370,7 @@ def allProjects =
scriptedSbtReduxProj, scriptedSbtReduxProj,
scriptedSbtOldProj, scriptedSbtOldProj,
scriptedPluginProj, scriptedPluginProj,
dependencyTreeProj,
protocolProj, protocolProj,
actionsProj, actionsProj,
commandProj, 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)) 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. * Adds Maven resolver plugin.
*/ */

View File

@ -52,7 +52,7 @@ object PluginDiscovery {
"sbt.plugins.SemanticdbPlugin" -> sbt.plugins.SemanticdbPlugin, "sbt.plugins.SemanticdbPlugin" -> sbt.plugins.SemanticdbPlugin,
"sbt.plugins.JUnitXmlReportPlugin" -> sbt.plugins.JUnitXmlReportPlugin, "sbt.plugins.JUnitXmlReportPlugin" -> sbt.plugins.JUnitXmlReportPlugin,
"sbt.plugins.Giter8TemplatePlugin" -> sbt.plugins.Giter8TemplatePlugin, "sbt.plugins.Giter8TemplatePlugin" -> sbt.plugins.Giter8TemplatePlugin,
"sbt.plugins.DependencyGraphPlugin" -> sbt.plugins.DependencyGraphPlugin, "sbt.plugins.MiniDependencyTreePlugin" -> sbt.plugins.MiniDependencyTreePlugin,
) )
val detectedAutoPlugins = discover[AutoPlugin](AutoPlugins) val detectedAutoPlugins = discover[AutoPlugin](AutoPlugins)
val allAutoPlugins = (defaultAutoPlugins ++ detectedAutoPlugins.modules) map { val allAutoPlugins = (defaultAutoPlugins ++ detectedAutoPlugins.modules) map {

View File

@ -6,23 +6,36 @@
*/ */
package sbt package sbt
package internal package plugins
package graph
import java.io.File import java.io.File
import java.net.URI import java.net.URI
import sbt.internal.graph._
import sbt.BuildSyntax._ import sbt.BuildSyntax._
import sbt.librarymanagement.{ ModuleID, UpdateReport } 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 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 printToConsole = TaskKey[Unit]("printToConsole", "Prints the tasks value to the console")
val toFile = inputKey[File]("Writes the task value to the given file") val toFile = inputKey[File]("Writes the task value to the given file")
val dependencyTreeIncludeScalaLibrary = settingKey[Boolean]( // internal
"Specifies if scala dependency should be included in dependencyTree output" 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 = val dependencyGraphMLFile =
settingKey[File]("The location the graphml file should be generated at") settingKey[File]("The location the graphml file should be generated at")
val dependencyGraphML = val dependencyGraphML =
@ -59,37 +72,15 @@ trait DependencyGraphKeys {
val dependencyBrowseTree = taskKey[URI]( val dependencyBrowseTree = taskKey[URI](
"Opens an HTML page that can be used to view the dependency tree" "Opens an HTML page that can be used to view the dependency tree"
) )
val moduleGraph = taskKey[ModuleGraph]("The dependency graph for a project") val dependencyTreeModuleGraph = 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 = val dependencyList =
taskKey[Unit]("Prints a list of all dependencies to the console") taskKey[Unit]("Prints a list of all dependencies to the console")
val dependencyStats = val dependencyStats =
taskKey[Unit]("Prints statistics for all dependencies to the console") 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]( val dependencyLicenseInfo = taskKey[Unit](
"Aggregates and shows information about the licenses of dependencies" "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.Def._
import sbt.Keys._ import sbt.Keys._
import sbt.SlashSyntax0._ import sbt.SlashSyntax0._
import sbt.PluginTrigger.AllRequirements
import sbt.Project._ import sbt.Project._
import sbt.internal.graph._ 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.graph.rendering.{ DagreHTML, TreeView }
import sbt.internal.librarymanagement._ import sbt.internal.librarymanagement._
import sbt.internal.util.complete.{ Parser, Parsers } import sbt.internal.util.complete.{ Parser, Parsers }
import sbt.io.IO import sbt.io.IO
import sbt.io.syntax._ import sbt.io.syntax._
import sbt.librarymanagement._ 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 sjsonnew.BasicJsonProtocol._
import MiniDependencyTreeKeys._
import DependencyTreeKeys._
object autoImport extends DependencyGraphKeys /**
* Core settings needed for any graphing tasks.
import autoImport._ */
override def trigger: PluginTrigger = AllRequirements def coreSettings =
override def globalSettings: Seq[Def.Setting[_]] = Seq(
dependencyTreeIncludeScalaLibrary := false
)
override def projectSettings: Seq[Def.Setting[_]] = graphSettings
def graphSettings = baseSettings ++ reportSettings
def baseSettings =
Seq( Seq(
ivyReportFunction := ivyReportFunctionTask.value,
// disable the cached resolution engine (exposing a scoped `ivyModule` used directly by `updateTask`), as it // 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 // generates artificial module descriptors which are internal to sbt, making it hard to reconstruct the
// dependency tree // dependency tree
@ -73,50 +56,32 @@ object DependencyGraphPlugin extends AutoPlugin {
}, },
) )
def reportSettings = /**
Seq(Compile, Test, IntegrationTest, Runtime, Provided, Optional).flatMap(ivyReportForConfig) * MiniDependencyTreePlugin includes these settings for Compile and Test scopes
* to provide dependencyTree task.
val renderingAlternatives: Seq[(TaskKey[Unit], ModuleGraph => String)] = */
lazy val baseBasicReportingSettings: Seq[Def.Setting[_]] =
Seq( Seq(
dependencyTree -> rendering.AsciiTree.asciiTree _,
dependencyList -> rendering.FlatList.render(_.id.idString),
dependencyStats -> rendering.Statistics.renderModuleStatsList _,
dependencyLicenseInfo -> rendering.LicenseInfo.render _
)
def ivyReportForConfig(config: Configuration) =
inConfig(config)(
Seq(
ivyReport := {
Def
.task {
ivyReportFunction.value.apply(config.toString)
}
.dependsOn(ignoreMissingUpdate)
}.value,
crossProjectId := CrossVersion(scalaVersion.value, scalaBinaryVersion.value)( crossProjectId := CrossVersion(scalaVersion.value, scalaBinaryVersion.value)(
projectID.value projectID.value
), ),
moduleGraphSbt := dependencyTreeModuleGraph := {
ignoreMissingUpdate.value val sv = scalaVersion.value
val g = ignoreMissingUpdate.value
.configuration(configuration.value) .configuration(configuration.value)
.map(report => SbtUpdateReport.fromConfigurationReport(report, crossProjectId.value)) .map(report => SbtUpdateReport.fromConfigurationReport(report, crossProjectId.value))
.getOrElse(ModuleGraph.empty), .getOrElse(ModuleGraph.empty)
moduleGraphIvyReport := IvyReport.fromReportFile(absoluteReportPath(ivyReport.value)), if (dependencyTreeIncludeScalaLibrary.value) g
moduleGraph := { else GraphTransformations.ignoreScalaLibrary(sv, g)
sbtVersion.value match {
case Version(0, 13, x, _) if x >= 6 => moduleGraphSbt.value
case Version(1, _, _, _) => moduleGraphSbt.value
}
}, },
moduleGraph := { moduleGraphStore := (dependencyTreeModuleGraph storeAs moduleGraphStore triggeredBy dependencyTreeModuleGraph).value,
// FIXME: remove busywork ) ++ renderingTaskSettings(dependencyTree, rendering.AsciiTree.asciiTree _)
val sv = scalaVersion.value
val moduleGraph = DependencyGraphKeys.moduleGraph.value /**
if (dependencyTreeIncludeScalaLibrary.value) moduleGraph * This is the maximum strength settings for DependencyTreePlugin.
else GraphTransformations.ignoreScalaLibrary(sv, moduleGraph) */
}, lazy val baseFullReportingSettings: Seq[Def.Setting[_]] =
moduleGraphStore := (moduleGraph storeAs moduleGraphStore triggeredBy moduleGraph).value, Seq(
// browse // browse
dependencyBrowseGraphTarget := { target.value / "browse-dependency-graph" }, dependencyBrowseGraphTarget := { target.value / "browse-dependency-graph" },
dependencyBrowseGraphHTML := browseGraphHTMLTask.value, dependencyBrowseGraphHTML := browseGraphHTMLTask.value,
@ -125,9 +90,12 @@ object DependencyGraphPlugin extends AutoPlugin {
dependencyBrowseTreeHTML := browseTreeHTMLTask.value, dependencyBrowseTreeHTML := browseTreeHTMLTask.value,
dependencyBrowseTree := openBrowser(dependencyBrowseTreeHTML).value, dependencyBrowseTree := openBrowser(dependencyBrowseTreeHTML).value,
// dot support // dot support
dependencyDotFile := { target.value / "dependencies-%s.dot".format(config.toString) }, dependencyDotFile := {
val config = configuration.value
target.value / "dependencies-%s.dot".format(config.toString)
},
dependencyDot / asString := rendering.DOT.dotGraph( dependencyDot / asString := rendering.DOT.dotGraph(
moduleGraph.value, dependencyTreeModuleGraph.value,
dependencyDotHeader.value, dependencyDotHeader.value,
dependencyDotNodeLabel.value, dependencyDotNodeLabel.value,
rendering.DOT.AngleBrackets rendering.DOT.AngleBrackets
@ -144,12 +112,13 @@ object DependencyGraphPlugin extends AutoPlugin {
}, },
// GraphML support // GraphML support
dependencyGraphMLFile := { dependencyGraphMLFile := {
val config = configuration.value
target.value / "dependencies-%s.graphml".format(config.toString) target.value / "dependencies-%s.graphml".format(config.toString)
}, },
dependencyGraphML := dependencyGraphMLTask.value, dependencyGraphML := dependencyGraphMLTask.value,
whatDependsOn := { whatDependsOn := {
val ArtifactPattern(org, name, versionFilter) = artifactPatternParser.parsed val ArtifactPattern(org, name, versionFilter) = artifactPatternParser.parsed
val graph = moduleGraph.value val graph = dependencyTreeModuleGraph.value
val modules = val modules =
versionFilter match { versionFilter match {
case Some(version) => GraphModuleId(org, name, version) :: Nil case Some(version) => GraphModuleId(org, name, version) :: Nil
@ -169,6 +138,12 @@ object DependencyGraphPlugin extends AutoPlugin {
}, },
) ++ ) ++
renderingAlternatives.flatMap((renderingTaskSettings _).tupled) 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[_]] = def renderingTaskSettings(key: TaskKey[Unit], renderer: ModuleGraph => String): Seq[Setting[_]] =
@ -178,37 +153,27 @@ object DependencyGraphPlugin extends AutoPlugin {
val str = (key / asString).value val str = (key / asString).value
s.log.info(str) s.log.info(str)
}, },
key / asString := renderer(moduleGraph.value), key / asString := renderer(dependencyTreeModuleGraph.value),
key / toFile := { key / toFile := {
val (targetFile, force) = targetFileAndForceParser.parsed val (targetFile, force) = targetFileAndForceParser.parsed
writeToFile(key.key.label, (asString in key).value, targetFile, force, streams.value) 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 dependencyGraphMLTask =
Def.task { Def.task {
val resultFile = dependencyGraphMLFile.value 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) streams.value.log.info("Wrote dependency graph to '%s'" format resultFile)
resultFile resultFile
} }
def browseGraphHTMLTask = def browseGraphHTMLTask =
Def.task { Def.task {
val graph = dependencyTreeModuleGraph.value
val dotGraph = rendering.DOT.dotGraph( val dotGraph = rendering.DOT.dotGraph(
moduleGraph.value, graph,
dependencyDotHeader.value, dependencyDotHeader.value,
dependencyDotNodeLabel.value, dependencyDotNodeLabel.value,
rendering.DOT.LabelTypeHtml rendering.DOT.LabelTypeHtml
@ -220,7 +185,8 @@ object DependencyGraphPlugin extends AutoPlugin {
def browseTreeHTMLTask = def browseTreeHTMLTask =
Def.task { Def.task {
val renderedTree = TreeView.createJson(moduleGraph.value) val graph = dependencyTreeModuleGraph.value
val renderedTree = TreeView.createJson(graph)
val link = TreeView.createLink(renderedTree, target.value) val link = TreeView.createLink(renderedTree, target.value)
streams.value.log.info(s"HTML tree written to $link") streams.value.log.info(s"HTML tree written to $link")
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) updateOptions := updateOptions.value.withCachedResolution(true)
TaskKey[Unit]("check") := { TaskKey[Unit]("check") := {
val report = (Test / ivyReport).value val report = (Test / updateFull).value
val graph = (Test / dependencyTree / asString).value val graph = (Test / dependencyTree / asString).value
def sanitize(str: String): String = str.split('\n').drop(1).mkString("\n") 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 # to initialize parser with deps
> compile:moduleGraph > Compile/dependencyTreeModuleGraph
> check > check