From 1de086755b78863dfc05fc3707487b377d56cc32 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Mon, 21 Mar 2011 20:26:04 -0400 Subject: [PATCH] command logging through Streams, 'last' without a key to redisplay it --- main/Command.scala | 2 +- main/CommandSupport.scala | 4 +++- main/Main.scala | 10 ++++++---- main/Output.scala | 10 ++++++---- main/Project.scala | 14 +++++++++++--- main/State.scala | 7 ++++++- util/control/ExitHook.scala | 8 +++++--- util/log/ConsoleLogger.scala | 14 ++++++++++---- 8 files changed, 48 insertions(+), 21 deletions(-) diff --git a/main/Command.scala b/main/Command.scala index 8ef1461e9..698ce9967 100644 --- a/main/Command.scala +++ b/main/Command.scala @@ -86,7 +86,7 @@ object Command case Right(s) => s() // apply command. command side effects happen here case Left((msg,pos)) => val errMsg = commandError(command, msg, pos) - logger(state).info(errMsg) + logger(state).error(errMsg) state.fail } } diff --git a/main/CommandSupport.scala b/main/CommandSupport.scala index ea765ff2a..61a7cd147 100644 --- a/main/CommandSupport.scala +++ b/main/CommandSupport.scala @@ -46,10 +46,11 @@ EvalCommand + """ val lastGrepBrief = (LastGrepCommand + " ", "Shows lines from the last output for 'key' that match 'pattern'.") val lastGrepDetailed = -LastGrepCommand + """ +LastGrepCommand + """ [key] is a regular expression interpreted by java.util.Pattern. Lines that match 'pattern' from the last streams output associated with the key are displayed. + If no key is specified, the global streams output is used. See also """ + LastCommand + "." val lastBrief = (LastCommand + " ", "Prints the last output associated with 'key'.") @@ -57,6 +58,7 @@ LastGrepCommand + """ LastCommand + """ Redisplays the last streams output associated with the key (typically a task key). + If no key is specified, the global streams output is displayed. See also """ + LastGrepCommand + "." val InspectCommand = "inspect" diff --git a/main/Main.scala b/main/Main.scala index c96f3e491..7d1e5053f 100644 --- a/main/Main.scala +++ b/main/Main.scala @@ -247,13 +247,14 @@ object BuiltinCommands s } def lastGrep = Command(LastGrepCommand, lastGrepBrief, lastGrepDetailed)(lastGrepParser) { case (s,(pattern,sk)) => - Output.lastGrep(sk.scope, sk.key, Project.structure(s).streams, pattern) + Output.lastGrep(sk, Project.structure(s).streams, pattern) s } val spacedKeyParser = (s: State) => Act.requireSession(s, token(Space) ~> Act.scopedKeyParser(s)) - def lastGrepParser(s: State) = Act.requireSession(s, (token(Space) ~> token(NotSpace, "")) ~ spacedKeyParser(s)) - def last = Command(LastCommand, lastBrief, lastDetailed)(spacedKeyParser) { (s,sk) => - Output.last(sk.scope, sk.key, Project.structure(s).streams) + val optSpacedKeyParser = (s: State) => spacedKeyParser(s).? + def lastGrepParser(s: State) = Act.requireSession(s, (token(Space) ~> token(NotSpace, "")) ~ optSpacedKeyParser(s)) + def last = Command(LastCommand, lastBrief, lastDetailed)(optSpacedKeyParser) { (s,sk) => + Output.last(sk, Project.structure(s).streams) s } @@ -334,6 +335,7 @@ object BuiltinCommands case _ => log.trace(e) log.error(e.toString) + log.error("Use 'last' for the full log.") } s.fail } diff --git a/main/Output.scala b/main/Output.scala index 63ea350f8..5b931efbe 100644 --- a/main/Output.scala +++ b/main/Output.scala @@ -23,14 +23,16 @@ object Output None } - def last(scope: Scope, key: AttributeKey[_], mgr: Streams): Unit = - printLines(lastLines(ScopedKey(scope,key), mgr)) + def last(key: Option[ScopedKey[_]], mgr: Streams): Unit = + printLines(lastLines(key, mgr)) def printLines(lines: Seq[String]) = lines foreach println - def lastGrep(scope: Scope, key: AttributeKey[_], mgr: Streams, patternString: String) + def lastGrep(key: Option[ScopedKey[_]], mgr: Streams, patternString: String) { val pattern = Pattern.compile(patternString) - printLines(lastLines(ScopedKey(scope,key), mgr).flatMap(showMatches(pattern)) ) + printLines(lastLines(key, mgr).flatMap(showMatches(pattern)) ) } + def lastLines(key: Option[ScopedKey[_]], mgr: Streams): Seq[String] = + lastLines(key getOrElse Project.globalLoggerKey, mgr) def lastLines(key: ScopedKey[_], mgr: Streams): Seq[String] = mgr.use(key) { s => IO.readLines(s.readText( Project.fillTaskAxis(key) )) } } diff --git a/main/Project.scala b/main/Project.scala index a66eaef3d..74aab4e1c 100644 --- a/main/Project.scala +++ b/main/Project.scala @@ -6,7 +6,7 @@ package sbt import java.io.File import java.net.URI import Project._ - import Keys.{appConfiguration, buildStructure, commands, configuration, historyPath, projectCommand, sessionSettings, shellPrompt, thisProject, thisProjectRef, watch} + import Keys.{appConfiguration, buildStructure, commands, configuration, historyPath, logged, projectCommand, sessionSettings, shellPrompt, streams, thisProject, thisProjectRef, watch} import Scope.{GlobalScope,ThisScope} import CommandSupport.logger @@ -88,9 +88,10 @@ object Project extends Init[Scope] updateCurrent(newState.runExitHooks()) } def current(state: State): ProjectRef = session(state).current - def updateCurrent(s: State): State = + def updateCurrent(s0: State): State = { - val structure = Project.structure(s) + val structure = Project.structure(s0) + val s = installGlobalLogger(s0, structure) val ref = Project.current(s) val project = Load.getProject(structure.units, ref.build, ref.project) logger(s).info("Set current project to " + ref.project + " (in build " + ref.build +")") @@ -225,6 +226,13 @@ object Project extends Init[Scope] val extracted = Project.extract(state) EvaluateTask.evaluateTask(extracted.structure, taskKey, state, extracted.currentRef, checkCycles, maxWorkers) } + def globalLoggerKey = fillTaskAxis(ScopedKey(GlobalScope, streams.key)) + def installGlobalLogger(s: State, structure: Load.BuildStructure): State = + { + val str = structure.streams(globalLoggerKey) + str.open() + s.put(logged, str.log).addExitHook { str.close() } + } } trait ProjectConstructors diff --git a/main/State.scala b/main/State.scala index a962b5efa..db3ad62f1 100644 --- a/main/State.scala +++ b/main/State.scala @@ -41,8 +41,10 @@ trait StateOps { def + (newCommand: Command): State def get[T](key: AttributeKey[T]): Option[T] def put[T](key: AttributeKey[T], value: T): State + def remove(key: AttributeKey[_]): State def baseDir: File def runExitHooks(): State + def addExitHook(f: => Unit): State } object State { @@ -63,8 +65,9 @@ object State def reboot(full: Boolean) = throw new xsbti.FullReload(s.remainingCommands.toArray, full) def reload = setNext(Next.Reload) def exit(ok: Boolean) = setNext(if(ok) Next.Done else Next.Fail) - def get[T](key: AttributeKey[T]) = s.attributes.get(key) + 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 remove(key: AttributeKey[_]) = s.copy(attributes = s.attributes remove key) def fail = { val remaining = s.remainingCommands.dropWhile(_ != FailureWall) @@ -80,6 +83,8 @@ object State case None => noHandler } + def addExitHook(act: => Unit): State = + s.copy(exitHooks = s.exitHooks + ExitHook(act)) def runExitHooks(): State = { ExitHooks.runExitHooks(s.exitHooks.toSeq) s.copy(exitHooks = Set.empty) diff --git a/util/control/ExitHook.scala b/util/control/ExitHook.scala index 1e491b095..de85bff42 100644 --- a/util/control/ExitHook.scala +++ b/util/control/ExitHook.scala @@ -4,13 +4,15 @@ package sbt /** Defines a function to call as sbt exits.*/ -trait ExitHook extends NotNull +trait ExitHook { - /** Provides a name for this hook to be used to provide feedback to the user. */ - def name: String /** Subclasses should implement this method, which is called when this hook is executed. */ def runBeforeExiting(): Unit } +object ExitHook +{ + def apply(f: => Unit): ExitHook = new ExitHook { def runBeforeExiting() = f } +} object ExitHooks { diff --git a/util/log/ConsoleLogger.scala b/util/log/ConsoleLogger.scala index 4005a0889..f7229573e 100644 --- a/util/log/ConsoleLogger.scala +++ b/util/log/ConsoleLogger.scala @@ -1,9 +1,9 @@ /* sbt -- Simple Build Tool - * Copyright 2008, 2009, 2010 Mark Harrah + * Copyright 2008, 2009, 2010, 2011 Mark Harrah */ package sbt - import java.io.{PrintStream, PrintWriter} + import java.io.{BufferedWriter, PrintStream, PrintWriter} object ConsoleLogger { @@ -17,8 +17,14 @@ object ConsoleLogger def printWriterOut(out: PrintWriter): ConsoleOut = new ConsoleOut { val lockObject = out def print(s: String) = out.print(s) - def println(s: String) = out.println(s) - def println() = out.println() + def println(s: String) = { out.println(s); out.flush() } + def println() = { out.println(); out.flush() } + } + def bufferedWriterOut(out: BufferedWriter): ConsoleOut = new ConsoleOut { + val lockObject = out + def print(s: String) = out.write(s) + def println(s: String) = { out.write(s); println() } + def println() = { out.newLine(); out.flush() } } val formatEnabled =