mirror of https://github.com/sbt/sbt.git
Merge pull request #274 from eed3si9n/inspecttree
"inspect tree <key>" command
This commit is contained in:
commit
2f52884df8
|
|
@ -142,6 +142,13 @@ object Command
|
|||
( (c & opOrIDSpaced(name)) ~ c.+) map { case (f, rem) => (f +: rem).mkString }
|
||||
}
|
||||
|
||||
sealed trait InspectOption
|
||||
object InspectOption
|
||||
{
|
||||
final case class Details(actual: Boolean) extends InspectOption
|
||||
case object DependencyTree extends InspectOption
|
||||
}
|
||||
|
||||
trait Help
|
||||
{
|
||||
def detail: Map[String, String]
|
||||
|
|
|
|||
|
|
@ -84,14 +84,17 @@ LastCommand + """ <key>
|
|||
See also '""" + LastGrepCommand + "'."
|
||||
|
||||
val InspectCommand = "inspect"
|
||||
val inspectBrief = (InspectCommand + " <key>", "Prints the value for 'key', the defining scope, delegates, related definitions, and dependencies.")
|
||||
val inspectBrief = (InspectCommand + " [tree] <key>", "Prints the value for 'key', the defining scope, delegates, related definitions, and dependencies.")
|
||||
val inspectDetailed =
|
||||
InspectCommand + """ <key>
|
||||
InspectCommand + """ [tree] <key>
|
||||
|
||||
For a plain setting, the value bound to the key argument is displayed using its toString method.
|
||||
Otherwise, the type of task ("Task" or "Input task") is displayed.
|
||||
|
||||
"Dependencies" shows the settings that this setting depends on.
|
||||
If 'tree' is specified, the bound value as well as the settings that this setting depends on
|
||||
(and their bound values) are displayed as a tree structure.
|
||||
|
||||
"Reverse dependencies" shows the settings that depend on this setting.
|
||||
|
||||
When a key is resolved to a value, it may not actually be defined in the requested scope.
|
||||
|
|
|
|||
|
|
@ -382,10 +382,16 @@ object BuiltinCommands
|
|||
val newSession = session.appendSettings( append map (a => (a, arg)))
|
||||
reapply(newSession, structure, s)
|
||||
}
|
||||
def inspect = Command(InspectCommand, inspectBrief, inspectDetailed)(inspectParser) { case (s,(actual,sk)) =>
|
||||
val detailString = Project.details(Project.structure(s), actual, sk.scope, sk.key)( Project.showContextKey(s) )
|
||||
logger(s).info(detailString)
|
||||
s
|
||||
def inspect = Command(InspectCommand, inspectBrief, inspectDetailed)(inspectParser) {
|
||||
case (s, (InspectOption.DependencyTree, sk)) =>
|
||||
val basedir = new File(Project.session(s).current.build)
|
||||
val treeString = Project.settingGraph(Project.structure(s), basedir, sk)( Project.showContextKey(s) ).dependsAscii
|
||||
logger(s).info(treeString)
|
||||
s
|
||||
case (s, (InspectOption.Details(actual), sk)) =>
|
||||
val detailString = Project.details(Project.structure(s), actual, sk.scope, sk.key)( Project.showContextKey(s) )
|
||||
logger(s).info(detailString)
|
||||
s
|
||||
}
|
||||
def lastGrep = Command(LastGrepCommand, lastGrepBrief, lastGrepDetailed)(lastGrepParser) {
|
||||
case (s, (pattern,Some(sk))) =>
|
||||
|
|
@ -401,7 +407,13 @@ object BuiltinCommands
|
|||
val ext = Project.extract(s)
|
||||
(ext.structure, Select(ext.currentRef), ext.showKey)
|
||||
}
|
||||
def inspectParser = (s: State) => token((Space ~> ("actual" ^^^ true)) ?? false) ~ spacedKeyParser(s)
|
||||
def inspectParser = (s: State) => spacedInspectOptionParser(s) ~ spacedKeyParser(s)
|
||||
val spacedInspectOptionParser: (State => Parser[InspectOption]) = (s: State) => {
|
||||
import InspectOption._
|
||||
val actual = "actual" ^^^ Details(true)
|
||||
val tree = "tree" ^^^ DependencyTree
|
||||
token(Space ~> (tree | actual)) ?? Details(false)
|
||||
}
|
||||
val spacedKeyParser = (s: State) => Act.requireSession(s, token(Space) ~> Act.scopedKeyParser(s))
|
||||
val optSpacedKeyParser = (s: State) => spacedKeyParser(s).?
|
||||
def lastGrepParser(s: State) = Act.requireSession(s, (token(Space) ~> token(NotSpace, "<pattern>")) ~ optSpacedKeyParser(s))
|
||||
|
|
|
|||
|
|
@ -291,6 +291,8 @@ object Project extends Init[Scope] with ProjectExtra
|
|||
printScopes("Delegates", delegates(structure, scope, key)) +
|
||||
printScopes("Related", related)
|
||||
}
|
||||
def settingGraph(structure: BuildStructure, basedir: File, scoped: ScopedKey[_])(implicit display: Show[ScopedKey[_]]): SettingGraph =
|
||||
SettingGraph(structure, basedir, scoped, 0)
|
||||
def graphSettings(structure: BuildStructure, basedir: File)(implicit display: Show[ScopedKey[_]])
|
||||
{
|
||||
def graph(actual: Boolean, name: String) = graphSettings(structure, actual, name, new File(basedir, name + ".dot"))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2011 Mark Harrah, Eugene Yokota
|
||||
*/
|
||||
package sbt
|
||||
|
||||
import java.net.URI
|
||||
import java.io.File
|
||||
import Project.{ScopedKey, flattenLocals, compiled}
|
||||
import Load.BuildStructure
|
||||
import Predef.{any2stringadd => _, _}
|
||||
|
||||
object SettingGraph
|
||||
{
|
||||
def apply(structure: BuildStructure, basedir: File, scoped: ScopedKey[_], generation: Int)
|
||||
(implicit display: Show[ScopedKey[_]]): SettingGraph =
|
||||
{
|
||||
val key = scoped.key
|
||||
val scope = scoped.scope
|
||||
|
||||
lazy val clazz = key.manifest.erasure
|
||||
lazy val firstType = key.manifest.typeArguments.head
|
||||
val (typeName: String, value: Option[_]) =
|
||||
structure.data.get(scope, key) match {
|
||||
case None => ("", None)
|
||||
case Some(v) =>
|
||||
if(clazz == classOf[Task[_]]) ("Task[" + firstType.toString + "]", None)
|
||||
else if(clazz == classOf[InputTask[_]]) ("InputTask[" + firstType.toString + "]", None)
|
||||
else (key.manifest.toString, Some(v))
|
||||
}
|
||||
|
||||
val definedIn = structure.data.definingScope(scope, key) map { sc => display(ScopedKey(sc, key)) }
|
||||
val cMap = flattenLocals(compiled(structure.settings, false)(structure.delegates, structure.scopeLocal, display))
|
||||
// val related = cMap.keys.filter(k => k.key == key && k.scope != scope)
|
||||
val depends = cMap.get(scoped) match { case Some(c) => c.dependencies.toSet; case None => Set.empty }
|
||||
// val reverse = reverseDependencies(cMap, scoped)
|
||||
|
||||
SettingGraph(display(scoped), definedIn, typeName, value, key.description, basedir,
|
||||
depends map { apply(structure, basedir, _, generation + 1) })
|
||||
}
|
||||
}
|
||||
|
||||
case class SettingGraph(name: String,
|
||||
definedIn: Option[String],
|
||||
typeName: String,
|
||||
value: Option[Any],
|
||||
description: Option[String],
|
||||
basedir: File,
|
||||
depends: Set[SettingGraph])
|
||||
{
|
||||
def valueString: String =
|
||||
value map {
|
||||
case f: File => IO.relativize(basedir, f) getOrElse {f.toString}
|
||||
case x => x.toString
|
||||
} getOrElse {typeName}
|
||||
|
||||
def dependsAscii: String = Graph.toAscii(this,
|
||||
(x: SettingGraph) => x.depends.toSeq,
|
||||
(x: SettingGraph) => "%s = %s" format(x.definedIn getOrElse {""}, x.valueString))
|
||||
}
|
||||
|
||||
object Graph
|
||||
{
|
||||
// [info] foo
|
||||
// [info] +-bar
|
||||
// [info] | +-baz
|
||||
// [info] |
|
||||
// [info] +-quux
|
||||
def toAscii[A](top: A, children: A => Seq[A], display: A => String): String = {
|
||||
val maxColumn = jline.Terminal.getTerminal.getTerminalWidth - 8
|
||||
val twoSpaces = " " + " " // prevent accidentally being converted into a tab
|
||||
def limitLine(s: String): String =
|
||||
if (s.length > maxColumn) s.slice(0, maxColumn - 2) + ".."
|
||||
else s
|
||||
def insertBar(s: String, at: Int): String =
|
||||
s.slice(0, at) +
|
||||
(s(at).toString match {
|
||||
case " " => "|"
|
||||
case x => x
|
||||
}) +
|
||||
s.slice(at + 1, s.length)
|
||||
def toAsciiLines(node: A, level: Int): 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 (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
|
||||
}
|
||||
|
||||
toAsciiLines(top, 0).mkString("\n")
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue