Skip to content

Commit

Permalink
First pass at a ticker-based progress logger to reduce the spamminess…
Browse files Browse the repository at this point in the history
… of the console
  • Loading branch information
lihaoyi committed Jan 5, 2018
1 parent 53f55cb commit fb68da0
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 27 deletions.
6 changes: 5 additions & 1 deletion core/src/main/scala/mill/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ object Main {
else cliConfig.copy(
predefCode =
"""import $file.build, build._
|implicit val replApplyHandler = mill.main.ReplApplyHandler(repl.pprinter(), build.mapping)
|implicit val replApplyHandler = mill.main.ReplApplyHandler(
| interp.colors(),
| repl.pprinter(),
| build.mapping
|)
|repl.pprinter() = replApplyHandler.pprinter
|import replApplyHandler.generatedEval._
|
Expand Down
28 changes: 20 additions & 8 deletions core/src/main/scala/mill/eval/Evaluator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ class Evaluator[T](val workspacePath: Path,
val evaluated = new OSet.Mutable[Task[_]]
val results = mutable.LinkedHashMap.empty[Task[_], Result[Any]]

for ((terminal, group)<- sortedGroups.items()){
val (newResults, newEvaluated) = evaluateGroupCached(terminal, group, results)
for (((terminal, group), i) <- sortedGroups.items().zipWithIndex){
// Increment the counter message by 1 to go from 1/10 to 10/10 instead of 0/10 to 9/10
val counterMsg = (i+1) + "/" + sortedGroups.keyCount
val (newResults, newEvaluated) = evaluateGroupCached(terminal, group, results, counterMsg)
for(ev <- newEvaluated){
evaluated.append(ev)
}
Expand All @@ -83,7 +85,8 @@ class Evaluator[T](val workspacePath: Path,

def evaluateGroupCached(terminal: Either[Task[_], Labelled[_]],
group: OSet[Task[_]],
results: collection.Map[Task[_], Result[Any]]): (collection.Map[Task[_], Result[Any]], Seq[Task[_]]) = {
results: collection.Map[Task[_], Result[Any]],
counterMsg: String): (collection.Map[Task[_], Result[Any]], Seq[Task[_]]) = {


val externalInputs = group.items.flatMap(_.inputs).filter(!group.contains(_))
Expand All @@ -95,7 +98,14 @@ class Evaluator[T](val workspacePath: Path,

terminal match{
case Left(task) =>
evaluateGroup(group, results, groupBasePath = None, paths = None, maybeTargetLabel = None)
evaluateGroup(
group,
results,
groupBasePath = None,
paths = None,
maybeTargetLabel = None,
counterMsg = counterMsg
)
case Right(labelledTarget) =>
val paths = Evaluator.resolveDestPaths(workspacePath, labelledTarget.segments)
val groupBasePath = basePath / Evaluator.makeSegmentStrings(labelledTarget.segments)
Expand Down Expand Up @@ -128,7 +138,8 @@ class Evaluator[T](val workspacePath: Path,
results,
groupBasePath = Some(groupBasePath),
paths = Some(paths),
maybeTargetLabel = Some(msgParts.mkString)
maybeTargetLabel = Some(msgParts.mkString),
counterMsg = counterMsg
)

newResults(labelledTarget.target) match{
Expand Down Expand Up @@ -161,7 +172,8 @@ class Evaluator[T](val workspacePath: Path,
results: collection.Map[Task[_], Result[Any]],
groupBasePath: Option[Path],
paths: Option[Evaluator.Paths],
maybeTargetLabel: Option[String]) = {
maybeTargetLabel: Option[String],
counterMsg: String) = {


val newEvaluated = mutable.Buffer.empty[Task[_]]
Expand All @@ -177,7 +189,7 @@ class Evaluator[T](val workspacePath: Path,

val logRun = inputResults.forall(_.isInstanceOf[Result.Success[_]])

if(logRun) { log.info("Running " + targetLabel) }
if(logRun) { log.ticker(s"[$counterMsg] $targetLabel ") }
}

val multiLogger = resolveLogger(paths.map(_.log))
Expand Down Expand Up @@ -235,7 +247,7 @@ class Evaluator[T](val workspacePath: Path,
case None => log
case Some(path) =>
rm(path)
MultiLogger(log, FileLogger(path))
MultiLogger(log.colored, log, FileLogger(log.colored, path))
}
}

Expand Down
6 changes: 4 additions & 2 deletions core/src/main/scala/mill/main/MainRunner.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package mill.main
import java.io.{InputStream, OutputStream, PrintStream}

import ammonite.Main
import ammonite.interp.{Interpreter, Preprocessor}
import ammonite.ops.Path
import ammonite.util.{Imports, Name, Res, Util}
import ammonite.util._
import mill.discover.Discovered
import mill.eval.{Evaluator, PathRef}
import upickle.Js
Expand All @@ -21,14 +22,15 @@ class MainRunner(config: ammonite.main.Cli.Config, show: Boolean,
stdErr: OutputStream)
extends ammonite.MainRunner(config, outprintStream, errPrintStream, stdIn, stdOut, stdErr){
var lastEvaluator: Option[(Seq[(Path, Long)], Evaluator[_])] = None

override def runScript(scriptPath: Path, scriptArgs: List[String]) =
watchLoop(
isRepl = false,
printing = true,
mainCfg => {
val (result, interpWatched) = RunScript.runScript(
mainCfg.wd, scriptPath, mainCfg.instantiateInterpreter(), scriptArgs, lastEvaluator,
errPrintStream, errPrintStream
errPrintStream, errPrintStream, colors
)

result match{
Expand Down
11 changes: 9 additions & 2 deletions core/src/main/scala/mill/main/ReplApplyHandler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,21 @@ import mill.util.OSet

import scala.collection.mutable
object ReplApplyHandler{
def apply[T](pprinter0: pprint.PPrinter, mapping: Discovered.Mapping[T]) = {
def apply[T](colors: ammonite.util.Colors,
pprinter0: pprint.PPrinter,
mapping: Discovered.Mapping[T]) = {
new ReplApplyHandler(
pprinter0,
new mill.eval.Evaluator(
ammonite.ops.pwd / 'out,
ammonite.ops.pwd,
mapping,
new mill.util.PrintLogger(true, System.err, System.err)
new mill.util.PrintLogger(
colors != ammonite.util.Colors.BlackWhite,
colors,
System.err,
System.err
)
)
)
}
Expand Down
5 changes: 3 additions & 2 deletions core/src/main/scala/mill/main/RunScript.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ object RunScript{
scriptArgs: Seq[String],
lastEvaluator: Option[(Seq[(Path, Long)], Evaluator[_])],
infoStream: PrintStream,
errStream: PrintStream)
errStream: PrintStream,
colors: ammonite.util.Colors)
: (Res[(Evaluator[_], Seq[(Path, Long)], Either[String, Seq[Js.Value]])], Seq[(Path, Long)]) = {

val log = new PrintLogger(true, infoStream, errStream)
val log = new PrintLogger(colors != ammonite.util.Colors.BlackWhite, colors, infoStream, errStream)
val (evalRes, interpWatched) = lastEvaluator match{
case Some((prevInterpWatchedSig, prevEvaluator))
if watchedSigUnchanged(prevInterpWatchedSig) =>
Expand Down
68 changes: 58 additions & 10 deletions core/src/main/scala/mill/util/Logger.scala
Original file line number Diff line number Diff line change
@@ -1,38 +1,84 @@
package mill.util

import java.io.{FileOutputStream, PrintStream}
import java.io._

import ammonite.ops.Path
import ammonite.util.Colors


trait Logger {
def colored: Boolean
val outputStream: PrintStream
def info(s: String): Unit
def error(s: String): Unit

/**
* Like [[info]], but if two calls to [[ticker]] are made consecutively
* without any calls to [[info]]/[[error]][[outputStream]] in between,
* the second call to [[ticker]] over-writes the first one in the console.
* This is useful for displaying loading bars, progress updates or all other
* sorts of fast-changing information to the user
*/
def ticker(s: String): Unit
def close(): Unit = ()
}

object DummyLogger extends Logger {
def colored = false
object outputStream extends PrintStream(_ => ())
def info(s: String) = ()
def error(s: String) = ()
def ticker(s: String) = ()
}

case class PrintLogger(coloredOutput: Boolean,
case class PrintLogger(colored: Boolean,
colors: ammonite.util.Colors,
infoStream: PrintStream,
errorStream: PrintStream) extends Logger {

override val outputStream = infoStream
val colors =
if(coloredOutput) Colors.Default
else Colors.BlackWhite
var lastLineTicker = false
override val outputStream = new PrintStream(
new OutputStream {
override def write(b: Array[Byte]): Unit = {
lastLineTicker = false
infoStream.write(b)
}

override def write(b: Array[Byte], off: Int, len: Int): Unit = {
lastLineTicker = false
infoStream.write(b, off, len)
}

def write(b: Int) = {
lastLineTicker = false
infoStream.write(b)
}
}
)

def info(s: String) = infoStream.println(colors.info()(s))
def error(s: String) = errorStream.println(colors.error()(s))
def info(s: String) = {
lastLineTicker = false
infoStream.println(colors.info()(s))
}
def error(s: String) = {
lastLineTicker = false
errorStream.println(colors.error()(s))
}
def ticker(s: String) = {
if (lastLineTicker){
val p = new PrintWriter(infoStream)
val nav = new ammonite.terminal.AnsiNav(p)
nav.up(1)
nav.clearLine(2)
nav.left(9999)
p.flush()
}
lastLineTicker = true
infoStream.println(colors.info()(s))
}
}

case class FileLogger(file: Path) extends Logger {
case class FileLogger(colored: Boolean, file: Path) extends Logger {
private[this] var outputStreamUsed: Boolean = false

lazy val outputStream = {
Expand All @@ -42,13 +88,14 @@ case class FileLogger(file: Path) extends Logger {

def info(s: String) = outputStream.println(s)
def error(s: String) = outputStream.println(s)
def ticker(s: String) = outputStream.println(s)
override def close() = {
if (outputStreamUsed)
outputStream.close()
}
}

case class MultiLogger(streams: Logger*) extends Logger {
case class MultiLogger(colored: Boolean, streams: Logger*) extends Logger {
lazy val outputStream: PrintStream =
new PrintStream(b => streams.foreach(_.outputStream.write(b))) {
override def flush() = streams.foreach(_.outputStream.flush())
Expand All @@ -57,5 +104,6 @@ case class MultiLogger(streams: Logger*) extends Logger {

def info(s: String) = streams.foreach(_.info(s))
def error(s: String) = streams.foreach(_.error(s))
def ticker(s: String) = streams.foreach(_.ticker(s))
override def close() = streams.foreach(_.close())
}
3 changes: 2 additions & 1 deletion scalalib/src/main/scala/mill/scalalib/Module.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ trait TestModule extends Module with TaskModule {
(runDepClasspath().map(_.path) :+ compile().classes.path).distinct.mkString(" "),
Seq(compile().classes.path).mkString(" "),
args.mkString(" "),
outputPath.toString
outputPath.toString,
T.ctx().log.colored.toString
),
workingDir = forkWorkingDir
)
Expand Down
10 changes: 9 additions & 1 deletion scalalib/src/main/scala/mill/scalalib/TestRunner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import java.net.URLClassLoader
import java.util.zip.ZipInputStream

import ammonite.ops.{Path, ls, pwd}
import ammonite.util.Colors
import mill.util.Ctx.LogCtx
import mill.util.PrintLogger
import sbt.testing._
import upickle.Js
import mill.util.JsonFormatters._

import scala.collection.mutable

object TestRunner {
Expand Down Expand Up @@ -49,7 +51,13 @@ object TestRunner {
entireClasspath = args(1).split(" ").map(Path(_)),
testClassfilePath = args(2).split(" ").map(Path(_)),
args = args(3) match{ case "" => Nil case x => x.split(" ").toList }
)(new PrintLogger(true, System.err, System.err))
)(new PrintLogger(
args(5) == "true",
if(args(5) == "true") Colors.Default
else Colors.BlackWhite,
System.err,
System.err
))
val outputPath = args(4)

ammonite.ops.write(Path(outputPath), upickle.default.write(result))
Expand Down

0 comments on commit fb68da0

Please sign in to comment.