Skip to content

Commit

Permalink
Forward bulk output to stderr when --show is called, reserving `s…
Browse files Browse the repository at this point in the history
…tdout` for the shown JSON blob
  • Loading branch information
lihaoyi committed Jan 6, 2018
1 parent c5271ee commit dd16d55
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 35 deletions.
2 changes: 1 addition & 1 deletion core/src/main/scala/mill/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ object Main {

val runner = new mill.main.MainRunner(
config, show,
System.out, System.err, System.in, System.out, System.err
System.out, System.err, System.in
)
if (repl){
runner.printInfo("Loading...")
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/scala/mill/eval/Evaluator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,10 @@ class Evaluator[T](val workspacePath: Path,
val out = System.out
val err = System.err
try{
System.setErr(multiLogger.outputStream)
System.setErr(multiLogger.errorStream)
System.setOut(multiLogger.outputStream)
Console.withOut(multiLogger.outputStream){
Console.withErr(multiLogger.outputStream){
Console.withErr(multiLogger.errorStream){
target.evaluate(args)
}
}
Expand Down
29 changes: 21 additions & 8 deletions core/src/main/scala/mill/main/MainRunner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,23 @@ import ammonite.ops.Path
import ammonite.util._
import mill.discover.Discovered
import mill.eval.{Evaluator, PathRef}
import mill.util.PrintLogger
import upickle.Js

/**
* Customized version of [[ammonite.MainRunner]], allowing us to run Mill
* `build.sc` scripts with mill-specific tweaks such as a custom
* `scriptCodeWrapper` or with a persistent evaluator between runs.
*/
class MainRunner(config: ammonite.main.Cli.Config, show: Boolean,
class MainRunner(config: ammonite.main.Cli.Config,
show: Boolean,
outprintStream: PrintStream,
errPrintStream: PrintStream,
stdIn: InputStream,
stdOut: OutputStream,
stdErr: OutputStream)
extends ammonite.MainRunner(config, outprintStream, errPrintStream, stdIn, stdOut, stdErr){
stdIn: InputStream)
extends ammonite.MainRunner(
config, outprintStream, errPrintStream,
stdIn, outprintStream, errPrintStream
){
var lastEvaluator: Option[(Seq[(Path, Long)], Evaluator[_])] = None

override def runScript(scriptPath: Path, scriptArgs: List[String]) =
Expand All @@ -29,8 +32,18 @@ class MainRunner(config: ammonite.main.Cli.Config, show: Boolean,
printing = true,
mainCfg => {
val (result, interpWatched) = RunScript.runScript(
mainCfg.wd, scriptPath, mainCfg.instantiateInterpreter(), scriptArgs, lastEvaluator,
errPrintStream, errPrintStream, colors
mainCfg.wd,
scriptPath,
mainCfg.instantiateInterpreter(),
scriptArgs,
lastEvaluator,
new PrintLogger(
colors != ammonite.util.Colors.BlackWhite,
colors,
if (show) errPrintStream else outprintStream,
errPrintStream,
errPrintStream
)
)

result match{
Expand All @@ -50,7 +63,7 @@ class MainRunner(config: ammonite.main.Cli.Config, show: Boolean,
case Res.Success(value) =>
if (show){
for(json <- value.asInstanceOf[Seq[Js.Value]]){
System.out.println(json)
outprintStream.println(json)
}
}

Expand Down
1 change: 1 addition & 0 deletions core/src/main/scala/mill/main/ReplApplyHandler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ object ReplApplyHandler{
new mill.util.PrintLogger(
colors != ammonite.util.Colors.BlackWhite,
colors,
System.out,
System.err,
System.err
)
Expand Down
7 changes: 2 additions & 5 deletions core/src/main/scala/mill/main/RunScript.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import mill.define.Task
import mill.discover.Mirror.Segment
import mill.discover.{Discovered, Mirror}
import mill.eval.{Evaluator, PathRef, Result}
import mill.util.{OSet, PrintLogger}
import mill.util.{Logger, OSet, PrintLogger}
import upickle.Js

/**
Expand All @@ -26,12 +26,9 @@ object RunScript{
instantiateInterpreter: => Either[(Res.Failing, Seq[(Path, Long)]), ammonite.interp.Interpreter],
scriptArgs: Seq[String],
lastEvaluator: Option[(Seq[(Path, Long)], Evaluator[_])],
infoStream: PrintStream,
errStream: PrintStream,
colors: ammonite.util.Colors)
log: Logger)
: (Res[(Evaluator[_], Seq[(Path, Long)], Either[String, Seq[Js.Value]])], Seq[(Path, Long)]) = {

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
15 changes: 9 additions & 6 deletions core/src/main/scala/mill/modules/Jvm.scala
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ object Jvm {

val stdout = proc.getInputStream
val stderr = proc.getErrorStream
val sources = Seq(stdout -> (Left(_: Bytes)), stderr -> (Right(_: Bytes)))
val sources = Seq(
(stdout, Left(_: Bytes), ctx.log.outputStream),
(stderr, Right(_: Bytes),ctx.log.errorStream )
)
val chunks = mutable.Buffer.empty[Either[Bytes, Bytes]]
while(
// Process.isAlive doesn't exist on JDK 7 =/
Expand All @@ -69,13 +72,13 @@ object Jvm {
stderr.available() > 0
){
var readSomething = false
for ((std, wrapper) <- sources){
while (std.available() > 0){
for ((subStream, wrapper, parentStream) <- sources){
while (subStream.available() > 0){
readSomething = true
val array = new Array[Byte](std.available())
val actuallyRead = std.read(array)
val array = new Array[Byte](subStream.available())
val actuallyRead = subStream.read(array)
chunks.append(wrapper(new ammonite.ops.Bytes(array)))
ctx.log.outputStream.write(array, 0, actuallyRead)
parentStream.write(array, 0, actuallyRead)
}
}
// if we did not read anything sleep briefly to avoid spinning
Expand Down
69 changes: 56 additions & 13 deletions core/src/main/scala/mill/util/Logger.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,38 @@ import ammonite.ops.Path
import ammonite.util.Colors


/**
* The standard logging interface of the Mill build tool.
*
* Contains four primary logging methods, in order of increasing importance:
*
* - `ticker`: short-lived logging output where consecutive lines over-write
* each other; useful for information which is transient and disposable
*
* - `info`: miscellaneous logging output which isn't part of the main output
* a user is looking for, but useful to provide context on what Mill is doing
*
* - `error`: logging output which represents problems the user should care
* about
*
* Also contains the two forwarded stdout and stderr streams, for code executed
* by Mill to use directly. Typically these correspond to the stdout and stderr,
* but when `--show` is used both are forwarded to stderr and stdout is only
* used to display the final `--show` output for easy piping.
*/
trait Logger {
def colored: Boolean
val errorStream: PrintStream
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 errorStream extends PrintStream(_ => ())
object outputStream extends PrintStream(_ => ())
def info(s: String) = ()
def error(s: String) = ()
Expand All @@ -33,36 +46,56 @@ object DummyLogger extends Logger {

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

var lastLineTicker = false
override val errorStream = new PrintStream(
new OutputStream {
override def write(b: Array[Byte]): Unit = {
lastLineTicker = false
errStream.write(b)
}

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

def write(b: Int) = {
lastLineTicker = false
errStream.write(b)
}
}
)
override val outputStream = new PrintStream(
new OutputStream {
override def write(b: Array[Byte]): Unit = {
lastLineTicker = false
infoStream.write(b)
outStream.write(b)
}

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

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


def info(s: String) = {
lastLineTicker = false
infoStream.println(colors.info()(s))
}
def error(s: String) = {
lastLineTicker = false
errorStream.println(colors.error()(s))
errStream.println(colors.error()(s))
}
def ticker(s: String) = {
if (lastLineTicker){
Expand All @@ -86,6 +119,11 @@ case class FileLogger(colored: Boolean, file: Path) extends Logger {
new PrintStream(new FileOutputStream(file.toIO.getAbsolutePath))
}

lazy val errorStream = {
outputStreamUsed = true
new PrintStream(new FileOutputStream(file.toIO.getAbsolutePath))
}

def info(s: String) = outputStream.println(s)
def error(s: String) = outputStream.println(s)
def ticker(s: String) = outputStream.println(s)
Expand All @@ -101,6 +139,11 @@ case class MultiLogger(colored: Boolean, streams: Logger*) extends Logger {
override def flush() = streams.foreach(_.outputStream.flush())
override def close() = streams.foreach(_.outputStream.close())
}
lazy val errorStream: PrintStream =
new PrintStream(b => streams.foreach(_.outputStream.write(b))) {
override def flush() = streams.foreach(_.outputStream.flush())
override def close() = streams.foreach(_.outputStream.close())
}

def info(s: String) = streams.foreach(_.info(s))
def error(s: String) = streams.foreach(_.error(s))
Expand Down
1 change: 1 addition & 0 deletions scalalib/src/main/scala/mill/scalalib/TestRunner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ object TestRunner {
args(5) == "true",
if(args(5) == "true") Colors.Default
else Colors.BlackWhite,
System.out,
System.err,
System.err
))
Expand Down

0 comments on commit dd16d55

Please sign in to comment.