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,102 +56,94 @@ 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 _, crossProjectId := CrossVersion(scalaVersion.value, scalaBinaryVersion.value)(
dependencyList -> rendering.FlatList.render(_.id.idString), projectID.value
dependencyStats -> rendering.Statistics.renderModuleStatsList _, ),
dependencyLicenseInfo -> rendering.LicenseInfo.render _ 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)( * This is the maximum strength settings for DependencyTreePlugin.
Seq( */
ivyReport := { lazy val baseFullReportingSettings: Seq[Def.Setting[_]] =
Def Seq(
.task { // browse
ivyReportFunction.value.apply(config.toString) dependencyBrowseGraphTarget := { target.value / "browse-dependency-graph" },
} dependencyBrowseGraphHTML := browseGraphHTMLTask.value,
.dependsOn(ignoreMissingUpdate) dependencyBrowseGraph := openBrowser(dependencyBrowseGraphHTML).value,
}.value, dependencyBrowseTreeTarget := { target.value / "browse-dependency-tree" },
crossProjectId := CrossVersion(scalaVersion.value, scalaBinaryVersion.value)( dependencyBrowseTreeHTML := browseTreeHTMLTask.value,
projectID.value dependencyBrowseTree := openBrowser(dependencyBrowseTreeHTML).value,
), // dot support
moduleGraphSbt := dependencyDotFile := {
ignoreMissingUpdate.value val config = configuration.value
.configuration(configuration.value) target.value / "dependencies-%s.dot".format(config.toString)
.map(report => SbtUpdateReport.fromConfigurationReport(report, crossProjectId.value)) },
.getOrElse(ModuleGraph.empty), dependencyDot / asString := rendering.DOT.dotGraph(
moduleGraphIvyReport := IvyReport.fromReportFile(absoluteReportPath(ivyReport.value)), dependencyTreeModuleGraph.value,
moduleGraph := { dependencyDotHeader.value,
sbtVersion.value match { dependencyDotNodeLabel.value,
case Version(0, 13, x, _) if x >= 6 => moduleGraphSbt.value rendering.DOT.AngleBrackets
case Version(1, _, _, _) => moduleGraphSbt.value ),
} dependencyDot := writeToFile(dependencyDot / asString, dependencyDotFile).value,
}, dependencyDotHeader :=
moduleGraph := { """|digraph "dependency-graph" {
// 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" {
| graph[rankdir="LR"] | graph[rankdir="LR"]
| edge [ | edge [
| arrowtail="none" | arrowtail="none"
| ]""".stripMargin, | ]""".stripMargin,
dependencyDotNodeLabel := { (organization: String, name: String, version: String) => dependencyDotNodeLabel := { (organization: String, name: String, version: String) =>
"""%s<BR/><B>%s</B><BR/>%s""".format(organization, name, version) """%s<BR/><B>%s</B><BR/>%s""".format(organization, name, version)
}, },
// GraphML support // GraphML support
dependencyGraphMLFile := { dependencyGraphMLFile := {
target.value / "dependencies-%s.graphml".format(config.toString) val config = configuration.value
}, target.value / "dependencies-%s.graphml".format(config.toString)
dependencyGraphML := dependencyGraphMLTask.value, },
whatDependsOn := { dependencyGraphML := dependencyGraphMLTask.value,
val ArtifactPattern(org, name, versionFilter) = artifactPatternParser.parsed whatDependsOn := {
val graph = moduleGraph.value val ArtifactPattern(org, name, versionFilter) = artifactPatternParser.parsed
val modules = val graph = dependencyTreeModuleGraph.value
versionFilter match { val modules =
case Some(version) => GraphModuleId(org, name, version) :: Nil versionFilter match {
case None => case Some(version) => GraphModuleId(org, name, version) :: Nil
graph.nodes.filter(m => m.id.organization == org && m.id.name == name).map(_.id) 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 = .mkString("\n")
modules
.map { module =>
rendering.AsciiTree
.asciiTree(GraphTransformations.reverseGraphStartingAt(graph, module))
}
.mkString("\n")
streams.value.log.info(output) streams.value.log.info(output)
output output
}, },
) ++ ) ++
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