clean up last/global logging

add history of actually executed commands
This commit is contained in:
Mark Harrah 2011-10-30 18:39:18 -04:00
parent 2cbc755856
commit fa8fb49cc3
13 changed files with 171 additions and 73 deletions

View File

@ -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)
}

View File

@ -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) )

View File

@ -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 =

View 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() }
}

View File

@ -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)

View File

@ -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.")

View File

@ -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

View File

@ -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)

View File

@ -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"))

View File

@ -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) )) }

View File

@ -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 }
}

View File

@ -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)))

View File

@ -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)
}
}