mirror of https://github.com/sbt/sbt.git
split command core to main/command/
This commit is contained in:
parent
bf472b8ff4
commit
98c98f9c26
|
|
@ -11,7 +11,7 @@ package sbt
|
||||||
import DefaultParsers._
|
import DefaultParsers._
|
||||||
import Types.idFun
|
import Types.idFun
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import CommandSupport.ShowCommand
|
import CommandStrings.ShowCommand
|
||||||
|
|
||||||
final class ParsedKey(val key: ScopedKey[_], val mask: ScopeMask)
|
final class ParsedKey(val key: ScopedKey[_], val mask: ScopeMask)
|
||||||
object Act
|
object Act
|
||||||
|
|
|
||||||
|
|
@ -3,37 +3,15 @@
|
||||||
*/
|
*/
|
||||||
package sbt
|
package sbt
|
||||||
|
|
||||||
import complete.HistoryCommands
|
object CommandStrings
|
||||||
import scala.annotation.tailrec
|
|
||||||
|
|
||||||
import java.io.File
|
|
||||||
import Path._
|
|
||||||
|
|
||||||
object CommandSupport
|
|
||||||
{
|
{
|
||||||
def logger(s: State) = globalLogging(s).full
|
@deprecated("Use the `log` member of a State instance directly.", "0.12.0")
|
||||||
def globalLogging(s: State) = s get Keys.globalLogging getOrElse error("Global logging misconfigured")
|
def logger(s: State) = s.log
|
||||||
|
|
||||||
// slightly better fallback in case of older launcher
|
@deprecated("Use the `globalLogging` member of a State instance directly.", "0.12.0")
|
||||||
def bootDirectory(state: State): File =
|
def globalLogging(s: State) = s.globalLogging
|
||||||
try { state.configuration.provider.scalaProvider.launcher.bootDirectory }
|
|
||||||
catch { case e: NoSuchMethodError => new File(".").getAbsoluteFile }
|
|
||||||
|
|
||||||
private def canRead = (_: File).canRead
|
|
||||||
def notReadable(files: Seq[File]): Seq[File] = files filterNot canRead
|
|
||||||
def readable(files: Seq[File]): Seq[File] = files filter canRead
|
|
||||||
def sbtRCs(s: State): Seq[File] =
|
|
||||||
(Path.userHome / sbtrc) ::
|
|
||||||
(s.baseDir / sbtrc asFile) ::
|
|
||||||
Nil
|
|
||||||
|
|
||||||
def readLines(files: Seq[File]): Seq[String] = files flatMap (line => IO.readLines(line)) flatMap processLine
|
|
||||||
def processLine(s: String) = { val trimmed = s.trim; if(ignoreLine(trimmed)) None else Some(trimmed) }
|
|
||||||
def ignoreLine(s: String) = s.isEmpty || s.startsWith("#")
|
|
||||||
|
|
||||||
/** The prefix used to identify a request to execute the remaining input on source changes.*/
|
/** The prefix used to identify a request to execute the remaining input on source changes.*/
|
||||||
val ContinuousExecutePrefix = "~"
|
|
||||||
val HelpCommand = "help"
|
|
||||||
val AboutCommand = "about"
|
val AboutCommand = "about"
|
||||||
val TasksCommand = "tasks"
|
val TasksCommand = "tasks"
|
||||||
val ProjectCommand = "project"
|
val ProjectCommand = "project"
|
||||||
|
|
@ -41,8 +19,14 @@ object CommandSupport
|
||||||
val ShowCommand = "show"
|
val ShowCommand = "show"
|
||||||
val BootCommand = "boot"
|
val BootCommand = "boot"
|
||||||
|
|
||||||
val Exit = "exit"
|
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||||
val Quit = "quit"
|
val ContinuousExecutePrefix = BasicCommandStrings.ContinuousExecutePrefix
|
||||||
|
|
||||||
|
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||||
|
val Exit = BasicCommandStrings.Exit
|
||||||
|
|
||||||
|
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||||
|
val Quit = BasicCommandStrings.Quit
|
||||||
|
|
||||||
val EvalCommand = "eval"
|
val EvalCommand = "eval"
|
||||||
val evalBrief = (EvalCommand + " <expression>", "Evaluates the given Scala expression and prints the result and type.")
|
val evalBrief = (EvalCommand + " <expression>", "Evaluates the given Scala expression and prints the result and type.")
|
||||||
|
|
@ -128,9 +112,8 @@ SetCommand + """ <setting-expression>
|
||||||
def sessionBrief = (SessionCommand + " <session-command>", "Manipulates session settings. For details, run 'help " + SessionCommand + "'.")
|
def sessionBrief = (SessionCommand + " <session-command>", "Manipulates session settings. For details, run 'help " + SessionCommand + "'.")
|
||||||
|
|
||||||
/** The command name to terminate the program.*/
|
/** The command name to terminate the program.*/
|
||||||
val TerminateAction: String = Exit
|
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||||
|
val TerminateAction: String = BasicCommandStrings.TerminateAction
|
||||||
def continuousBriefHelp = (ContinuousExecutePrefix + " <command>", "Executes the specified command whenever source files change.")
|
|
||||||
|
|
||||||
def tasksPreamble = """
|
def tasksPreamble = """
|
||||||
This is a list of tasks defined for the current project.
|
This is a list of tasks defined for the current project.
|
||||||
|
|
@ -140,11 +123,6 @@ Tasks produce values. Use the 'show' command to run the task and print the resu
|
||||||
def tasksBrief = "Displays the tasks defined for the current project."
|
def tasksBrief = "Displays the tasks defined for the current project."
|
||||||
def tasksDetailed = "Displays the tasks defined directly or indirectly for the current project."
|
def tasksDetailed = "Displays the tasks defined directly or indirectly for the current project."
|
||||||
|
|
||||||
def helpBrief = (HelpCommand + " [command]*", "Displays this help message or prints detailed help on requested commands.")
|
|
||||||
def helpDetailed = """
|
|
||||||
If an argument is provided, this prints detailed help for that command.
|
|
||||||
Otherwise, this prints a help summary."""
|
|
||||||
|
|
||||||
def aboutBrief = "Displays basic information about sbt and the build."
|
def aboutBrief = "Displays basic information about sbt and the build."
|
||||||
def aboutDetailed = aboutBrief
|
def aboutDetailed = aboutBrief
|
||||||
|
|
||||||
|
|
@ -175,126 +153,23 @@ ProjectCommand +
|
||||||
def projectsBrief = projectsDetailed
|
def projectsBrief = projectsDetailed
|
||||||
def projectsDetailed = "Displays the names of available projects."
|
def projectsDetailed = "Displays the names of available projects."
|
||||||
|
|
||||||
def historyHelp = Help.briefDetail(HistoryCommands.descriptions)
|
|
||||||
|
|
||||||
def exitBrief = "Terminates the build."
|
|
||||||
|
|
||||||
def sbtrc = ".sbtrc"
|
def sbtrc = ".sbtrc"
|
||||||
|
|
||||||
def ReadCommand = "<"
|
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||||
def ReadFiles = " file1 file2 ..."
|
def ReadCommand = BasicCommandStrings.ReadCommand
|
||||||
def ReadBrief = (ReadCommand + " <file>*", "Reads command lines from the provided files.")
|
|
||||||
def ReadDetailed =
|
|
||||||
ReadCommand + ReadFiles + """
|
|
||||||
|
|
||||||
Reads the lines from the given files and inserts them as commands.
|
|
||||||
All empty lines and lines that start with '#' are ignored.
|
|
||||||
If a file does not exist or is not readable, this command fails.
|
|
||||||
|
|
||||||
All the lines from all the files are read before any of the commands
|
|
||||||
are executed. Thus, if any file is not readable, none of commands
|
|
||||||
from any of the files (even the existing ones) will be run.
|
|
||||||
|
|
||||||
You probably need to escape this command if entering it at your shell."""
|
|
||||||
|
|
||||||
def ApplyCommand = "apply"
|
|
||||||
def ApplyBrief = (ApplyCommand + " <module-name>*", ApplyDetailed)
|
|
||||||
def ApplyDetailed = "Transforms the current State by calling <module-name>.apply(currentState) for each listed."
|
|
||||||
|
|
||||||
def DefaultsCommand = "add-default-commands"
|
def DefaultsCommand = "add-default-commands"
|
||||||
def DefaultsBrief = (DefaultsCommand, DefaultsDetailed)
|
def DefaultsBrief = (DefaultsCommand, DefaultsDetailed)
|
||||||
def DefaultsDetailed = "Registers default built-in commands"
|
def DefaultsDetailed = "Registers default built-in commands"
|
||||||
|
|
||||||
|
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||||
def RebootCommand = "reboot"
|
def RebootCommand = "reboot"
|
||||||
def RebootSummary = RebootCommand + " [full]"
|
|
||||||
def RebootBrief = (RebootSummary, "Reboots sbt and then executes the remaining commands.")
|
|
||||||
def RebootDetailed =
|
|
||||||
RebootSummary + """
|
|
||||||
|
|
||||||
This command is equivalent to exiting sbt, restarting, and running the
|
|
||||||
remaining commands with the exception that the JVM is not shut down.
|
|
||||||
|
|
||||||
If 'full' is specified, the boot directory (`~/.sbt/boot` by default)
|
|
||||||
is deleted before restarting. This forces an update of sbt and Scala
|
|
||||||
and is useful when working with development versions of sbt or Scala."""
|
|
||||||
|
|
||||||
|
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||||
def Multi = ";"
|
def Multi = ";"
|
||||||
def MultiBrief = (Multi + " <command> (" + Multi + " <command>)*", "Runs the provided semicolon-separated commands.")
|
|
||||||
def MultiDetailed =
|
|
||||||
Multi + " command1 " + Multi + """ command2 ...
|
|
||||||
|
|
||||||
Runs the specified commands."""
|
|
||||||
|
|
||||||
|
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||||
def AppendCommand = "append"
|
def AppendCommand = "append"
|
||||||
def AppendLastBrief = (AppendCommand + " <command>", AppendLastDetailed)
|
|
||||||
def AppendLastDetailed = "Appends 'command' to list of commands to run."
|
|
||||||
|
|
||||||
val AliasCommand = "alias"
|
|
||||||
def AliasBrief = (AliasCommand, "Adds, removes, or prints command aliases.")
|
|
||||||
def AliasDetailed =
|
|
||||||
AliasCommand + """
|
|
||||||
|
|
||||||
Prints a list of defined aliases.
|
|
||||||
|
|
||||||
""" +
|
|
||||||
AliasCommand + """ name
|
|
||||||
|
|
||||||
Prints the alias defined for `name`.
|
|
||||||
|
|
||||||
""" +
|
|
||||||
AliasCommand + """ name=value
|
|
||||||
|
|
||||||
Sets the alias `name` to `value`, replacing any existing alias with that name.
|
|
||||||
Whenever `name` is entered, the corresponding `value` is run.
|
|
||||||
If any argument is provided to `name`, it is appended as argument to `value`.
|
|
||||||
|
|
||||||
""" +
|
|
||||||
AliasCommand + """ name=
|
|
||||||
|
|
||||||
Removes the alias for `name`."""
|
|
||||||
|
|
||||||
def Discover = "discover"
|
|
||||||
def DiscoverBrief = (DiscoverSyntax, "Finds annotated classes and subclasses.")
|
|
||||||
def DiscoverSyntax = Discover + " [-module true|false] [-sub <names>] [-annot <names>]"
|
|
||||||
def DiscoverDetailed =
|
|
||||||
DiscoverSyntax + """
|
|
||||||
|
|
||||||
Looks for public, concrete classes that match the requested query using the current sbt.inc.Analysis instance.
|
|
||||||
|
|
||||||
-module
|
|
||||||
Specifies whether modules (true) or classes (false) are found.
|
|
||||||
The default is classes/traits (false).
|
|
||||||
|
|
||||||
-sub
|
|
||||||
Specifies comma-separated class names.
|
|
||||||
Classes that have one or more of these classes as an ancestor are included in the resulting list.
|
|
||||||
|
|
||||||
-annot
|
|
||||||
Specifies comma-separated annotation names.
|
|
||||||
Classes with one or more of these annotations on the class or one of its non-private methods are included in the resulting list.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def CompileName = "direct-compile"
|
|
||||||
def CompileBrief = (CompileSyntax, "Incrementally compiles the provided sources.")
|
|
||||||
def CompileSyntax = CompileName + " -src <paths> [-cp <paths>] [-d <path>]"
|
|
||||||
def CompileDetailed =
|
|
||||||
CompileSyntax + """
|
|
||||||
|
|
||||||
Incrementally compiles Scala and Java sources.
|
|
||||||
|
|
||||||
<paths> are explicit paths separated by the platform path separator.
|
|
||||||
|
|
||||||
The specified output path will contain the following directory structure:
|
|
||||||
|
|
||||||
scala_<version>/
|
|
||||||
classes/
|
|
||||||
cache/
|
|
||||||
|
|
||||||
Compiled classes will be written to the 'classes' directory.
|
|
||||||
Cached information about the compilation will be written to 'cache'.
|
|
||||||
"""
|
|
||||||
|
|
||||||
val FailureWall = "---"
|
|
||||||
|
|
||||||
def Load = "load"
|
def Load = "load"
|
||||||
def LoadLabel = "a project"
|
def LoadLabel = "a project"
|
||||||
|
|
@ -308,31 +183,20 @@ CompileSyntax + """
|
||||||
def LoadProjectBrief = (LoadProject, LoadProjectDetailed)
|
def LoadProjectBrief = (LoadProject, LoadProjectDetailed)
|
||||||
def LoadProjectDetailed = "Loads the project in the current directory"
|
def LoadProjectDetailed = "Loads the project in the current directory"
|
||||||
|
|
||||||
def Shell = "shell"
|
@deprecated("Moved to State", "0.12.0")
|
||||||
def ShellBrief = ShellDetailed
|
val FailureWall = State.FailureWall
|
||||||
def ShellDetailed = "Provides an interactive prompt from which commands can be run."
|
|
||||||
|
|
||||||
|
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||||
|
def Shell = BasicCommandStrings.Shell
|
||||||
|
|
||||||
|
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||||
def ClearOnFailure = "--"
|
def ClearOnFailure = "--"
|
||||||
|
|
||||||
|
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||||
def OnFailure = "-"
|
def OnFailure = "-"
|
||||||
def OnFailureBrief = (OnFailure + " command", "Registers 'command' to run if a command fails.")
|
|
||||||
def OnFailureDetailed =
|
|
||||||
OnFailure + """ command
|
|
||||||
|
|
||||||
Registers 'command' to run when a command fails to complete normally.
|
|
||||||
|
|
||||||
Only one failure command may be registered at a time, so this command
|
|
||||||
replaces the previous command if there is one.
|
|
||||||
|
|
||||||
The failure command resets when it runs once, so it must be added
|
|
||||||
again if desired."""
|
|
||||||
|
|
||||||
|
@deprecated("Moved to BasicCommandStrings", "0.12.0")
|
||||||
def IfLast = "iflast"
|
def IfLast = "iflast"
|
||||||
def IfLastBrief = (IfLast + " <command>", IfLastCommon)
|
|
||||||
def IfLastCommon = "If there are no more commands after this one, 'command' is run."
|
|
||||||
def IfLastDetailed =
|
|
||||||
IfLast + """ command
|
|
||||||
|
|
||||||
""" + IfLastCommon
|
|
||||||
|
|
||||||
def InitCommand = "initialize"
|
def InitCommand = "initialize"
|
||||||
def InitBrief = (InitCommand, "Initializes command processing.")
|
def InitBrief = (InitCommand, "Initializes command processing.")
|
||||||
|
|
@ -352,4 +216,12 @@ load-commands -base ~/.sbt/commands
|
||||||
< .sbtrc
|
< .sbtrc
|
||||||
Runs commands from ~/.sbtrc and ./.sbtrc if they exist
|
Runs commands from ~/.sbtrc and ./.sbtrc if they exist
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import Path._
|
||||||
|
|
||||||
|
def sbtRCs(s: State): Seq[File] =
|
||||||
|
(Path.userHome / sbtrc) ::
|
||||||
|
(s.baseDir / sbtrc asFile) ::
|
||||||
|
Nil
|
||||||
}
|
}
|
||||||
|
|
@ -14,7 +14,7 @@ object IvyConsole
|
||||||
lazy val command =
|
lazy val command =
|
||||||
Command.command(Name) { state =>
|
Command.command(Name) { state =>
|
||||||
val Dependencies(managed, repos, unmanaged) = parseDependencies(state.remainingCommands, state.log)
|
val Dependencies(managed, repos, unmanaged) = parseDependencies(state.remainingCommands, state.log)
|
||||||
val base = new File(CommandSupport.bootDirectory(state), Name)
|
val base = new File(CommandUtil.bootDirectory(state), Name)
|
||||||
IO.createDirectory(base)
|
IO.createDirectory(base)
|
||||||
|
|
||||||
val (eval, structure) = Load.defaultLoad(state, base, state.log)
|
val (eval, structure) = Load.defaultLoad(state, base, state.log)
|
||||||
|
|
|
||||||
|
|
@ -56,11 +56,10 @@ object Keys
|
||||||
// val onComplete = SettingKey[RMap[Task,Result] => RMap[Task,Result]]("on-complete", "Transformation to apply to the final task result map. This may also be used to register hooks to run when task evaluation completes.")
|
// val onComplete = SettingKey[RMap[Task,Result] => RMap[Task,Result]]("on-complete", "Transformation to apply to the final task result map. This may also be used to register hooks to run when task evaluation completes.")
|
||||||
|
|
||||||
// Command keys
|
// Command keys
|
||||||
val globalLogging = AttributeKey[GlobalLogging]("global-logging", "Provides a global Logger, including command logging.")
|
val historyPath = SettingKey(BasicKeys.historyPath)
|
||||||
val historyPath = SettingKey[Option[File]]("history", "The location where command line history is persisted.")
|
val shellPrompt = SettingKey(BasicKeys.shellPrompt)
|
||||||
val shellPrompt = SettingKey[State => String]("shell-prompt", "The function that constructs the command prompt from the current build state.")
|
|
||||||
val analysis = AttributeKey[inc.Analysis]("analysis", "Analysis of compilation, including dependencies and generated outputs.")
|
val analysis = AttributeKey[inc.Analysis]("analysis", "Analysis of compilation, including dependencies and generated outputs.")
|
||||||
val watch = SettingKey[Watched]("watch", "Continuous execution configuration.")
|
val watch = SettingKey(BasicKeys.watch)
|
||||||
val pollInterval = SettingKey[Int]("poll-interval", "Interval between checks for modified sources by the continuous execution command.")
|
val pollInterval = SettingKey[Int]("poll-interval", "Interval between checks for modified sources by the continuous execution command.")
|
||||||
val watchSources = TaskKey[Seq[File]]("watch-sources", "Defines the sources in this project for continuous execution to watch for changes.")
|
val watchSources = TaskKey[Seq[File]]("watch-sources", "Defines the sources in this project for continuous execution to watch for changes.")
|
||||||
val watchTransitiveSources = TaskKey[Seq[File]]("watch-transitive-sources", "Defines the sources in all projects for continuous execution to watch.")
|
val watchTransitiveSources = TaskKey[Seq[File]]("watch-transitive-sources", "Defines the sources in all projects for continuous execution to watch.")
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ package sbt
|
||||||
import inc.{FileValueCache, Locate}
|
import inc.{FileValueCache, Locate}
|
||||||
import Project.{inScope, ScopedKey, ScopeLocal, Setting}
|
import Project.{inScope, ScopedKey, ScopeLocal, Setting}
|
||||||
import Keys.{appConfiguration, baseDirectory, configuration, streams, Streams, thisProject, thisProjectRef}
|
import Keys.{appConfiguration, baseDirectory, configuration, streams, Streams, thisProject, thisProjectRef}
|
||||||
import Keys.{globalLogging, isDummy, loadedBuild, parseResult, resolvedScoped, taskDefinitionKey}
|
import Keys.{isDummy, loadedBuild, parseResult, resolvedScoped, taskDefinitionKey}
|
||||||
import tools.nsc.reporters.ConsoleReporter
|
import tools.nsc.reporters.ConsoleReporter
|
||||||
import Build.{analyzed, data}
|
import Build.{analyzed, data}
|
||||||
import Scope.{GlobalScope, ThisScope}
|
import Scope.{GlobalScope, ThisScope}
|
||||||
|
|
@ -489,7 +489,7 @@ object Load
|
||||||
val inputs = Compiler.inputs(data(classpath), sources, target, Nil, Nil, definesClass, Compiler.DefaultMaxErrors, CompileOrder.Mixed)(compilers, log)
|
val inputs = Compiler.inputs(data(classpath), sources, target, Nil, Nil, definesClass, Compiler.DefaultMaxErrors, CompileOrder.Mixed)(compilers, log)
|
||||||
val analysis =
|
val analysis =
|
||||||
try { Compiler(inputs, log) }
|
try { Compiler(inputs, log) }
|
||||||
catch { case _: xsbti.CompileFailed => throw new NoMessageException } // compiler already logged errors
|
catch { case _: xsbti.CompileFailed => throw new AlreadyHandledException } // compiler already logged errors
|
||||||
(inputs, analysis)
|
(inputs, analysis)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ package sbt
|
||||||
import std.Transform
|
import std.Transform
|
||||||
import Project.ScopedKey
|
import Project.ScopedKey
|
||||||
import Scope.GlobalScope
|
import Scope.GlobalScope
|
||||||
|
import MainLogging._
|
||||||
import Keys.{logLevel, logManager, persistLogLevel, persistTraceLevel, state, traceLevel}
|
import Keys.{logLevel, logManager, persistLogLevel, persistTraceLevel, state, traceLevel}
|
||||||
|
|
||||||
object LogManager
|
object LogManager
|
||||||
|
|
@ -21,11 +22,6 @@ object LogManager
|
||||||
lazy val default: LogManager = withLoggers()
|
lazy val default: LogManager = withLoggers()
|
||||||
def defaults(extra: ScopedKey[_] => Seq[AbstractLogger]): LogManager = withLoggers(extra = extra)
|
def defaults(extra: ScopedKey[_] => Seq[AbstractLogger]): LogManager = withLoggers(extra = extra)
|
||||||
|
|
||||||
def defaultScreen: AbstractLogger = ConsoleLogger()
|
|
||||||
|
|
||||||
def defaultBacked(useColor: Boolean = ConsoleLogger.formatEnabled): PrintWriter => ConsoleLogger =
|
|
||||||
to => ConsoleLogger(ConsoleLogger.printWriterOut(to), useColor = useColor) // TODO: should probably filter ANSI codes when useColor=false
|
|
||||||
|
|
||||||
def withScreenLogger(mk: => AbstractLogger): LogManager = withLoggers(mk)
|
def withScreenLogger(mk: => AbstractLogger): LogManager = withLoggers(mk)
|
||||||
|
|
||||||
def withLoggers(screen: => AbstractLogger = defaultScreen, backed: PrintWriter => AbstractLogger = defaultBacked(), extra: ScopedKey[_] => Seq[AbstractLogger] = _ => Nil): LogManager =
|
def withLoggers(screen: => AbstractLogger = defaultScreen, backed: PrintWriter => AbstractLogger = defaultBacked(), extra: ScopedKey[_] => Seq[AbstractLogger] = _ => Nil): LogManager =
|
||||||
|
|
@ -42,40 +38,12 @@ object LogManager
|
||||||
val backingLevel = getOr(persistLogLevel.key, Level.Debug)
|
val backingLevel = getOr(persistLogLevel.key, Level.Debug)
|
||||||
val screenTrace = getOr(traceLevel.key, -1)
|
val screenTrace = getOr(traceLevel.key, -1)
|
||||||
val backingTrace = getOr(persistTraceLevel.key, Int.MaxValue)
|
val backingTrace = getOr(persistTraceLevel.key, Int.MaxValue)
|
||||||
val extraBacked = (state get Keys.globalLogging).map(_.backed).toList
|
val extraBacked = state.globalLogging.backed :: Nil
|
||||||
multiLogger( new MultiLoggerConfig(console, backed, extraBacked ::: extra, screenLevel, backingLevel, screenTrace, backingTrace) )
|
multiLogger( new MultiLoggerConfig(console, backed, extraBacked ::: extra, screenLevel, backingLevel, screenTrace, backingTrace) )
|
||||||
}
|
}
|
||||||
def multiLogger(config: MultiLoggerConfig): Logger =
|
|
||||||
{
|
|
||||||
import config._
|
|
||||||
val multi = new MultiLogger(console :: backed :: extra)
|
|
||||||
// sets multi to the most verbose for clients that inspect the current level
|
|
||||||
multi setLevel Level.unionAll(backingLevel :: screenLevel :: extra.map(_.getLevel))
|
|
||||||
// set the specific levels
|
|
||||||
console setLevel screenLevel
|
|
||||||
backed setLevel backingLevel
|
|
||||||
console setTrace screenTrace
|
|
||||||
backed setTrace backingTrace
|
|
||||||
multi: Logger
|
|
||||||
}
|
|
||||||
def globalDefault(writer: PrintWriter, backing: GlobalLogBacking): GlobalLogging =
|
|
||||||
{
|
|
||||||
val backed = defaultBacked()(writer)
|
|
||||||
val full = multiLogger(defaultMultiConfig( backed ) )
|
|
||||||
GlobalLogging(full, backed, backing)
|
|
||||||
}
|
|
||||||
|
|
||||||
def defaultMultiConfig(backing: AbstractLogger): MultiLoggerConfig =
|
|
||||||
new MultiLoggerConfig(defaultScreen, backing, Nil, Level.Info, Level.Debug, -1, Int.MaxValue)
|
|
||||||
}
|
}
|
||||||
final case class MultiLoggerConfig(console: AbstractLogger, backed: AbstractLogger, extra: List[AbstractLogger], screenLevel: Level.Value, backingLevel: Level.Value, screenTrace: Int, backingTrace: Int)
|
|
||||||
trait LogManager
|
trait LogManager
|
||||||
{
|
{
|
||||||
def apply(data: Settings[Scope], state: State, task: ScopedKey[_], writer: PrintWriter): Logger
|
def apply(data: Settings[Scope], state: State, task: ScopedKey[_], writer: PrintWriter): Logger
|
||||||
}
|
}
|
||||||
final case class GlobalLogBacking(file: File, last: Option[File])
|
|
||||||
{
|
|
||||||
def shift(newFile: File) = GlobalLogBacking(newFile, Some(file))
|
|
||||||
def unshift = GlobalLogBacking(last getOrElse file, None)
|
|
||||||
}
|
|
||||||
final case class GlobalLogging(full: Logger, backed: ConsoleLogger, backing: GlobalLogBacking)
|
|
||||||
|
|
|
||||||
370
main/Main.scala
370
main/Main.scala
|
|
@ -3,132 +3,64 @@
|
||||||
*/
|
*/
|
||||||
package sbt
|
package sbt
|
||||||
|
|
||||||
import Execute.NodeView
|
import complete.{DefaultParsers, Parser}
|
||||||
import complete.{DefaultParsers, HistoryCommands, Parser}
|
|
||||||
import HistoryCommands.{Start => HistoryPrefix}
|
|
||||||
import compiler.EvalImports
|
import compiler.EvalImports
|
||||||
import Types.{const,idFun}
|
import Types.idFun
|
||||||
import Aggregation.AnyKeys
|
import Aggregation.AnyKeys
|
||||||
|
|
||||||
import Command.applyEffect
|
|
||||||
import Keys.{analysis,historyPath,globalLogging,shellPrompt}
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.collection.JavaConversions._
|
|
||||||
import Function.tupled
|
|
||||||
import java.net.URI
|
|
||||||
import java.lang.reflect.InvocationTargetException
|
|
||||||
import Path._
|
import Path._
|
||||||
|
import StandardMain._
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.net.URI
|
||||||
|
|
||||||
/** This class is the entry point for sbt.*/
|
/** This class is the entry point for sbt.*/
|
||||||
final class xMain extends xsbti.AppMain
|
final class xMain extends xsbti.AppMain
|
||||||
{
|
{
|
||||||
def run(configuration: xsbti.AppConfiguration): xsbti.MainResult =
|
def run(configuration: xsbti.AppConfiguration): xsbti.MainResult =
|
||||||
{
|
{
|
||||||
import BuiltinCommands.{initialAttributes, initialize, defaults, DefaultBootCommands}
|
import BuiltinCommands.{initialize, defaults}
|
||||||
import CommandSupport.{BootCommand, DefaultsCommand, InitCommand}
|
import CommandStrings.{BootCommand, DefaultsCommand, InitCommand}
|
||||||
val initialCommandDefs = Seq(initialize, defaults)
|
MainLoop.runLogged( initialState(configuration,
|
||||||
val commands = DefaultsCommand +: InitCommand +: BootCommand +: configuration.arguments.map(_.trim)
|
Seq(initialize, defaults),
|
||||||
val state = State( configuration, initialCommandDefs, Set.empty, None, commands, State.newHistory, initialAttributes, State.Continue )
|
DefaultsCommand :: InitCommand :: BootCommand :: Nil)
|
||||||
MainLoop.runLogged(state)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final class ScriptMain extends xsbti.AppMain
|
final class ScriptMain extends xsbti.AppMain
|
||||||
{
|
{
|
||||||
def run(configuration: xsbti.AppConfiguration): xsbti.MainResult =
|
def run(configuration: xsbti.AppConfiguration): xsbti.MainResult =
|
||||||
{
|
MainLoop.runLogged( initialState(configuration,
|
||||||
import BuiltinCommands.{initialAttributes, ScriptCommands}
|
BuiltinCommands.ScriptCommands,
|
||||||
val commands = Script.Name +: configuration.arguments.map(_.trim)
|
Script.Name :: Nil)
|
||||||
val state = State( configuration, ScriptCommands, Set.empty, None, commands, State.newHistory, initialAttributes, State.Continue )
|
)
|
||||||
MainLoop.runLogged(state)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
final class ConsoleMain extends xsbti.AppMain
|
final class ConsoleMain extends xsbti.AppMain
|
||||||
{
|
{
|
||||||
def run(configuration: xsbti.AppConfiguration): xsbti.MainResult =
|
def run(configuration: xsbti.AppConfiguration): xsbti.MainResult =
|
||||||
{
|
MainLoop.runLogged( initialState(configuration,
|
||||||
import BuiltinCommands.{initialAttributes, ConsoleCommands}
|
BuiltinCommands.ConsoleCommands,
|
||||||
val commands = IvyConsole.Name +: configuration.arguments.map(_.trim)
|
IvyConsole.Name :: Nil)
|
||||||
val state = State( configuration, ConsoleCommands, Set.empty, None, commands, State.newHistory, initialAttributes, State.Continue )
|
)
|
||||||
MainLoop.runLogged(state)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
object MainLoop
|
|
||||||
|
object StandardMain
|
||||||
{
|
{
|
||||||
/** Entry point to run the remaining commands in State with managed global logging.*/
|
def initialState(configuration: xsbti.AppConfiguration, initialDefinitions: Seq[Command], preCommands: Seq[String]): State =
|
||||||
def runLogged(state: State): xsbti.MainResult =
|
{
|
||||||
runLoggedLoop(state, GlobalLogBacking(newBackingFile(), None))
|
val commands = preCommands ++ configuration.arguments.map(_.trim)
|
||||||
|
State( configuration, initialDefinitions, Set.empty, None, commands, State.newHistory, BuiltinCommands.initialAttributes, initialGlobalLogging, State.Continue )
|
||||||
/** Constructs a new, (weakly) unique, temporary file to use as the backing for global logging. */
|
}
|
||||||
def newBackingFile(): File = File.createTempFile("sbt",".log")
|
def initialGlobalLogging: GlobalLogging =
|
||||||
|
GlobalLogging.initial(MainLogging.globalDefault _, File.createTempFile("sbt",".log"))
|
||||||
/** Run loop that evaluates remaining commands and manages changes to global logging configuration.*/
|
|
||||||
@tailrec def runLoggedLoop(state: State, logBacking: GlobalLogBacking): xsbti.MainResult =
|
|
||||||
runAndClearLast(state, logBacking) match {
|
|
||||||
case ret: Return => // delete current and last log files when exiting normally
|
|
||||||
logBacking.file.delete()
|
|
||||||
deleteLastLog(logBacking)
|
|
||||||
ret.result
|
|
||||||
case clear: ClearGlobalLog => // delete previous log file, move current to previous, and start writing to a new file
|
|
||||||
deleteLastLog(logBacking)
|
|
||||||
runLoggedLoop(clear.state, logBacking shift newBackingFile())
|
|
||||||
case keep: KeepGlobalLog => // make previous log file the current log file
|
|
||||||
logBacking.file.delete
|
|
||||||
runLoggedLoop(keep.state, logBacking.unshift)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Runs the next sequence of commands, cleaning up global logging after any exceptions. */
|
|
||||||
def runAndClearLast(state: State, logBacking: GlobalLogBacking): RunNext =
|
|
||||||
try
|
|
||||||
runWithNewLog(state, logBacking)
|
|
||||||
catch {
|
|
||||||
case e: xsbti.FullReload =>
|
|
||||||
deleteLastLog(logBacking)
|
|
||||||
throw e // pass along a reboot request
|
|
||||||
case e =>
|
|
||||||
System.err.println("sbt appears to be exiting abnormally.\n The log file for this session is at " + logBacking.file)
|
|
||||||
deleteLastLog(logBacking)
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Deletes the previous global log file. */
|
|
||||||
def deleteLastLog(logBacking: GlobalLogBacking): Unit =
|
|
||||||
logBacking.last.foreach(_.delete())
|
|
||||||
|
|
||||||
/** Runs the next sequence of commands with global logging in place. */
|
|
||||||
def runWithNewLog(state: State, logBacking: GlobalLogBacking): RunNext =
|
|
||||||
Using.fileWriter(append = true)(logBacking.file) { writer =>
|
|
||||||
val out = new java.io.PrintWriter(writer)
|
|
||||||
val loggedState = state.put(globalLogging, LogManager.globalDefault(out, logBacking))
|
|
||||||
try run(loggedState) finally out.close()
|
|
||||||
}
|
|
||||||
sealed trait RunNext
|
|
||||||
final class ClearGlobalLog(val state: State) extends RunNext
|
|
||||||
final class KeepGlobalLog(val state: State) extends RunNext
|
|
||||||
final class Return(val result: xsbti.MainResult) extends RunNext
|
|
||||||
|
|
||||||
/** Runs the next sequence of commands that doesn't require global logging changes.*/
|
|
||||||
@tailrec def run(state: State): RunNext =
|
|
||||||
state.next match
|
|
||||||
{
|
|
||||||
case State.Continue => run(next(state))
|
|
||||||
case State.ClearGlobalLog => new ClearGlobalLog(state.continue)
|
|
||||||
case State.KeepLastLog => new KeepGlobalLog(state.continue)
|
|
||||||
case ret: State.Return => new Return(ret.result)
|
|
||||||
}
|
|
||||||
|
|
||||||
def next(state: State): State =
|
|
||||||
ErrorHandling.wideConvert { state.process(Command.process) } match
|
|
||||||
{
|
|
||||||
case Right(s) => s
|
|
||||||
case Left(t: xsbti.FullReload) => throw t
|
|
||||||
case Left(t) => BuiltinCommands.handleException(t, state)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
import DefaultParsers._
|
import DefaultParsers._
|
||||||
import CommandSupport._
|
import CommandStrings._
|
||||||
|
import BasicCommands._
|
||||||
|
import CommandUtil._
|
||||||
|
|
||||||
object BuiltinCommands
|
object BuiltinCommands
|
||||||
{
|
{
|
||||||
def initialAttributes = AttributeMap.empty
|
def initialAttributes = AttributeMap.empty
|
||||||
|
|
@ -140,22 +72,9 @@ object BuiltinCommands
|
||||||
def DefaultBootCommands: Seq[String] = LoadProject :: (IfLast + " " + Shell) :: Nil
|
def DefaultBootCommands: Seq[String] = LoadProject :: (IfLast + " " + Shell) :: Nil
|
||||||
|
|
||||||
def boot = Command.make(BootCommand)(bootParser)
|
def boot = Command.make(BootCommand)(bootParser)
|
||||||
def nop = Command.custom(s => success(() => s))
|
|
||||||
def ignore = Command.command(FailureWall)(idFun)
|
|
||||||
|
|
||||||
def detail(selected: Seq[String], detailMap: Map[String, String]): Seq[String] =
|
|
||||||
selected.distinct flatMap { detailMap get _ }
|
|
||||||
|
|
||||||
def help = Command.make(HelpCommand, helpBrief, helpDetailed)(helpParser)
|
|
||||||
def about = Command.command(AboutCommand, aboutBrief, aboutDetailed) { s => logger(s).info(aboutString(s)); s }
|
def about = Command.command(AboutCommand, aboutBrief, aboutDetailed) { s => logger(s).info(aboutString(s)); s }
|
||||||
|
|
||||||
def helpParser(s: State) =
|
|
||||||
{
|
|
||||||
val h = (Help.empty /: s.definedCommands)(_ ++ _.help(s))
|
|
||||||
val helpCommands = h.detail.keySet
|
|
||||||
val args = (token(Space) ~> token( NotSpace examples helpCommands )).*
|
|
||||||
applyEffect(args)(runHelp(s, h))
|
|
||||||
}
|
|
||||||
// This parser schedules the default boot commands unless overridden by an alias
|
// This parser schedules the default boot commands unless overridden by an alias
|
||||||
def bootParser(s: State) =
|
def bootParser(s: State) =
|
||||||
{
|
{
|
||||||
|
|
@ -163,16 +82,6 @@ object BuiltinCommands
|
||||||
delegateToAlias(BootCommand, success(orElse) )(s)
|
delegateToAlias(BootCommand, success(orElse) )(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
def runHelp(s: State, h: Help)(args: Seq[String]): State =
|
|
||||||
{
|
|
||||||
val message =
|
|
||||||
if(args.isEmpty)
|
|
||||||
aligned(" ", " ", h.brief).mkString("\n", "\n", "\n")
|
|
||||||
else
|
|
||||||
detail(args, h.detail) mkString("\n", "\n\n", "\n")
|
|
||||||
System.out.println(message)
|
|
||||||
s
|
|
||||||
}
|
|
||||||
def sbtVersion(s: State): String = s.configuration.provider.id.version
|
def sbtVersion(s: State): String = s.configuration.provider.id.version
|
||||||
def scalaVersion(s: State): String = s.configuration.provider.scalaProvider.version
|
def scalaVersion(s: State): String = s.configuration.provider.scalaProvider.version
|
||||||
def aboutString(s: State): String =
|
def aboutString(s: State): String =
|
||||||
|
|
@ -230,154 +139,15 @@ object BuiltinCommands
|
||||||
aligned(" ", " ", taskDetail(s)) mkString("\n", "\n", "")
|
aligned(" ", " ", taskDetail(s)) mkString("\n", "\n", "")
|
||||||
|
|
||||||
def taskStrings(key: AttributeKey[_]): Option[(String, String)] = key.description map { d => (key.label, d) }
|
def taskStrings(key: AttributeKey[_]): Option[(String, String)] = key.description map { d => (key.label, d) }
|
||||||
def aligned(pre: String, sep: String, in: Seq[(String, String)]): Seq[String] =
|
|
||||||
{
|
|
||||||
val width = in.map(_._1.length).max
|
|
||||||
in.map { case (a, b) => (" " + fill(a, width) + sep + b) }
|
|
||||||
}
|
|
||||||
def fill(s: String, size: Int) = s + " " * math.max(size - s.length, 0)
|
|
||||||
|
|
||||||
def alias = Command.make(AliasCommand, AliasBrief, AliasDetailed) { s =>
|
|
||||||
val name = token(OpOrID.examples( aliasNames(s) : _*) )
|
|
||||||
val assign = token(OptSpace ~ '=' ~ OptSpace)
|
|
||||||
val sfree = removeAliases(s)
|
|
||||||
val to = matched(sfree.combinedParser, partial = true) | any.+.string
|
|
||||||
val base = (OptSpace ~> (name ~ (assign ~> to.?).?).?)
|
|
||||||
applyEffect(base)(t => runAlias(s, t) )
|
|
||||||
}
|
|
||||||
|
|
||||||
def runAlias(s: State, args: Option[(String, Option[Option[String]])]): State =
|
|
||||||
args match
|
|
||||||
{
|
|
||||||
case None => printAliases(s); s
|
|
||||||
case Some(x ~ None) if !x.isEmpty => printAlias(s, x.trim); s
|
|
||||||
case Some(name ~ Some(None)) => removeAlias(s, name.trim)
|
|
||||||
case Some(name ~ Some(Some(value))) => addAlias(s, name.trim, value.trim)
|
|
||||||
}
|
|
||||||
|
|
||||||
def shell = Command.command(Shell, ShellBrief, ShellDetailed) { s =>
|
|
||||||
val history = (s get historyPath.key) getOrElse Some((s.baseDir / ".history").asFile)
|
|
||||||
val prompt = (s get shellPrompt.key) match { case Some(pf) => pf(s); case None => "> " }
|
|
||||||
val reader = new FullReader(history, s.combinedParser)
|
|
||||||
val line = reader.readLine(prompt)
|
|
||||||
line match {
|
|
||||||
case Some(line) =>
|
|
||||||
val newState = s.copy(onFailure = Some(Shell), remainingCommands = line +: Shell +: s.remainingCommands)
|
|
||||||
if(line.trim.isEmpty) newState else newState.clearGlobalLog
|
|
||||||
case None => s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def multiParser(s: State): Parser[Seq[String]] =
|
|
||||||
{
|
|
||||||
val nonSemi = token(charClass(_ != ';').+, hide= const(true))
|
|
||||||
( token(';' ~> OptSpace) flatMap { _ => matched((s.combinedParser&nonSemi) | nonSemi) <~ token(OptSpace) } map (_.trim) ).+
|
|
||||||
}
|
|
||||||
|
|
||||||
def multiApplied(s: State) =
|
|
||||||
Command.applyEffect( multiParser(s) )( _ ::: s )
|
|
||||||
|
|
||||||
def multi = Command.custom(multiApplied, Help(Multi, MultiBrief, MultiDetailed) )
|
|
||||||
|
|
||||||
lazy val otherCommandParser = (s: State) => token(OptSpace ~> combinedLax(s, any.+) )
|
|
||||||
def combinedLax(s: State, any: Parser[_]): Parser[String] =
|
|
||||||
matched(s.combinedParser | token(any, hide= const(true)))
|
|
||||||
|
|
||||||
def ifLast = Command(IfLast, IfLastBrief, IfLastDetailed)(otherCommandParser) { (s, arg) =>
|
|
||||||
if(s.remainingCommands.isEmpty) arg :: s else s
|
|
||||||
}
|
|
||||||
def append = Command(AppendCommand, AppendLastBrief, AppendLastDetailed)(otherCommandParser) { (s, arg) =>
|
|
||||||
s.copy(remainingCommands = s.remainingCommands :+ arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
def setOnFailure = Command(OnFailure, OnFailureBrief, OnFailureDetailed)(otherCommandParser) { (s, arg) =>
|
|
||||||
s.copy(onFailure = Some(arg))
|
|
||||||
}
|
|
||||||
def clearOnFailure = Command.command(ClearOnFailure)(s => s.copy(onFailure = None))
|
|
||||||
|
|
||||||
def reboot = Command(RebootCommand, RebootBrief, RebootDetailed)(rebootParser) { (s, full) =>
|
|
||||||
s.reboot(full)
|
|
||||||
}
|
|
||||||
def rebootParser(s: State) = token(Space ~> "full" ^^^ true) ?? false
|
|
||||||
|
|
||||||
def defaults = Command.command(DefaultsCommand) { s =>
|
def defaults = Command.command(DefaultsCommand) { s =>
|
||||||
s ++ DefaultCommands
|
s ++ DefaultCommands
|
||||||
}
|
}
|
||||||
def call = Command(ApplyCommand, ApplyBrief, ApplyDetailed)(_ => spaceDelimited("<class name>")) { (state,args) =>
|
|
||||||
val loader = getClass.getClassLoader
|
|
||||||
val loaded = args.map(arg => ModuleUtilities.getObject(arg, loader))
|
|
||||||
(state /: loaded) { case (s, obj: (State => State)) => obj(s) }
|
|
||||||
}
|
|
||||||
|
|
||||||
def initialize = Command.command(InitCommand) { s =>
|
def initialize = Command.command(InitCommand) { s =>
|
||||||
/*"load-commands -base ~/.sbt/commands" :: */readLines( readable( sbtRCs(s) ) ) ::: s
|
/*"load-commands -base ~/.sbt/commands" :: */readLines( readable( sbtRCs(s) ) ) ::: s
|
||||||
}
|
}
|
||||||
|
|
||||||
def readParser(s: State) =
|
|
||||||
{
|
|
||||||
val files = (token(Space) ~> fileParser(s.baseDir)).+
|
|
||||||
val portAndSuccess = token(OptSpace) ~> Port
|
|
||||||
portAndSuccess || files
|
|
||||||
}
|
|
||||||
|
|
||||||
def read = Command.make(ReadCommand, ReadBrief, ReadDetailed)(s => applyEffect(readParser(s))(doRead(s)) )
|
|
||||||
|
|
||||||
def doRead(s: State)(arg: Either[Int, Seq[File]]): State =
|
|
||||||
arg match
|
|
||||||
{
|
|
||||||
case Left(portAndSuccess) =>
|
|
||||||
val port = math.abs(portAndSuccess)
|
|
||||||
val previousSuccess = portAndSuccess >= 0
|
|
||||||
readMessage(port, previousSuccess) match
|
|
||||||
{
|
|
||||||
case Some(message) => (message :: (ReadCommand + " " + port) :: s).copy(onFailure = Some(ReadCommand + " " + (-port)))
|
|
||||||
case None =>
|
|
||||||
System.err.println("Connection closed.")
|
|
||||||
s.fail
|
|
||||||
}
|
|
||||||
case Right(from) =>
|
|
||||||
val notFound = notReadable(from)
|
|
||||||
if(notFound.isEmpty)
|
|
||||||
readLines(from) ::: s // this means that all commands from all files are loaded, parsed, and inserted before any are executed
|
|
||||||
else {
|
|
||||||
logger(s).error("Command file(s) not readable: \n\t" + notFound.mkString("\n\t"))
|
|
||||||
s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private def readMessage(port: Int, previousSuccess: Boolean): Option[String] =
|
|
||||||
{
|
|
||||||
// split into two connections because this first connection ends the previous communication
|
|
||||||
xsbt.IPC.client(port) { _.send(previousSuccess.toString) }
|
|
||||||
// and this second connection starts the next communication
|
|
||||||
xsbt.IPC.client(port) { ipc =>
|
|
||||||
val message = ipc.receive
|
|
||||||
if(message eq null) None else Some(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def continuous =
|
|
||||||
Command(ContinuousExecutePrefix, Help(continuousBriefHelp) )(otherCommandParser) { (s, arg) =>
|
|
||||||
withAttribute(s, Watched.Configuration, "Continuous execution not configured.") { w =>
|
|
||||||
val repeat = ContinuousExecutePrefix + (if(arg.startsWith(" ")) arg else " " + arg)
|
|
||||||
Watched.executeContinuously(w, s, arg, repeat)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def history = Command.custom(historyParser, historyHelp)
|
|
||||||
def historyParser(s: State): Parser[() => State] =
|
|
||||||
Command.applyEffect(HistoryCommands.actionParser) { histFun =>
|
|
||||||
val logError = (msg: String) => s.log.error(msg)
|
|
||||||
val hp = s get historyPath.key getOrElse None
|
|
||||||
val lines = hp.toList.flatMap( p => IO.readLines(p) ).toIndexedSeq
|
|
||||||
histFun( complete.History(lines, hp, logError) ) match
|
|
||||||
{
|
|
||||||
case Some(commands) =>
|
|
||||||
commands foreach println //printing is more appropriate than logging
|
|
||||||
(commands ::: s).continue
|
|
||||||
case None => s.fail
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def eval = Command.single(EvalCommand, evalBrief, evalDetailed) { (s, arg) =>
|
def eval = Command.single(EvalCommand, evalBrief, evalDetailed) { (s, arg) =>
|
||||||
val log = logger(s)
|
val log = logger(s)
|
||||||
val extracted = Project extract s
|
val extracted = Project extract s
|
||||||
|
|
@ -482,7 +252,7 @@ object BuiltinCommands
|
||||||
/** Determines the log file that last* commands should operate on. See also isLastOnly. */
|
/** Determines the log file that last* commands should operate on. See also isLastOnly. */
|
||||||
def lastLogFile(s: State) =
|
def lastLogFile(s: State) =
|
||||||
{
|
{
|
||||||
val backing = CommandSupport.globalLogging(s).backing
|
val backing = s.globalLogging.backing
|
||||||
if(isLastOnly(s)) backing.last else Some(backing.file)
|
if(isLastOnly(s)) backing.last else Some(backing.file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -514,7 +284,7 @@ object BuiltinCommands
|
||||||
}
|
}
|
||||||
|
|
||||||
def act = Command.customHelp(Act.actParser, actHelp)
|
def act = Command.customHelp(Act.actParser, actHelp)
|
||||||
def actHelp = (s: State) => CommandSupport.showHelp ++ keysHelp(s)
|
def actHelp = (s: State) => CommandStrings.showHelp ++ keysHelp(s)
|
||||||
def keysHelp(s: State): Help =
|
def keysHelp(s: State): Help =
|
||||||
if(Project.isProjectLoaded(s))
|
if(Project.isProjectLoaded(s))
|
||||||
Help.detailOnly(taskDetail(s))
|
Help.detailOnly(taskDetail(s))
|
||||||
|
|
@ -530,16 +300,9 @@ object BuiltinCommands
|
||||||
for( (uri, build) <- structure.units if curi != uri) listBuild(uri, build, false, cid, log)
|
for( (uri, build) <- structure.units if curi != uri) listBuild(uri, build, false, cid, log)
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
def withAttribute[T](s: State, key: AttributeKey[T], ifMissing: String)(f: T => State): State =
|
|
||||||
(s get key) match {
|
|
||||||
case None => logger(s).error(ifMissing); s.fail
|
|
||||||
case Some(nav) => f(nav)
|
|
||||||
}
|
|
||||||
|
|
||||||
def project = Command.make(ProjectCommand, projectBrief, projectDetailed)(ProjectNavigation.command)
|
def project = Command.make(ProjectCommand, projectBrief, projectDetailed)(ProjectNavigation.command)
|
||||||
|
|
||||||
def exit = Command.command(TerminateAction, exitBrief, exitBrief ) ( _ exit true )
|
|
||||||
|
|
||||||
def loadFailed = Command.command(LoadFailed)(handleLoadFailed)
|
def loadFailed = Command.command(LoadFailed)(handleLoadFailed)
|
||||||
@tailrec def handleLoadFailed(s: State): State =
|
@tailrec def handleLoadFailed(s: State): State =
|
||||||
{
|
{
|
||||||
|
|
@ -576,71 +339,4 @@ object BuiltinCommands
|
||||||
SessionSettings.checkSession(session, s)
|
SessionSettings.checkSession(session, s)
|
||||||
Project.setProject(session, structure, s)
|
Project.setProject(session, structure, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
def handleException(e: Throwable, s: State): State =
|
|
||||||
handleException(e, s, logger(s))
|
|
||||||
def handleException(e: Throwable, s: State, log: Logger): State =
|
|
||||||
{
|
|
||||||
e match
|
|
||||||
{
|
|
||||||
case _: Incomplete => () // already handled by evaluateTask
|
|
||||||
case _: NoMessageException => ()
|
|
||||||
case ite: InvocationTargetException =>
|
|
||||||
val cause = ite.getCause
|
|
||||||
if(cause == null || cause == ite) logFullException(ite, log) else handleException(cause, s, log)
|
|
||||||
case _: MessageOnlyException => log.error(e.toString)
|
|
||||||
case _: Project.Uninitialized => logFullException(e, log, true)
|
|
||||||
case _ => logFullException(e, log)
|
|
||||||
}
|
|
||||||
s.fail
|
|
||||||
}
|
|
||||||
def logFullException(e: Throwable, log: Logger, messageOnly: Boolean = false)
|
|
||||||
{
|
|
||||||
log.trace(e)
|
|
||||||
log.error(if(messageOnly) e.getMessage else ErrorHandling reducedToString e)
|
|
||||||
log.error("Use 'last' for the full log.")
|
|
||||||
}
|
|
||||||
|
|
||||||
def addAlias(s: State, name: String, value: String): State =
|
|
||||||
if(Command validID name) {
|
|
||||||
val removed = removeAlias(s, name)
|
|
||||||
if(value.isEmpty) removed else removed.copy(definedCommands = newAlias(name, value) +: removed.definedCommands)
|
|
||||||
} else {
|
|
||||||
System.err.println("Invalid alias name '" + name + "'.")
|
|
||||||
s.fail
|
|
||||||
}
|
|
||||||
|
|
||||||
def removeAliases(s: State): State = removeTagged(s, CommandAliasKey)
|
|
||||||
def removeAlias(s: State, name: String): State = s.copy(definedCommands = s.definedCommands.filter(c => !isAliasNamed(name, c)) )
|
|
||||||
|
|
||||||
def removeTagged(s: State, tag: AttributeKey[_]): State = s.copy(definedCommands = removeTagged(s.definedCommands, tag))
|
|
||||||
def removeTagged(as: Seq[Command], tag: AttributeKey[_]): Seq[Command] = as.filter(c => ! (c.tags contains tag))
|
|
||||||
|
|
||||||
def isAliasNamed(name: String, c: Command): Boolean = isNamed(name, getAlias(c))
|
|
||||||
def isNamed(name: String, alias: Option[(String,String)]): Boolean = alias match { case None => false; case Some((n,_)) => name == n }
|
|
||||||
|
|
||||||
def getAlias(c: Command): Option[(String,String)] = c.tags get CommandAliasKey
|
|
||||||
def printAlias(s: State, name: String): Unit = printAliases(aliases(s,(n,v) => n == name) )
|
|
||||||
def printAliases(s: State): Unit = printAliases(allAliases(s))
|
|
||||||
def printAliases(as: Seq[(String,String)]): Unit =
|
|
||||||
for( (name,value) <- as)
|
|
||||||
println("\t" + name + " = " + value)
|
|
||||||
|
|
||||||
def aliasNames(s: State): Seq[String] = allAliases(s).map(_._1)
|
|
||||||
def allAliases(s: State): Seq[(String,String)] = aliases(s, (n,v) => true)
|
|
||||||
def aliases(s: State, pred: (String,String) => Boolean): Seq[(String,String)] =
|
|
||||||
s.definedCommands.flatMap(c => getAlias(c).filter(tupled(pred)))
|
|
||||||
|
|
||||||
def newAlias(name: String, value: String): Command =
|
|
||||||
Command.make(name, (name, "'" + value + "'"), "Alias of '" + value + "'")(aliasBody(name, value)).tag(CommandAliasKey, (name, value))
|
|
||||||
def aliasBody(name: String, value: String)(state: State): Parser[() => State] =
|
|
||||||
OptSpace ~> Parser(Command.combine(removeAlias(state,name).definedCommands)(state))(value)
|
|
||||||
|
|
||||||
def delegateToAlias(name: String, orElse: Parser[() => State])(state: State): Parser[() => State] =
|
|
||||||
aliases(state, (nme,_) => nme == name).headOption match {
|
|
||||||
case None => orElse
|
|
||||||
case Some((n,v)) => aliasBody(n,v)(state)
|
|
||||||
}
|
|
||||||
|
|
||||||
val CommandAliasKey = AttributeKey[(String,String)]("is-command-alias", "Internal: marker for Commands created as aliases for another command.")
|
|
||||||
}
|
}
|
||||||
|
|
@ -203,7 +203,7 @@ object Project extends Init[Scope] with ProjectExtra
|
||||||
val prompt = get(shellPrompt)
|
val prompt = get(shellPrompt)
|
||||||
val watched = get(watch)
|
val watched = get(watch)
|
||||||
val commandDefs = allCommands.distinct.flatten[Command].map(_ tag (projectCommand, true))
|
val commandDefs = allCommands.distinct.flatten[Command].map(_ tag (projectCommand, true))
|
||||||
val newDefinedCommands = commandDefs ++ BuiltinCommands.removeTagged(s.definedCommands, projectCommand)
|
val newDefinedCommands = commandDefs ++ BasicCommands.removeTagged(s.definedCommands, projectCommand)
|
||||||
val newAttrs = setCond(Watched.Configuration, watched, s.attributes).put(historyPath.key, history)
|
val newAttrs = setCond(Watched.Configuration, watched, s.attributes).put(historyPath.key, history)
|
||||||
s.copy(attributes = setCond(shellPrompt.key, prompt, newAttrs), definedCommands = newDefinedCommands)
|
s.copy(attributes = setCond(shellPrompt.key, prompt, newAttrs), definedCommands = newDefinedCommands)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ object Script
|
||||||
val scriptArg = state.remainingCommands.headOption getOrElse error("No script file specified")
|
val scriptArg = state.remainingCommands.headOption getOrElse error("No script file specified")
|
||||||
val script = new File(scriptArg).getAbsoluteFile
|
val script = new File(scriptArg).getAbsoluteFile
|
||||||
val hash = Hash.halve(Hash.toHex(Hash(script.getAbsolutePath)))
|
val hash = Hash.halve(Hash.toHex(Hash(script.getAbsolutePath)))
|
||||||
val base = new File(CommandSupport.bootDirectory(state), hash)
|
val base = new File(CommandUtil.bootDirectory(state), hash)
|
||||||
IO.createDirectory(base)
|
IO.createDirectory(base)
|
||||||
|
|
||||||
val (eval, structure) = Load.defaultLoad(state, base, state.log)
|
val (eval, structure) = Load.defaultLoad(state, base, state.log)
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,6 @@ object TaskData
|
||||||
private[this] def fakeState(structure: BuildStructure): State =
|
private[this] def fakeState(structure: BuildStructure): State =
|
||||||
{
|
{
|
||||||
val config = Keys.appConfiguration in Scope.GlobalScope get structure.data
|
val config = Keys.appConfiguration in Scope.GlobalScope get structure.data
|
||||||
State(config.get, Nil, Set.empty, None, Nil, State.newHistory, AttributeMap.empty, State.Continue)
|
State(config.get, Nil, Set.empty, None, Nil, State.newHistory, AttributeMap.empty, StandardMain.initialGlobalLogging, State.Continue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
/* sbt -- Simple Build Tool
|
||||||
|
* Copyright 2010 Mark Harrah
|
||||||
|
*/
|
||||||
|
package sbt
|
||||||
|
|
||||||
|
import complete.HistoryCommands
|
||||||
|
import scala.annotation.tailrec
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import Path._
|
||||||
|
|
||||||
|
object BasicCommandStrings
|
||||||
|
{
|
||||||
|
val HelpCommand = "help"
|
||||||
|
val Exit = "exit"
|
||||||
|
val Quit = "quit"
|
||||||
|
|
||||||
|
/** The command name to terminate the program.*/
|
||||||
|
val TerminateAction: String = Exit
|
||||||
|
|
||||||
|
def helpBrief = (HelpCommand + " [command]*", "Displays this help message or prints detailed help on requested commands.")
|
||||||
|
def helpDetailed = """
|
||||||
|
If an argument is provided, this prints detailed help for that command.
|
||||||
|
Otherwise, this prints a help summary."""
|
||||||
|
|
||||||
|
def historyHelp = Help.briefDetail(HistoryCommands.descriptions)
|
||||||
|
|
||||||
|
def exitBrief = "Terminates the build."
|
||||||
|
|
||||||
|
def ReadCommand = "<"
|
||||||
|
def ReadFiles = " file1 file2 ..."
|
||||||
|
def ReadBrief = (ReadCommand + " <file>*", "Reads command lines from the provided files.")
|
||||||
|
def ReadDetailed =
|
||||||
|
ReadCommand + ReadFiles + """
|
||||||
|
|
||||||
|
Reads the lines from the given files and inserts them as commands.
|
||||||
|
All empty lines and lines that start with '#' are ignored.
|
||||||
|
If a file does not exist or is not readable, this command fails.
|
||||||
|
|
||||||
|
All the lines from all the files are read before any of the commands
|
||||||
|
are executed. Thus, if any file is not readable, none of commands
|
||||||
|
from any of the files (even the existing ones) will be run.
|
||||||
|
|
||||||
|
You probably need to escape this command if entering it at your shell."""
|
||||||
|
|
||||||
|
def ApplyCommand = "apply"
|
||||||
|
def ApplyBrief = (ApplyCommand + " <module-name>*", ApplyDetailed)
|
||||||
|
def ApplyDetailed = "Transforms the current State by calling <module-name>.apply(currentState) for each listed module name."
|
||||||
|
|
||||||
|
def RebootCommand = "reboot"
|
||||||
|
def RebootSummary = RebootCommand + " [full]"
|
||||||
|
def RebootBrief = (RebootSummary, "Reboots sbt and then executes the remaining commands.")
|
||||||
|
def RebootDetailed =
|
||||||
|
RebootSummary + """
|
||||||
|
|
||||||
|
This command is equivalent to exiting sbt, restarting, and running the
|
||||||
|
remaining commands with the exception that the JVM is not shut down.
|
||||||
|
|
||||||
|
If 'full' is specified, the boot directory (`~/.sbt/boot` by default)
|
||||||
|
is deleted before restarting. This forces an update of sbt and Scala
|
||||||
|
and is useful when working with development versions of sbt or Scala."""
|
||||||
|
|
||||||
|
def Multi = ";"
|
||||||
|
def MultiBrief = (Multi + " <command> (" + Multi + " <command>)*", "Runs the provided semicolon-separated commands.")
|
||||||
|
def MultiDetailed =
|
||||||
|
Multi + " command1 " + Multi + """ command2 ...
|
||||||
|
|
||||||
|
Runs the specified commands."""
|
||||||
|
|
||||||
|
def AppendCommand = "append"
|
||||||
|
def AppendLastBrief = (AppendCommand + " <command>", AppendLastDetailed)
|
||||||
|
def AppendLastDetailed = "Appends 'command' to list of commands to run."
|
||||||
|
|
||||||
|
val AliasCommand = "alias"
|
||||||
|
def AliasBrief = (AliasCommand, "Adds, removes, or prints command aliases.")
|
||||||
|
def AliasDetailed =
|
||||||
|
AliasCommand + """
|
||||||
|
|
||||||
|
Prints a list of defined aliases.
|
||||||
|
|
||||||
|
""" +
|
||||||
|
AliasCommand + """ name
|
||||||
|
|
||||||
|
Prints the alias defined for `name`.
|
||||||
|
|
||||||
|
""" +
|
||||||
|
AliasCommand + """ name=value
|
||||||
|
|
||||||
|
Sets the alias `name` to `value`, replacing any existing alias with that name.
|
||||||
|
Whenever `name` is entered, the corresponding `value` is run.
|
||||||
|
If any argument is provided to `name`, it is appended as argument to `value`.
|
||||||
|
|
||||||
|
""" +
|
||||||
|
AliasCommand + """ name=
|
||||||
|
|
||||||
|
Removes the alias for `name`."""
|
||||||
|
|
||||||
|
def Shell = "shell"
|
||||||
|
def ShellBrief = ShellDetailed
|
||||||
|
def ShellDetailed = "Provides an interactive prompt from which commands can be run."
|
||||||
|
|
||||||
|
def ClearOnFailure = "--"
|
||||||
|
def OnFailure = "-"
|
||||||
|
def OnFailureBrief = (OnFailure + " command", "Registers 'command' to run if a command fails.")
|
||||||
|
def OnFailureDetailed =
|
||||||
|
OnFailure + """ command
|
||||||
|
|
||||||
|
Registers 'command' to run when a command fails to complete normally.
|
||||||
|
|
||||||
|
Only one failure command may be registered at a time, so this command
|
||||||
|
replaces the previous command if there is one.
|
||||||
|
|
||||||
|
The failure command resets when it runs once, so it must be added
|
||||||
|
again if desired."""
|
||||||
|
|
||||||
|
def IfLast = "iflast"
|
||||||
|
def IfLastBrief = (IfLast + " <command>", IfLastCommon)
|
||||||
|
def IfLastCommon = "If there are no more commands after this one, 'command' is run."
|
||||||
|
def IfLastDetailed =
|
||||||
|
IfLast + """ command
|
||||||
|
|
||||||
|
""" + IfLastCommon
|
||||||
|
|
||||||
|
val ContinuousExecutePrefix = "~"
|
||||||
|
def continuousBriefHelp = (ContinuousExecutePrefix + " <command>", "Executes the specified command whenever source files change.")
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,223 @@
|
||||||
|
package sbt
|
||||||
|
|
||||||
|
import complete.{DefaultParsers, HistoryCommands, Parser}
|
||||||
|
import DefaultParsers._
|
||||||
|
import Types.{const,idFun}
|
||||||
|
import Function.tupled
|
||||||
|
import Command.applyEffect
|
||||||
|
import State.FailureWall
|
||||||
|
import HistoryCommands.{Start => HistoryPrefix}
|
||||||
|
import BasicCommandStrings._
|
||||||
|
import CommandUtil._
|
||||||
|
import BasicKeys._
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
object BasicCommands
|
||||||
|
{
|
||||||
|
lazy val allBasicCommands = Seq(nop, ignore, help, multi, ifLast, append, setOnFailure, clearOnFailure, reboot, call, exit, continuous, history, shell, read, alias)
|
||||||
|
|
||||||
|
def nop = Command.custom(s => success(() => s))
|
||||||
|
def ignore = Command.command(FailureWall)(idFun)
|
||||||
|
|
||||||
|
def help = Command.make(HelpCommand, helpBrief, helpDetailed)(helpParser)
|
||||||
|
|
||||||
|
def helpParser(s: State) =
|
||||||
|
{
|
||||||
|
val h = (Help.empty /: s.definedCommands)(_ ++ _.help(s))
|
||||||
|
val helpCommands = h.detail.keySet
|
||||||
|
val args = (token(Space) ~> token( NotSpace examples helpCommands )).*
|
||||||
|
applyEffect(args)(runHelp(s, h))
|
||||||
|
}
|
||||||
|
|
||||||
|
def runHelp(s: State, h: Help)(args: Seq[String]): State =
|
||||||
|
{
|
||||||
|
val message =
|
||||||
|
if(args.isEmpty)
|
||||||
|
aligned(" ", " ", h.brief).mkString("\n", "\n", "\n")
|
||||||
|
else
|
||||||
|
detail(args, h.detail) mkString("\n", "\n\n", "\n")
|
||||||
|
System.out.println(message)
|
||||||
|
s
|
||||||
|
}
|
||||||
|
def detail(selected: Seq[String], detailMap: Map[String, String]): Seq[String] =
|
||||||
|
selected.distinct flatMap { detailMap get _ }
|
||||||
|
|
||||||
|
def multiParser(s: State): Parser[Seq[String]] =
|
||||||
|
{
|
||||||
|
val nonSemi = token(charClass(_ != ';').+, hide= const(true))
|
||||||
|
( token(';' ~> OptSpace) flatMap { _ => matched((s.combinedParser&nonSemi) | nonSemi) <~ token(OptSpace) } map (_.trim) ).+
|
||||||
|
}
|
||||||
|
|
||||||
|
def multiApplied(s: State) =
|
||||||
|
Command.applyEffect( multiParser(s) )( _ ::: s )
|
||||||
|
|
||||||
|
def multi = Command.custom(multiApplied, Help(Multi, MultiBrief, MultiDetailed) )
|
||||||
|
|
||||||
|
lazy val otherCommandParser = (s: State) => token(OptSpace ~> combinedLax(s, any.+) )
|
||||||
|
def combinedLax(s: State, any: Parser[_]): Parser[String] =
|
||||||
|
matched(s.combinedParser | token(any, hide= const(true)))
|
||||||
|
|
||||||
|
def ifLast = Command(IfLast, IfLastBrief, IfLastDetailed)(otherCommandParser) { (s, arg) =>
|
||||||
|
if(s.remainingCommands.isEmpty) arg :: s else s
|
||||||
|
}
|
||||||
|
def append = Command(AppendCommand, AppendLastBrief, AppendLastDetailed)(otherCommandParser) { (s, arg) =>
|
||||||
|
s.copy(remainingCommands = s.remainingCommands :+ arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
def setOnFailure = Command(OnFailure, OnFailureBrief, OnFailureDetailed)(otherCommandParser) { (s, arg) =>
|
||||||
|
s.copy(onFailure = Some(arg))
|
||||||
|
}
|
||||||
|
def clearOnFailure = Command.command(ClearOnFailure)(s => s.copy(onFailure = None))
|
||||||
|
|
||||||
|
def reboot = Command(RebootCommand, RebootBrief, RebootDetailed)(rebootParser) { (s, full) =>
|
||||||
|
s.reboot(full)
|
||||||
|
}
|
||||||
|
def rebootParser(s: State) = token(Space ~> "full" ^^^ true) ?? false
|
||||||
|
|
||||||
|
def call = Command(ApplyCommand, ApplyBrief, ApplyDetailed)(_ => spaceDelimited("<class name>")) { (state,args) =>
|
||||||
|
val loader = getClass.getClassLoader
|
||||||
|
val loaded = args.map(arg => ModuleUtilities.getObject(arg, loader))
|
||||||
|
(state /: loaded) { case (s, obj: (State => State)) => obj(s) }
|
||||||
|
}
|
||||||
|
|
||||||
|
def exit = Command.command(TerminateAction, exitBrief, exitBrief ) ( _ exit true )
|
||||||
|
|
||||||
|
|
||||||
|
def continuous =
|
||||||
|
Command(ContinuousExecutePrefix, Help(continuousBriefHelp) )(otherCommandParser) { (s, arg) =>
|
||||||
|
withAttribute(s, Watched.Configuration, "Continuous execution not configured.") { w =>
|
||||||
|
val repeat = ContinuousExecutePrefix + (if(arg.startsWith(" ")) arg else " " + arg)
|
||||||
|
Watched.executeContinuously(w, s, arg, repeat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def history = Command.custom(historyParser, BasicCommandStrings.historyHelp)
|
||||||
|
def historyParser(s: State): Parser[() => State] =
|
||||||
|
Command.applyEffect(HistoryCommands.actionParser) { histFun =>
|
||||||
|
val logError = (msg: String) => s.log.error(msg)
|
||||||
|
val hp = s get historyPath getOrElse None
|
||||||
|
val lines = hp.toList.flatMap( p => IO.readLines(p) ).toIndexedSeq
|
||||||
|
histFun( complete.History(lines, hp, logError) ) match
|
||||||
|
{
|
||||||
|
case Some(commands) =>
|
||||||
|
commands foreach println //printing is more appropriate than logging
|
||||||
|
(commands ::: s).continue
|
||||||
|
case None => s.fail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def shell = Command.command(Shell, ShellBrief, ShellDetailed) { s =>
|
||||||
|
val history = (s get historyPath) getOrElse Some(new File(s.baseDir, ".history"))
|
||||||
|
val prompt = (s get shellPrompt) match { case Some(pf) => pf(s); case None => "> " }
|
||||||
|
val reader = new FullReader(history, s.combinedParser)
|
||||||
|
val line = reader.readLine(prompt)
|
||||||
|
line match {
|
||||||
|
case Some(line) =>
|
||||||
|
val newState = s.copy(onFailure = Some(Shell), remainingCommands = line +: Shell +: s.remainingCommands)
|
||||||
|
if(line.trim.isEmpty) newState else newState.clearGlobalLog
|
||||||
|
case None => s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def read = Command.make(ReadCommand, ReadBrief, ReadDetailed)(s => applyEffect(readParser(s))(doRead(s)) )
|
||||||
|
def readParser(s: State) =
|
||||||
|
{
|
||||||
|
val files = (token(Space) ~> fileParser(s.baseDir)).+
|
||||||
|
val portAndSuccess = token(OptSpace) ~> Port
|
||||||
|
portAndSuccess || files
|
||||||
|
}
|
||||||
|
def doRead(s: State)(arg: Either[Int, Seq[File]]): State =
|
||||||
|
arg match
|
||||||
|
{
|
||||||
|
case Left(portAndSuccess) =>
|
||||||
|
val port = math.abs(portAndSuccess)
|
||||||
|
val previousSuccess = portAndSuccess >= 0
|
||||||
|
readMessage(port, previousSuccess) match
|
||||||
|
{
|
||||||
|
case Some(message) => (message :: (ReadCommand + " " + port) :: s).copy(onFailure = Some(ReadCommand + " " + (-port)))
|
||||||
|
case None =>
|
||||||
|
System.err.println("Connection closed.")
|
||||||
|
s.fail
|
||||||
|
}
|
||||||
|
case Right(from) =>
|
||||||
|
val notFound = notReadable(from)
|
||||||
|
if(notFound.isEmpty)
|
||||||
|
readLines(from) ::: s // this means that all commands from all files are loaded, parsed, and inserted before any are executed
|
||||||
|
else {
|
||||||
|
s.log.error("Command file(s) not readable: \n\t" + notFound.mkString("\n\t"))
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private def readMessage(port: Int, previousSuccess: Boolean): Option[String] =
|
||||||
|
{
|
||||||
|
// split into two connections because this first connection ends the previous communication
|
||||||
|
xsbt.IPC.client(port) { _.send(previousSuccess.toString) }
|
||||||
|
// and this second connection starts the next communication
|
||||||
|
xsbt.IPC.client(port) { ipc =>
|
||||||
|
val message = ipc.receive
|
||||||
|
if(message eq null) None else Some(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def alias = Command.make(AliasCommand, AliasBrief, AliasDetailed) { s =>
|
||||||
|
val name = token(OpOrID.examples( aliasNames(s) : _*) )
|
||||||
|
val assign = token(OptSpace ~ '=' ~ OptSpace)
|
||||||
|
val sfree = removeAliases(s)
|
||||||
|
val to = matched(sfree.combinedParser, partial = true) | any.+.string
|
||||||
|
val base = (OptSpace ~> (name ~ (assign ~> to.?).?).?)
|
||||||
|
applyEffect(base)(t => runAlias(s, t) )
|
||||||
|
}
|
||||||
|
|
||||||
|
def runAlias(s: State, args: Option[(String, Option[Option[String]])]): State =
|
||||||
|
args match
|
||||||
|
{
|
||||||
|
case None => printAliases(s); s
|
||||||
|
case Some(x ~ None) if !x.isEmpty => printAlias(s, x.trim); s
|
||||||
|
case Some(name ~ Some(None)) => removeAlias(s, name.trim)
|
||||||
|
case Some(name ~ Some(Some(value))) => addAlias(s, name.trim, value.trim)
|
||||||
|
}
|
||||||
|
def addAlias(s: State, name: String, value: String): State =
|
||||||
|
if(Command validID name) {
|
||||||
|
val removed = removeAlias(s, name)
|
||||||
|
if(value.isEmpty) removed else removed.copy(definedCommands = newAlias(name, value) +: removed.definedCommands)
|
||||||
|
} else {
|
||||||
|
System.err.println("Invalid alias name '" + name + "'.")
|
||||||
|
s.fail
|
||||||
|
}
|
||||||
|
|
||||||
|
def removeAliases(s: State): State = removeTagged(s, CommandAliasKey)
|
||||||
|
def removeAlias(s: State, name: String): State = s.copy(definedCommands = s.definedCommands.filter(c => !isAliasNamed(name, c)) )
|
||||||
|
|
||||||
|
def removeTagged(s: State, tag: AttributeKey[_]): State = s.copy(definedCommands = removeTagged(s.definedCommands, tag))
|
||||||
|
def removeTagged(as: Seq[Command], tag: AttributeKey[_]): Seq[Command] = as.filter(c => ! (c.tags contains tag))
|
||||||
|
|
||||||
|
def isAliasNamed(name: String, c: Command): Boolean = isNamed(name, getAlias(c))
|
||||||
|
def isNamed(name: String, alias: Option[(String,String)]): Boolean = alias match { case None => false; case Some((n,_)) => name == n }
|
||||||
|
|
||||||
|
def getAlias(c: Command): Option[(String,String)] = c.tags get CommandAliasKey
|
||||||
|
def printAlias(s: State, name: String): Unit = printAliases(aliases(s,(n,v) => n == name) )
|
||||||
|
def printAliases(s: State): Unit = printAliases(allAliases(s))
|
||||||
|
def printAliases(as: Seq[(String,String)]): Unit =
|
||||||
|
for( (name,value) <- as)
|
||||||
|
println("\t" + name + " = " + value)
|
||||||
|
|
||||||
|
def aliasNames(s: State): Seq[String] = allAliases(s).map(_._1)
|
||||||
|
def allAliases(s: State): Seq[(String,String)] = aliases(s, (n,v) => true)
|
||||||
|
def aliases(s: State, pred: (String,String) => Boolean): Seq[(String,String)] =
|
||||||
|
s.definedCommands.flatMap(c => getAlias(c).filter(tupled(pred)))
|
||||||
|
|
||||||
|
def newAlias(name: String, value: String): Command =
|
||||||
|
Command.make(name, (name, "'" + value + "'"), "Alias of '" + value + "'")(aliasBody(name, value)).tag(CommandAliasKey, (name, value))
|
||||||
|
def aliasBody(name: String, value: String)(state: State): Parser[() => State] =
|
||||||
|
OptSpace ~> Parser(Command.combine(removeAlias(state,name).definedCommands)(state))(value)
|
||||||
|
|
||||||
|
def delegateToAlias(name: String, orElse: Parser[() => State])(state: State): Parser[() => State] =
|
||||||
|
aliases(state, (nme,_) => nme == name).headOption match {
|
||||||
|
case None => orElse
|
||||||
|
case Some((n,v)) => aliasBody(n,v)(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
val CommandAliasKey = AttributeKey[(String,String)]("is-command-alias", "Internal: marker for Commands created as aliases for another command.")
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
package sbt
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
object BasicKeys
|
||||||
|
{
|
||||||
|
val historyPath = AttributeKey[Option[File]]("history", "The location where command line history is persisted.")
|
||||||
|
val shellPrompt = AttributeKey[State => String]("shell-prompt", "The function that constructs the command prompt from the current build state.")
|
||||||
|
val watch = AttributeKey[Watched]("watch", "Continuous execution configuration.")
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package sbt
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
object CommandUtil
|
||||||
|
{
|
||||||
|
def readLines(files: Seq[File]): Seq[String] = files flatMap (line => IO.readLines(line)) flatMap processLine
|
||||||
|
def processLine(s: String) = { val trimmed = s.trim; if(ignoreLine(trimmed)) None else Some(trimmed) }
|
||||||
|
def ignoreLine(s: String) = s.isEmpty || s.startsWith("#")
|
||||||
|
|
||||||
|
private def canRead = (_: File).canRead
|
||||||
|
def notReadable(files: Seq[File]): Seq[File] = files filterNot canRead
|
||||||
|
def readable(files: Seq[File]): Seq[File] = files filter canRead
|
||||||
|
|
||||||
|
// slightly better fallback in case of older launcher
|
||||||
|
def bootDirectory(state: State): File =
|
||||||
|
try { state.configuration.provider.scalaProvider.launcher.bootDirectory }
|
||||||
|
catch { case e: NoSuchMethodError => new File(".").getAbsoluteFile }
|
||||||
|
|
||||||
|
def aligned(pre: String, sep: String, in: Seq[(String, String)]): Seq[String] =
|
||||||
|
{
|
||||||
|
val width = in.map(_._1.length).max
|
||||||
|
in.map { case (a, b) => (" " + fill(a, width) + sep + b) }
|
||||||
|
}
|
||||||
|
def fill(s: String, size: Int) = s + " " * math.max(size - s.length, 0)
|
||||||
|
|
||||||
|
def withAttribute[T](s: State, key: AttributeKey[T], ifMissing: String)(f: T => State): State =
|
||||||
|
(s get key) match {
|
||||||
|
case None => s.log.error(ifMissing); s.fail
|
||||||
|
case Some(nav) => f(nav)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
/* sbt -- Simple Build Tool
|
||||||
|
* Copyright 2008, 2009, 2010, 2011 Mark Harrah
|
||||||
|
*/
|
||||||
|
package sbt
|
||||||
|
|
||||||
|
import scala.annotation.tailrec
|
||||||
|
import java.io.{File, PrintWriter}
|
||||||
|
import java.lang.reflect.InvocationTargetException
|
||||||
|
|
||||||
|
object MainLoop
|
||||||
|
{
|
||||||
|
/** Entry point to run the remaining commands in State with managed global logging.*/
|
||||||
|
def runLogged(state: State): xsbti.MainResult =
|
||||||
|
runLoggedLoop(state, state.globalLogging.backing)
|
||||||
|
|
||||||
|
/** Run loop that evaluates remaining commands and manages changes to global logging configuration.*/
|
||||||
|
@tailrec def runLoggedLoop(state: State, logBacking: GlobalLogBacking): xsbti.MainResult =
|
||||||
|
runAndClearLast(state, logBacking) match {
|
||||||
|
case ret: Return => // delete current and last log files when exiting normally
|
||||||
|
logBacking.file.delete()
|
||||||
|
deleteLastLog(logBacking)
|
||||||
|
ret.result
|
||||||
|
case clear: ClearGlobalLog => // delete previous log file, move current to previous, and start writing to a new file
|
||||||
|
deleteLastLog(logBacking)
|
||||||
|
runLoggedLoop(clear.state, logBacking.shiftNew())
|
||||||
|
case keep: KeepGlobalLog => // make previous log file the current log file
|
||||||
|
logBacking.file.delete
|
||||||
|
runLoggedLoop(keep.state, logBacking.unshift)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Runs the next sequence of commands, cleaning up global logging after any exceptions. */
|
||||||
|
def runAndClearLast(state: State, logBacking: GlobalLogBacking): RunNext =
|
||||||
|
try
|
||||||
|
runWithNewLog(state, logBacking)
|
||||||
|
catch {
|
||||||
|
case e: xsbti.FullReload =>
|
||||||
|
deleteLastLog(logBacking)
|
||||||
|
throw e // pass along a reboot request
|
||||||
|
case e =>
|
||||||
|
System.err.println("sbt appears to be exiting abnormally.\n The log file for this session is at " + logBacking.file)
|
||||||
|
deleteLastLog(logBacking)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Deletes the previous global log file. */
|
||||||
|
def deleteLastLog(logBacking: GlobalLogBacking): Unit =
|
||||||
|
logBacking.last.foreach(_.delete())
|
||||||
|
|
||||||
|
/** Runs the next sequence of commands with global logging in place. */
|
||||||
|
def runWithNewLog(state: State, logBacking: GlobalLogBacking): RunNext =
|
||||||
|
Using.fileWriter(append = true)(logBacking.file) { writer =>
|
||||||
|
val out = new java.io.PrintWriter(writer)
|
||||||
|
val loggedState = state.copy(globalLogging = logBacking.newLogger(out, logBacking))
|
||||||
|
try run(loggedState) finally out.close()
|
||||||
|
}
|
||||||
|
sealed trait RunNext
|
||||||
|
final class ClearGlobalLog(val state: State) extends RunNext
|
||||||
|
final class KeepGlobalLog(val state: State) extends RunNext
|
||||||
|
final class Return(val result: xsbti.MainResult) extends RunNext
|
||||||
|
|
||||||
|
/** Runs the next sequence of commands that doesn't require global logging changes.*/
|
||||||
|
@tailrec def run(state: State): RunNext =
|
||||||
|
state.next match
|
||||||
|
{
|
||||||
|
case State.Continue => run(next(state))
|
||||||
|
case State.ClearGlobalLog => new ClearGlobalLog(state.continue)
|
||||||
|
case State.KeepLastLog => new KeepGlobalLog(state.continue)
|
||||||
|
case ret: State.Return => new Return(ret.result)
|
||||||
|
}
|
||||||
|
|
||||||
|
def next(state: State): State =
|
||||||
|
ErrorHandling.wideConvert { state.process(Command.process) } match
|
||||||
|
{
|
||||||
|
case Right(s) => s
|
||||||
|
case Left(t: xsbti.FullReload) => throw t
|
||||||
|
case Left(t) => handleException(t, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
def handleException(e: Throwable, s: State): State =
|
||||||
|
handleException(e, s, s.log)
|
||||||
|
def handleException(e: Throwable, s: State, log: Logger): State =
|
||||||
|
{
|
||||||
|
e match
|
||||||
|
{
|
||||||
|
case _: AlreadyHandledException | _: UnprintableException => ()
|
||||||
|
case ite: InvocationTargetException =>
|
||||||
|
val cause = ite.getCause
|
||||||
|
if(cause == null || cause == ite) logFullException(ite, log) else handleException(cause, s, log)
|
||||||
|
case _: MessageOnlyException => log.error(e.toString)
|
||||||
|
case _ => logFullException(e, log)
|
||||||
|
}
|
||||||
|
s.fail
|
||||||
|
}
|
||||||
|
def logFullException(e: Throwable, log: Logger)
|
||||||
|
{
|
||||||
|
log.trace(e)
|
||||||
|
log.error(ErrorHandling reducedToString e)
|
||||||
|
log.error("Use 'last' for the full log.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,6 @@ package sbt
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.concurrent.Callable
|
import java.util.concurrent.Callable
|
||||||
import CommandSupport.{FailureWall, logger}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Data structure representing all command execution information.
|
Data structure representing all command execution information.
|
||||||
|
|
@ -27,6 +26,7 @@ final case class State(
|
||||||
remainingCommands: Seq[String],
|
remainingCommands: Seq[String],
|
||||||
history: State.History,
|
history: State.History,
|
||||||
attributes: AttributeMap,
|
attributes: AttributeMap,
|
||||||
|
globalLogging: GlobalLogging,
|
||||||
next: State.Next
|
next: State.Next
|
||||||
) extends Identity {
|
) extends Identity {
|
||||||
lazy val combinedParser = Command.combine(definedCommands)(this)
|
lazy val combinedParser = Command.combine(definedCommands)(this)
|
||||||
|
|
@ -112,6 +112,8 @@ trait StateOps {
|
||||||
|
|
||||||
object State
|
object State
|
||||||
{
|
{
|
||||||
|
final val FailureWall = "---"
|
||||||
|
|
||||||
/** Represents the next action for the command processor.*/
|
/** Represents the next action for the command processor.*/
|
||||||
sealed trait Next
|
sealed trait Next
|
||||||
/** Indicates that the command processor should process the next command.*/
|
/** Indicates that the command processor should process the next command.*/
|
||||||
|
|
@ -175,7 +177,7 @@ object State
|
||||||
def update[T](key: AttributeKey[T])(f: Option[T] => T): State = put(key, f(get(key)))
|
def update[T](key: AttributeKey[T])(f: Option[T] => T): State = put(key, f(get(key)))
|
||||||
def has(key: AttributeKey[_]) = s.attributes contains key
|
def has(key: AttributeKey[_]) = s.attributes contains key
|
||||||
def remove(key: AttributeKey[_]) = s.copy(attributes = s.attributes remove key)
|
def remove(key: AttributeKey[_]) = s.copy(attributes = s.attributes remove key)
|
||||||
def log = CommandSupport.logger(s)
|
def log = s.globalLogging.full
|
||||||
def fail =
|
def fail =
|
||||||
{
|
{
|
||||||
val remaining = s.remainingCommands.dropWhile(_ != FailureWall)
|
val remaining = s.remainingCommands.dropWhile(_ != FailureWall)
|
||||||
|
|
@ -3,7 +3,8 @@
|
||||||
*/
|
*/
|
||||||
package sbt
|
package sbt
|
||||||
|
|
||||||
import CommandSupport.{ClearOnFailure,FailureWall}
|
import BasicCommandStrings.ClearOnFailure
|
||||||
|
import State.FailureWall
|
||||||
import annotation.tailrec
|
import annotation.tailrec
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import Types.const
|
import Types.const
|
||||||
|
|
@ -63,7 +64,7 @@ object Watched
|
||||||
catch { case e: Exception =>
|
catch { case e: Exception =>
|
||||||
val log = s.log
|
val log = s.log
|
||||||
log.error("Error occurred obtaining files to watch. Terminating continuous execution...")
|
log.error("Error occurred obtaining files to watch. Terminating continuous execution...")
|
||||||
BuiltinCommands.handleException(e, s, log)
|
MainLoop.handleException(e, s, log)
|
||||||
(false, watchState, s.fail)
|
(false, watchState, s.fail)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,8 +111,11 @@ object Sbt extends Build
|
||||||
classfileSub, classpathSub, compileIncrementalSub, compilePersistSub, compilerSub, completeSub, apiSub,
|
classfileSub, classpathSub, compileIncrementalSub, compilePersistSub, compilerSub, completeSub, apiSub,
|
||||||
interfaceSub, ioSub, ivySub, logSub, processSub, runSub, stdTaskSub, taskSub, trackingSub, testingSub)
|
interfaceSub, ioSub, ivySub, logSub, processSub, runSub, stdTaskSub, taskSub, trackingSub, testingSub)
|
||||||
|
|
||||||
|
lazy val commandSub = testedBaseProject(commandPath, "Command") dependsOn(interfaceSub, ioSub, launchInterfaceSub, logSub, completeSub, classpathSub)
|
||||||
|
|
||||||
// The main integration project for sbt. It brings all of the subsystems together, configures them, and provides for overriding conventions.
|
// The main integration project for sbt. It brings all of the subsystems together, configures them, and provides for overriding conventions.
|
||||||
lazy val mainSub = testedBaseProject(mainPath, "Main") dependsOn(actionsSub, interfaceSub, ioSub, ivySub, launchInterfaceSub, logSub, processSub, runSub)
|
lazy val mainSub = testedBaseProject(mainPath, "Main") dependsOn(actionsSub, interfaceSub, ioSub, ivySub, launchInterfaceSub, logSub, processSub, runSub, commandSub)
|
||||||
|
|
||||||
// Strictly for bringing implicits and aliases from subsystems into the top-level sbt namespace through a single package object
|
// Strictly for bringing implicits and aliases from subsystems into the top-level sbt namespace through a single package object
|
||||||
// technically, we need a dependency on all of mainSub's dependencies, but we don't do that since this is strictly an integration project
|
// technically, we need a dependency on all of mainSub's dependencies, but we don't do that since this is strictly an integration project
|
||||||
// with the sole purpose of providing certain identifiers without qualification (with a package object)
|
// with the sole purpose of providing certain identifiers without qualification (with a package object)
|
||||||
|
|
@ -126,6 +129,7 @@ object Sbt extends Build
|
||||||
def utilPath = file("util")
|
def utilPath = file("util")
|
||||||
def compilePath = file("compile")
|
def compilePath = file("compile")
|
||||||
def mainPath = file("main")
|
def mainPath = file("main")
|
||||||
|
def commandPath = mainPath / "command"
|
||||||
def scriptedPath = file("scripted")
|
def scriptedPath = file("scripted")
|
||||||
|
|
||||||
def sbtSettings = Seq(
|
def sbtSettings = Seq(
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,9 @@
|
||||||
package object sbt extends sbt.std.TaskExtra with sbt.Types with sbt.ProcessExtra with sbt.impl.DependencyBuilders
|
package object sbt extends sbt.std.TaskExtra with sbt.Types with sbt.ProcessExtra with sbt.impl.DependencyBuilders
|
||||||
with sbt.PathExtra with sbt.ProjectExtra with sbt.DependencyFilterExtra with sbt.BuildExtra
|
with sbt.PathExtra with sbt.ProjectExtra with sbt.DependencyFilterExtra with sbt.BuildExtra
|
||||||
{
|
{
|
||||||
|
@deprecated("Renamed to CommandStrings.", "0.12.0")
|
||||||
|
val CommandSupport = CommandStrings
|
||||||
|
|
||||||
@deprecated("Use SettingKey, which is a drop-in replacement.", "0.11.1")
|
@deprecated("Use SettingKey, which is a drop-in replacement.", "0.11.1")
|
||||||
type ScopedSetting[T] = SettingKey[T]
|
type ScopedSetting[T] = SettingKey[T]
|
||||||
@deprecated("Use TaskKey, which is a drop-in replacement.", "0.11.1")
|
@deprecated("Use TaskKey, which is a drop-in replacement.", "0.11.1")
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import Incomplete.{Error, Value => IValue}
|
||||||
* @param causes a list of incompletions that prevented `node` from completing
|
* @param causes a list of incompletions that prevented `node` from completing
|
||||||
* @param directCause the exception that caused `node` to not complete */
|
* @param directCause the exception that caused `node` to not complete */
|
||||||
final case class Incomplete(node: Option[AnyRef], tpe: IValue = Error, message: Option[String] = None, causes: Seq[Incomplete] = Nil, directCause: Option[Throwable] = None)
|
final case class Incomplete(node: Option[AnyRef], tpe: IValue = Error, message: Option[String] = None, causes: Seq[Incomplete] = Nil, directCause: Option[Throwable] = None)
|
||||||
extends Exception(message.orNull, directCause.orNull) {
|
extends Exception(message.orNull, directCause.orNull) with UnprintableException {
|
||||||
override def toString = "Incomplete(node=" + node + ", tpe=" + tpe + ", msg=" + message + ", causes=" + causes + ", directCause=" + directCause +")"
|
override def toString = "Incomplete(node=" + node + ", tpe=" + tpe + ", msg=" + message + ", causes=" + causes + ", directCause=" + directCause +")"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,7 @@ trait Init[Scope]
|
||||||
if(dist < 0) None else Some(dist)
|
if(dist < 0) None else Some(dist)
|
||||||
}
|
}
|
||||||
|
|
||||||
final class Uninitialized(val undefined: Seq[Undefined], msg: String) extends Exception(msg)
|
final class Uninitialized(val undefined: Seq[Undefined], override val toString: String) extends Exception(toString)
|
||||||
final class Undefined(val definingKey: ScopedKey[_], val referencedKey: ScopedKey[_])
|
final class Undefined(val definingKey: ScopedKey[_], val referencedKey: ScopedKey[_])
|
||||||
final class RuntimeUndefined(val undefined: Seq[Undefined]) extends RuntimeException("References to undefined settings at runtime.")
|
final class RuntimeUndefined(val undefined: Seq[Undefined]) extends RuntimeException("References to undefined settings at runtime.")
|
||||||
def Undefined(definingKey: ScopedKey[_], referencedKey: ScopedKey[_]): Undefined = new Undefined(definingKey, referencedKey)
|
def Undefined(definingKey: ScopedKey[_], referencedKey: ScopedKey[_]): Undefined = new Undefined(definingKey, referencedKey)
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,11 @@
|
||||||
package sbt
|
package sbt
|
||||||
|
|
||||||
final class MessageOnlyException(override val toString: String) extends RuntimeException(toString)
|
final class MessageOnlyException(override val toString: String) extends RuntimeException(toString)
|
||||||
final class NoMessageException extends RuntimeException
|
|
||||||
|
/** A dummy exception for the top-level exception handler to know that an exception
|
||||||
|
* has been handled, but is being passed further up to indicate general failure. */
|
||||||
|
final class AlreadyHandledException extends RuntimeException
|
||||||
|
|
||||||
|
/** A marker trait for a top-level exception handler to know that this exception
|
||||||
|
* doesn't make sense to display. */
|
||||||
|
trait UnprintableException extends Throwable
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
/* sbt -- Simple Build Tool
|
||||||
|
* Copyright 2010 Mark Harrah
|
||||||
|
*/
|
||||||
|
package sbt
|
||||||
|
|
||||||
|
import java.io.{File, PrintWriter}
|
||||||
|
|
||||||
|
final case class GlobalLogging(full: Logger, backed: ConsoleLogger, backing: GlobalLogBacking)
|
||||||
|
final case class GlobalLogBacking(file: File, last: Option[File], newLogger: (PrintWriter, GlobalLogBacking) => GlobalLogging, newBackingFile: () => File)
|
||||||
|
{
|
||||||
|
def shift(newFile: File) = GlobalLogBacking(newFile, Some(file), newLogger, newBackingFile)
|
||||||
|
def shiftNew() = shift(newBackingFile())
|
||||||
|
def unshift = GlobalLogBacking(last getOrElse file, None, newLogger, newBackingFile)
|
||||||
|
}
|
||||||
|
object GlobalLogBacking
|
||||||
|
{
|
||||||
|
def apply(newLogger: (PrintWriter, GlobalLogBacking) => GlobalLogging, newBackingFile: => File): GlobalLogBacking =
|
||||||
|
GlobalLogBacking(newBackingFile, None, newLogger, newBackingFile _)
|
||||||
|
}
|
||||||
|
object GlobalLogging
|
||||||
|
{
|
||||||
|
def initial(newLogger: (PrintWriter, GlobalLogBacking) => GlobalLogging, newBackingFile: => File): GlobalLogging =
|
||||||
|
{
|
||||||
|
val log = ConsoleLogger()
|
||||||
|
GlobalLogging(log, log, GlobalLogBacking(newLogger, newBackingFile))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package sbt
|
||||||
|
|
||||||
|
import java.io.PrintWriter
|
||||||
|
|
||||||
|
object MainLogging
|
||||||
|
{
|
||||||
|
def multiLogger(config: MultiLoggerConfig): Logger =
|
||||||
|
{
|
||||||
|
import config._
|
||||||
|
val multi = new MultiLogger(console :: backed :: extra)
|
||||||
|
// sets multi to the most verbose for clients that inspect the current level
|
||||||
|
multi setLevel Level.unionAll(backingLevel :: screenLevel :: extra.map(_.getLevel))
|
||||||
|
// set the specific levels
|
||||||
|
console setLevel screenLevel
|
||||||
|
backed setLevel backingLevel
|
||||||
|
console setTrace screenTrace
|
||||||
|
backed setTrace backingTrace
|
||||||
|
multi: Logger
|
||||||
|
}
|
||||||
|
def globalDefault(writer: PrintWriter, backing: GlobalLogBacking): GlobalLogging =
|
||||||
|
{
|
||||||
|
val backed = defaultBacked()(writer)
|
||||||
|
val full = multiLogger(defaultMultiConfig( backed ) )
|
||||||
|
GlobalLogging(full, backed, backing)
|
||||||
|
}
|
||||||
|
|
||||||
|
def defaultMultiConfig(backing: AbstractLogger): MultiLoggerConfig =
|
||||||
|
new MultiLoggerConfig(defaultScreen, backing, Nil, Level.Info, Level.Debug, -1, Int.MaxValue)
|
||||||
|
|
||||||
|
def defaultScreen: AbstractLogger = ConsoleLogger()
|
||||||
|
|
||||||
|
def defaultBacked(useColor: Boolean = ConsoleLogger.formatEnabled): PrintWriter => ConsoleLogger =
|
||||||
|
to => ConsoleLogger(ConsoleLogger.printWriterOut(to), useColor = useColor) // TODO: should probably filter ANSI codes when useColor=false
|
||||||
|
}
|
||||||
|
|
||||||
|
final case class MultiLoggerConfig(console: AbstractLogger, backed: AbstractLogger, extra: List[AbstractLogger], screenLevel: Level.Value, backingLevel: Level.Value, screenTrace: Int, backingTrace: Int)
|
||||||
Loading…
Reference in New Issue