mirror of https://github.com/sbt/sbt.git
clean up last/global logging
add history of actually executed commands
This commit is contained in:
parent
2cbc755856
commit
fa8fb49cc3
|
|
@ -82,7 +82,7 @@ final object Aggregation
|
|||
val config = extractedConfig(extracted, structure)
|
||||
|
||||
val start = System.currentTimeMillis
|
||||
val (newS, result) = withStreams(structure){ str =>
|
||||
val (newS, result) = withStreams(structure, s){ str =>
|
||||
val transform = nodeView(s, str, extra.tasks, extra.values)
|
||||
runTask(toRun, s,str, structure.index.triggers, config)(transform)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -209,8 +209,8 @@ object BuildStreams
|
|||
final val BuildUnitPath = "$build"
|
||||
final val StreamsDirectory = "streams"
|
||||
|
||||
def mkStreams(units: Map[URI, LoadedBuildUnit], root: URI, data: Settings[Scope]): Streams =
|
||||
std.Streams( path(units, root, data), displayFull, LogManager.construct(data) )
|
||||
def mkStreams(units: Map[URI, LoadedBuildUnit], root: URI, data: Settings[Scope]): State => Streams = s =>
|
||||
std.Streams( path(units, root, data), displayFull, LogManager.construct(data, s) )
|
||||
|
||||
def path(units: Map[URI, LoadedBuildUnit], root: URI, data: Settings[Scope])(scoped: ScopedKey[_]): File =
|
||||
resolvePath( projectPath(units, root, scoped, data), nonProjectPath(scoped) )
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import Path._
|
|||
object CommandSupport
|
||||
{
|
||||
def logger(s: State) = globalLogging(s).full
|
||||
def globalLogging(s: State) = s get Keys.globalLogging.key getOrElse error("Global logging misconfigured")
|
||||
def globalLogging(s: State) = s get Keys.globalLogging getOrElse error("Global logging misconfigured")
|
||||
|
||||
// slightly better fallback in case of older launcher
|
||||
def bootDirectory(state: State): File =
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ object EvaluateTask
|
|||
def evaluateTask[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, ref: ProjectRef, checkCycles: Boolean = false, maxWorkers: Int = SystemProcessors): Option[Result[T]] =
|
||||
apply(structure, taskKey, state, ref, EvaluateConfig(false, checkCycles, maxWorkers)).map(_._2)
|
||||
def apply[T](structure: BuildStructure, taskKey: ScopedKey[Task[T]], state: State, ref: ProjectRef, config: EvaluateConfig = defaultConfig): Option[(State, Result[T])] =
|
||||
withStreams(structure) { str =>
|
||||
withStreams(structure, state) { str =>
|
||||
for( (task, toNode) <- getTask(structure, taskKey, state, str, ref) ) yield
|
||||
runTask(task, state, str, structure.index.triggers, config)(toNode)
|
||||
}
|
||||
|
|
@ -81,9 +81,9 @@ object EvaluateTask
|
|||
}
|
||||
def getStreams(key: ScopedKey[_], streams: Streams): TaskStreams =
|
||||
streams(ScopedKey(Project.fillTaskAxis(key).scope, Keys.streams.key))
|
||||
def withStreams[T](structure: BuildStructure)(f: Streams => T): T =
|
||||
def withStreams[T](structure: BuildStructure, state: State)(f: Streams => T): T =
|
||||
{
|
||||
val str = std.Streams.closeable(structure.streams)
|
||||
val str = std.Streams.closeable(structure.streams(state))
|
||||
try { f(str) } finally { str.close() }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ object GlobalPlugin
|
|||
def evaluate[T](state: State, structure: BuildStructure, t: Task[T]): (State, T) =
|
||||
{
|
||||
import EvaluateTask._
|
||||
withStreams(structure) { str =>
|
||||
withStreams(structure, state) { str =>
|
||||
val nv = nodeView(state, str)
|
||||
val config = EvaluateTask.defaultConfig
|
||||
val (newS, result) = runTask(t, state, str, structure.index.triggers, config)(nv)
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ 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.")
|
||||
|
||||
// Command keys
|
||||
val globalLogging = SettingKey[GlobalLogging]("global-logging", "Provides a global Logger, including command logging.")
|
||||
val globalLogging = AttributeKey[GlobalLogging]("global-logging", "Provides a global Logger, including command logging.")
|
||||
val historyPath = SettingKey[Option[File]]("history", "The location where command line history is persisted.")
|
||||
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.")
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ object Load
|
|||
}
|
||||
def injectGlobal(state: State): Seq[Project.Setting[_]] =
|
||||
(appConfiguration in GlobalScope :== state.configuration) +:
|
||||
(globalLogging in GlobalScope := CommandSupport.globalLogging(state)) +:
|
||||
EvaluateTask.injectSettings
|
||||
def defaultWithGlobal(state: State, base: File, rawConfig: LoadBuildConfiguration, globalBase: File, log: Logger): LoadBuildConfiguration =
|
||||
{
|
||||
|
|
@ -576,7 +575,7 @@ object Load
|
|||
|
||||
def referenced[PR <: ProjectReference](definitions: Seq[ProjectDefinition[PR]]): Seq[PR] = definitions flatMap { _.referenced }
|
||||
|
||||
final class BuildStructure(val units: Map[URI, LoadedBuildUnit], val root: URI, val settings: Seq[Setting[_]], val data: Settings[Scope], val index: StructureIndex, val streams: Streams, val delegates: Scope => Seq[Scope], val scopeLocal: ScopeLocal)
|
||||
final class BuildStructure(val units: Map[URI, LoadedBuildUnit], val root: URI, val settings: Seq[Setting[_]], val data: Settings[Scope], val index: StructureIndex, val streams: State => Streams, val delegates: Scope => Seq[Scope], val scopeLocal: ScopeLocal)
|
||||
{
|
||||
val rootProject: URI => String = Load getRootProject units
|
||||
def allProjects: Seq[ResolvedProject] = units.values.flatMap(_.defined.values).toSeq
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ package sbt
|
|||
|
||||
object LogManager
|
||||
{
|
||||
def construct(data: Settings[Scope]) = (task: ScopedKey[_], to: PrintWriter) =>
|
||||
def construct(data: Settings[Scope], state: State) = (task: ScopedKey[_], to: PrintWriter) =>
|
||||
{
|
||||
val manager = logManager in task.scope get data getOrElse default
|
||||
manager(data, task, to)
|
||||
manager(data, state, task, to)
|
||||
}
|
||||
lazy val default: LogManager = withLoggers()
|
||||
def defaults(extra: ScopedKey[_] => Seq[AbstractLogger]): LogManager = withLoggers(extra = extra)
|
||||
|
|
@ -30,11 +30,11 @@ object LogManager
|
|||
|
||||
def withLoggers(screen: => AbstractLogger = defaultScreen, backed: PrintWriter => AbstractLogger = defaultBacked(), extra: ScopedKey[_] => Seq[AbstractLogger] = _ => Nil): LogManager =
|
||||
new LogManager {
|
||||
def apply(data: Settings[Scope], task: ScopedKey[_], to: PrintWriter): Logger =
|
||||
defaultLogger(data, task, screen, backed(to), extra(task).toList)
|
||||
def apply(data: Settings[Scope], state: State, task: ScopedKey[_], to: PrintWriter): Logger =
|
||||
defaultLogger(data, state, task, screen, backed(to), extra(task).toList)
|
||||
}
|
||||
|
||||
def defaultLogger(data: Settings[Scope], task: ScopedKey[_], console: AbstractLogger, backed: AbstractLogger, extra: List[AbstractLogger]): Logger =
|
||||
def defaultLogger(data: Settings[Scope], state: State, task: ScopedKey[_], console: AbstractLogger, backed: AbstractLogger, extra: List[AbstractLogger]): Logger =
|
||||
{
|
||||
val scope = task.scope
|
||||
def getOr[T](key: AttributeKey[T], default: T): T = data.get(scope, key) getOrElse default
|
||||
|
|
@ -42,7 +42,7 @@ object LogManager
|
|||
val backingLevel = getOr(persistLogLevel.key, Level.Debug)
|
||||
val screenTrace = getOr(traceLevel.key, -1)
|
||||
val backingTrace = getOr(persistTraceLevel.key, Int.MaxValue)
|
||||
val extraBacked = data.get(Scope.GlobalScope, Keys.globalLogging.key).map(_.backed).toList
|
||||
val extraBacked = (state get Keys.globalLogging).map(_.backed).toList
|
||||
multiLogger( new MultiLoggerConfig(console, backed, extraBacked ::: extra, screenLevel, backingLevel, screenTrace, backingTrace) )
|
||||
}
|
||||
def multiLogger(config: MultiLoggerConfig): Logger =
|
||||
|
|
@ -58,11 +58,11 @@ object LogManager
|
|||
backed setTrace backingTrace
|
||||
multi: Logger
|
||||
}
|
||||
def globalDefault(writer: PrintWriter, file: File): GlobalLogging =
|
||||
def globalDefault(writer: PrintWriter, backing: GlobalLogBacking): GlobalLogging =
|
||||
{
|
||||
val backed = defaultBacked()(writer)
|
||||
val full = multiLogger(defaultMultiConfig( backed ) )
|
||||
GlobalLogging(full, backed, file)
|
||||
GlobalLogging(full, backed, backing)
|
||||
}
|
||||
|
||||
def defaultMultiConfig(backing: AbstractLogger): MultiLoggerConfig =
|
||||
|
|
@ -71,6 +71,11 @@ object LogManager
|
|||
final case class MultiLoggerConfig(console: AbstractLogger, backed: AbstractLogger, extra: List[AbstractLogger], screenLevel: Level.Value, backingLevel: Level.Value, screenTrace: Int, backingTrace: Int)
|
||||
trait LogManager
|
||||
{
|
||||
def apply(data: Settings[Scope], task: ScopedKey[_], writer: PrintWriter): Logger
|
||||
def apply(data: Settings[Scope], state: State, task: ScopedKey[_], writer: PrintWriter): Logger
|
||||
}
|
||||
final case class GlobalLogging(full: Logger, backed: ConsoleLogger, backing: File)
|
||||
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)
|
||||
|
|
|
|||
125
main/Main.scala
125
main/Main.scala
|
|
@ -29,7 +29,7 @@ final class xMain extends xsbti.AppMain
|
|||
import CommandSupport.{DefaultsCommand, InitCommand}
|
||||
val initialCommandDefs = Seq(initialize, defaults)
|
||||
val commands = DefaultsCommand +: InitCommand +: (DefaultBootCommands ++ configuration.arguments.map(_.trim))
|
||||
val state = State( configuration, initialCommandDefs, Set.empty, None, commands, initialAttributes, None )
|
||||
val state = State( configuration, initialCommandDefs, Set.empty, None, commands, State.newHistory, initialAttributes, State.Continue )
|
||||
MainLoop.runLogged(state)
|
||||
}
|
||||
}
|
||||
|
|
@ -39,7 +39,7 @@ final class ScriptMain extends xsbti.AppMain
|
|||
{
|
||||
import BuiltinCommands.{initialAttributes, ScriptCommands}
|
||||
val commands = Script.Name +: configuration.arguments.map(_.trim)
|
||||
val state = State( configuration, ScriptCommands, Set.empty, None, commands, initialAttributes, None )
|
||||
val state = State( configuration, ScriptCommands, Set.empty, None, commands, State.newHistory, initialAttributes, State.Continue )
|
||||
MainLoop.runLogged(state)
|
||||
}
|
||||
}
|
||||
|
|
@ -49,37 +49,72 @@ final class ConsoleMain extends xsbti.AppMain
|
|||
{
|
||||
import BuiltinCommands.{initialAttributes, ConsoleCommands}
|
||||
val commands = IvyConsole.Name +: configuration.arguments.map(_.trim)
|
||||
val state = State( configuration, ConsoleCommands, Set.empty, None, commands, initialAttributes, None )
|
||||
val state = State( configuration, ConsoleCommands, Set.empty, None, commands, State.newHistory, initialAttributes, State.Continue )
|
||||
MainLoop.runLogged(state)
|
||||
}
|
||||
}
|
||||
object MainLoop
|
||||
{
|
||||
/** Entry point to run the remaining commands in State with managed global logging.*/
|
||||
def runLogged(state: State): xsbti.MainResult =
|
||||
{
|
||||
val logFile = File.createTempFile("sbt", ".log")
|
||||
try {
|
||||
val result = runLogged(state, logFile)
|
||||
logFile.delete() // only delete when exiting normally
|
||||
result
|
||||
runLoggedLoop(state, GlobalLogBacking(newBackingFile(), None))
|
||||
|
||||
/** Constructs a new, (weakly) unique, temporary file to use as the backing for global logging. */
|
||||
def newBackingFile(): File = 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 => throw e
|
||||
case e => System.err.println("sbt appears to be exiting abnormally.\n The log file for this session is at " + logFile); throw e
|
||||
}
|
||||
}
|
||||
def runLogged(state: State, backing: File): xsbti.MainResult =
|
||||
Using.fileWriter()(backing) { writer =>
|
||||
val out = new java.io.PrintWriter(writer)
|
||||
val loggedState = state.put(globalLogging.key, LogManager.globalDefault(out, backing))
|
||||
try { run(loggedState) } finally { out.close() }
|
||||
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
|
||||
}
|
||||
|
||||
@tailrec def run(state: State): xsbti.MainResult =
|
||||
state.result match
|
||||
/** 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 None => run(next(state))
|
||||
case Some(result) => result
|
||||
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 =
|
||||
|
|
@ -220,12 +255,12 @@ object BuiltinCommands
|
|||
val line = reader.readLine(prompt)
|
||||
line match {
|
||||
case Some(line) =>
|
||||
if(!line.trim.isEmpty) CommandSupport.globalLogging(s).backed.out.println(Output.DefaultTail + line)
|
||||
s.copy(onFailure = Some(Shell), remainingCommands = line +: Shell +: s.remainingCommands)
|
||||
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]] =
|
||||
( token(';' ~> OptSpace) flatMap { _ => matched(s.combinedParser | token(charClass(_ != ';').+, hide= const(true))) <~ token(OptSpace) } ).+
|
||||
def multiApplied(s: State) =
|
||||
|
|
@ -356,11 +391,12 @@ object BuiltinCommands
|
|||
def lastGrep = Command(LastGrepCommand, lastGrepBrief, lastGrepDetailed)(lastGrepParser) {
|
||||
case (s, (pattern,Some(sk))) =>
|
||||
val (str, ref, display) = extractLast(s)
|
||||
Output.lastGrep(sk, str, pattern)(display)
|
||||
s
|
||||
Output.lastGrep(sk, str, str.streams(s), pattern, printLast(s))(display)
|
||||
keepLastLog(s)
|
||||
case (s, (pattern, None)) =>
|
||||
Output.lastGrep(CommandSupport.globalLogging(s).backing, pattern)
|
||||
s
|
||||
for(logFile <- lastLogFile(s)) yield
|
||||
Output.lastGrep(logFile, pattern, printLast(s))
|
||||
keepLastLog(s)
|
||||
}
|
||||
def extractLast(s: State) = {
|
||||
val ext = Project.extract(s)
|
||||
|
|
@ -373,13 +409,34 @@ object BuiltinCommands
|
|||
def last = Command(LastCommand, lastBrief, lastDetailed)(optSpacedKeyParser) {
|
||||
case (s,Some(sk)) =>
|
||||
val (str, ref, display) = extractLast(s)
|
||||
Output.last(sk, str)(display)
|
||||
s
|
||||
Output.last(sk, str, str.streams(s), printLast(s))(display)
|
||||
keepLastLog(s)
|
||||
case (s, None) =>
|
||||
Output.last( CommandSupport.globalLogging(s).backing )
|
||||
s
|
||||
for(logFile <- lastLogFile(s)) yield
|
||||
Output.last( logFile, printLast(s) )
|
||||
keepLastLog(s)
|
||||
}
|
||||
|
||||
/** Determines the log file that last* commands should operate on. See also isLastOnly. */
|
||||
def lastLogFile(s: State) =
|
||||
{
|
||||
val backing = CommandSupport.globalLogging(s).backing
|
||||
if(isLastOnly(s)) backing.last else Some(backing.file)
|
||||
}
|
||||
|
||||
/** If false, shift the current log file to be the log file that 'last' will operate on.
|
||||
* If true, keep the previous log file as the one 'last' operates on because there is nothing useful in the current one.*/
|
||||
def keepLastLog(s: State): State = if(isLastOnly(s)) s.keepLastLog else s
|
||||
|
||||
/** The last* commands need to determine whether to read from the current log file or the previous log file
|
||||
* and whether to keep the previous log file or not. This is selected based on whether the previous command
|
||||
* was 'shell', which meant that the user directly entered the 'last' command. If it wasn't directly entered,
|
||||
* the last* commands operate on any output since the last 'shell' command and do shift the log file.
|
||||
* Otherwise, the output since the previous 'shell' command is used and the log file is not shifted.*/
|
||||
def isLastOnly(s: State): Boolean = s.history.previous.forall(_ == Shell)
|
||||
|
||||
def printLast(s: State): Seq[String] => Unit = _ foreach println
|
||||
|
||||
def autoImports(extracted: Extracted): EvalImports = new EvalImports(imports(extracted), "<auto-imports>")
|
||||
def imports(extracted: Extracted): Seq[(String,Int)] =
|
||||
{
|
||||
|
|
@ -430,7 +487,7 @@ object BuiltinCommands
|
|||
def matches(s: String) = !result.isEmpty && (s startsWith result)
|
||||
|
||||
if(result.isEmpty || matches("retry"))
|
||||
LoadProject :: s
|
||||
LoadProject :: s.clearGlobalLog
|
||||
else if(matches(Quit))
|
||||
s.exit(ok = false)
|
||||
else if(matches("ignore"))
|
||||
|
|
|
|||
|
|
@ -30,16 +30,19 @@ object Output
|
|||
}
|
||||
final val DefaultTail = "> "
|
||||
|
||||
def last(key: ScopedKey[_], structure: BuildStructure)(implicit display: Show[ScopedKey[_]]): Unit = printLines( flatLines(lastLines(key, structure))(idFun) )
|
||||
def last(file: File, tailDelim: String = DefaultTail): Unit = printLines(tailLines(file, tailDelim))
|
||||
def last(key: ScopedKey[_], structure: BuildStructure, streams: Streams, printLines: Seq[String] => Unit)(implicit display: Show[ScopedKey[_]]): Unit =
|
||||
printLines( flatLines(lastLines(key, structure, streams))(idFun) )
|
||||
|
||||
def lastGrep(key: ScopedKey[_], structure: BuildStructure, patternString: String)(implicit display: Show[ScopedKey[_]]): Unit =
|
||||
def last(file: File, printLines: Seq[String] => Unit, tailDelim: String = DefaultTail): Unit =
|
||||
printLines(tailLines(file, tailDelim))
|
||||
|
||||
def lastGrep(key: ScopedKey[_], structure: BuildStructure, streams: Streams, patternString: String, printLines: Seq[String] => Unit)(implicit display: Show[ScopedKey[_]]): Unit =
|
||||
{
|
||||
val pattern = Pattern compile patternString
|
||||
val lines = flatLines( lastLines(key, structure) )(_ flatMap showMatches(pattern))
|
||||
val lines = flatLines( lastLines(key, structure, streams) )(_ flatMap showMatches(pattern))
|
||||
printLines( lines )
|
||||
}
|
||||
def lastGrep(file: File, patternString: String, tailDelim: String = DefaultTail): Unit =
|
||||
def lastGrep(file: File, patternString: String, printLines: Seq[String] => Unit, tailDelim: String = DefaultTail): Unit =
|
||||
printLines(grep( tailLines(file, tailDelim), patternString) )
|
||||
def grep(lines: Seq[String], patternString: String): Seq[String] =
|
||||
lines flatMap showMatches(Pattern compile patternString)
|
||||
|
|
@ -52,13 +55,12 @@ object Output
|
|||
if(!single) bold(display(key)) +: flines else flines
|
||||
}
|
||||
}
|
||||
def printLines(lines: Seq[String]) = lines foreach println
|
||||
def bold(s: String) = if(ConsoleLogger.formatEnabled) BOLD + s + RESET else s
|
||||
|
||||
def lastLines(key: ScopedKey[_], structure: BuildStructure): Seq[KeyValue[Seq[String]]] =
|
||||
def lastLines(key: ScopedKey[_], structure: BuildStructure, streams: Streams): Seq[KeyValue[Seq[String]]] =
|
||||
{
|
||||
val aggregated = Aggregation.getTasks(key, structure, true)
|
||||
val outputs = aggregated map { case KeyValue(key, value) => KeyValue(key, lastLines(key, structure.streams)) }
|
||||
val outputs = aggregated map { case KeyValue(key, value) => KeyValue(key, lastLines(key, streams)) }
|
||||
outputs.filterNot(_.value.isEmpty)
|
||||
}
|
||||
def lastLines(key: ScopedKey[_], mgr: Streams): Seq[String] = mgr.use(key) { s => IO.readLines(s.readText( Project.fillTaskAxis(key) )) }
|
||||
|
|
|
|||
|
|
@ -384,7 +384,7 @@ object SessionVar
|
|||
}
|
||||
|
||||
def persist[T](key: ScopedKey[Task[T]], state: State, value: T)(implicit f: sbinary.Format[T]): Unit =
|
||||
Project.structure(state).streams.use(key)( s =>
|
||||
Project.structure(state).streams(state).use(key)( s =>
|
||||
Operations.write(s.binary(DefaultDataID), value)(f)
|
||||
)
|
||||
|
||||
|
|
@ -410,7 +410,7 @@ object SessionVar
|
|||
}
|
||||
|
||||
def read[T](key: ScopedKey[Task[T]], state: State)(implicit f: Format[T]): Option[T] =
|
||||
Project.structure(state).streams.use(key) { s =>
|
||||
Project.structure(state).streams(state).use(key) { s =>
|
||||
try { Some(Operations.read(s.readBinary(key, DefaultDataID))) }
|
||||
catch { case e: Exception => None }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,9 @@ final case class State(
|
|||
exitHooks: Set[ExitHook],
|
||||
onFailure: Option[String],
|
||||
remainingCommands: Seq[String],
|
||||
history: State.History,
|
||||
attributes: AttributeMap,
|
||||
result: Option[xsbti.MainResult]
|
||||
next: State.Next
|
||||
) extends Identity {
|
||||
lazy val combinedParser = Command.combine(definedCommands)(this)
|
||||
}
|
||||
|
|
@ -31,8 +32,11 @@ trait StateOps {
|
|||
def :: (command: String): State
|
||||
def continue: State
|
||||
def reboot(full: Boolean): State
|
||||
def setResult(n: Option[xsbti.MainResult]): State
|
||||
def setNext(n: State.Next): State
|
||||
@deprecated("Use setNext", "0.11.0") def setResult(ro: Option[xsbti.MainResult])
|
||||
def reload: State
|
||||
def clearGlobalLog: State
|
||||
def keepLastLog: State
|
||||
def exit(ok: Boolean): State
|
||||
def fail: State
|
||||
def ++ (newCommands: Seq[Command]): State
|
||||
|
|
@ -50,6 +54,27 @@ trait StateOps {
|
|||
}
|
||||
object State
|
||||
{
|
||||
sealed trait Next
|
||||
object Continue extends Next
|
||||
final class Return(val result: xsbti.MainResult) extends Next
|
||||
final object ClearGlobalLog extends Next
|
||||
final object KeepLastLog extends Next
|
||||
|
||||
/** Provides a list of recently executed commands. The commands are stored as processed instead of as entered by the user.*/
|
||||
final class History private[State](val executed: Seq[String], val maxSize: Int)
|
||||
{
|
||||
def :: (command: String): History =
|
||||
{
|
||||
val prependTo = if(executed.size >= maxSize) executed.take(maxSize - 1) else executed
|
||||
new History(command +: prependTo, maxSize)
|
||||
}
|
||||
def setMaxSize(size: Int): History =
|
||||
new History(executed.take(size), size)
|
||||
def current: String = executed.head
|
||||
def previous: Option[String] = executed.drop(1).headOption
|
||||
}
|
||||
def newHistory = new History(Vector.empty, complete.HistoryCommands.MaxLines)
|
||||
|
||||
def defaultReload(state: State): Reboot =
|
||||
{
|
||||
val app = state.configuration.provider
|
||||
|
|
@ -59,7 +84,7 @@ object State
|
|||
implicit def stateOps(s: State): StateOps = new StateOps {
|
||||
def process(f: (String, State) => State): State =
|
||||
s.remainingCommands match {
|
||||
case Seq(x, xs @ _*) => f(x, s.copy(remainingCommands = xs))
|
||||
case Seq(x, xs @ _*) => f(x, s.copy(remainingCommands = xs, history = x :: s.history))
|
||||
case Seq() => exit(true)
|
||||
}
|
||||
s.copy(remainingCommands = s.remainingCommands.drop(1))
|
||||
|
|
@ -68,11 +93,14 @@ object State
|
|||
def ++ (newCommands: Seq[Command]): State = s.copy(definedCommands = (s.definedCommands ++ newCommands).distinct)
|
||||
def + (newCommand: Command): State = this ++ (newCommand :: Nil)
|
||||
def baseDir: File = s.configuration.baseDirectory
|
||||
def setResult(n: Option[xsbti.MainResult]) = s.copy(result = n)
|
||||
def continue = setResult(None)
|
||||
def setNext(n: Next) = s.copy(next = n)
|
||||
def setResult(ro: Option[xsbti.MainResult]) = ro match { case None => continue; case Some(r) => setNext(new Return(r)) }
|
||||
def continue = setNext(Continue)
|
||||
def reboot(full: Boolean) = throw new xsbti.FullReload(s.remainingCommands.toArray, full)
|
||||
def reload = setResult(Some(defaultReload(s)))
|
||||
def exit(ok: Boolean) = setResult(Some(Exit(if(ok) 0 else 1)))
|
||||
def reload = setNext(new Return(defaultReload(s)))
|
||||
def clearGlobalLog = setNext(ClearGlobalLog)
|
||||
def keepLastLog = setNext(KeepLastLog)
|
||||
def exit(ok: Boolean) = setNext(new Return(Exit(if(ok) 0 else 1)))
|
||||
def get[T](key: AttributeKey[T]) = s.attributes get key
|
||||
def put[T](key: AttributeKey[T], value: T) = s.copy(attributes = s.attributes.put(key, value))
|
||||
def update[T](key: AttributeKey[T])(f: Option[T] => T): State = put(key, f(get(key)))
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ object TaskData
|
|||
def dataStreams[T](structure: BuildStructure, reader: ScopedKey[_], readFrom: AttributeKey[_])(f: (TaskStreams, ScopedKey[_]) => T): Option[T] =
|
||||
structure.data.definingScope(reader.scope, readFrom) map { defined =>
|
||||
val key = ScopedKey(Scope.fillTaskAxis(defined, readFrom), readFrom)
|
||||
structure.streams.use(reader)(ts => f(ts, key))
|
||||
structure.streams(fakeState(structure)).use(reader)(ts => f(ts, key))
|
||||
}
|
||||
def write[T](i: Initialize[Task[T]], id: String = DefaultDataID)(implicit f: Format[T]): Initialize[Task[T]] = writeRelated(i, id)(idFun[T])(f)
|
||||
|
||||
|
|
@ -42,4 +42,11 @@ object TaskData
|
|||
value
|
||||
}
|
||||
}
|
||||
// exists to keep the method signatures the same (since this object is potentially used but deprecated),
|
||||
// but allow the BuildStructure Streams to be constructed from State, which isn't actually needed here
|
||||
private[this] def fakeState(structure: BuildStructure): State =
|
||||
{
|
||||
val config = Keys.appConfiguration in Scope.GlobalScope get structure.data
|
||||
State(config.get, Nil, Set.empty, None, Nil, State.newHistory, AttributeMap.empty, State.Continue)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue