mirror of
https://github.com/sbt/sbt.git
synced 2026-09-07 11:01:08 +02:00
the custom scalac Reporter now delegates to an instance of an sbt interface called xsbti.Reporter handling compilation logging is now mainly done on the sbt-side of the compiler interface the xsbti.Reporter interface provides access to richer information about errors and warnings, including source file, line, and offset xsbti.Reporter can be implemented by users to get access to detailed information without needing to parse the logging output the CompileFailed exception that is thrown when compilation fails now includes an array of the problems, providing detailed error and warning information that can, for example, be consumed by doing a mapFailure on 'compile' and using 'Compile.allProblems'
153 lines
3.8 KiB
Scala
153 lines
3.8 KiB
Scala
/* sbt -- Simple Build Tool
|
|
* Copyright 2008, 2009, 2010 Mark Harrah
|
|
*/
|
|
package sbt
|
|
|
|
// The following code is based on scala.tools.nsc.reporters.{AbstractReporter, ConsoleReporter, Reporter}
|
|
// Copyright 2002-2009 LAMP/EPFL
|
|
// see licenses/LICENSE_Scala
|
|
// Original author: Martin Odersky
|
|
|
|
import xsbti.{Maybe,Position,Problem,Reporter,Severity}
|
|
import java.util.EnumMap
|
|
import scala.collection.mutable
|
|
import LoggerReporter._
|
|
import Severity.{Error,Info,Warn}
|
|
|
|
object LoggerReporter
|
|
{
|
|
def m2o[S](m: Maybe[S]): Option[S] = if(m.isDefined) Some(m.get) else None
|
|
|
|
final class PositionKey(pos: Position)
|
|
{
|
|
def offset = pos.offset
|
|
def sourceFile = pos.sourceFile
|
|
|
|
override def equals(o: Any) =
|
|
o match { case pk: PositionKey => equalsKey(pk); case _ => false }
|
|
|
|
def equalsKey(o: PositionKey) =
|
|
m2o(pos.offset) == m2o(o.offset) &&
|
|
m2o(pos.sourceFile) == m2o(o.sourceFile)
|
|
override def hashCode =
|
|
m2o(pos.offset).hashCode * 31
|
|
m2o(pos.sourceFile).hashCode
|
|
}
|
|
|
|
def countElementsAsString(n: Int, elements: String): String =
|
|
n match {
|
|
case 0 => "no " + elements + "s"
|
|
case 1 => "one " + elements
|
|
case 2 => "two " + elements + "s"
|
|
case 3 => "three " + elements + "s"
|
|
case 4 => "four " + elements + "s"
|
|
case _ => "" + n + " " + elements + "s"
|
|
}
|
|
}
|
|
|
|
class LoggerReporter(maximumErrors: Int, log: Logger) extends xsbti.Reporter
|
|
{
|
|
val positions = new mutable.HashMap[PositionKey, Severity]
|
|
val count = new EnumMap[Severity, Int](classOf[Severity])
|
|
private val allProblems = new mutable.ListBuffer[Problem]
|
|
|
|
reset()
|
|
|
|
def reset()
|
|
{
|
|
count.put(Warn, 0)
|
|
count.put(Info, 0)
|
|
count.put(Error, 0)
|
|
positions.clear()
|
|
allProblems.clear()
|
|
}
|
|
def hasWarnings = count.get(Warn) > 0
|
|
def hasErrors = count.get(Error) > 0
|
|
def problems = allProblems.toArray
|
|
|
|
def printSummary()
|
|
{
|
|
val warnings = count.get(Severity.Warn)
|
|
if(warnings > 0)
|
|
log.warn(countElementsAsString(warnings, "warning") + " found")
|
|
val errors = count.get(Severity.Error)
|
|
if(errors > 0)
|
|
log.error(countElementsAsString(errors, "error") + " found")
|
|
}
|
|
|
|
def inc(sev: Severity) = count.put(sev, count.get(sev) + 1)
|
|
|
|
def display(pos: Position, msg: String, severity: Severity)
|
|
{
|
|
inc(severity)
|
|
if(severity != Warn || maximumErrors <= 0 || count.get(severity) <= maximumErrors)
|
|
print(severityLogger(severity), pos, msg)
|
|
}
|
|
def severityLogger(severity: Severity): (=> String) => Unit =
|
|
m =>
|
|
{
|
|
(severity match
|
|
{
|
|
case Error => log.error(m)
|
|
case Warn => log.warn(m)
|
|
case Info => log.info(m)
|
|
})
|
|
}
|
|
|
|
def print(log: (=> String) => Unit, pos: Position, msg: String)
|
|
{
|
|
if(pos.sourcePath.isEmpty && pos.line.isEmpty)
|
|
log(msg)
|
|
else
|
|
{
|
|
val sourcePrefix = m2o(pos.sourcePath).getOrElse("")
|
|
val lineNumberString = m2o(pos.line).map(":" + _ + ":").getOrElse(":") + " "
|
|
log(sourcePrefix + lineNumberString + msg)
|
|
val lineContent = pos.lineContent
|
|
if(!lineContent.isEmpty)
|
|
{
|
|
log(lineContent)
|
|
for(space <- m2o(pos.pointerSpace))
|
|
log(space + "^") // pointer to the column position of the error/warning
|
|
}
|
|
}
|
|
}
|
|
|
|
def log(pos: Position, msg: String, severity: Severity): Unit =
|
|
{
|
|
allProblems += problem(pos, msg, severity)
|
|
severity match
|
|
{
|
|
case Warn | Error =>
|
|
{
|
|
if(!testAndLog(pos, severity))
|
|
display(pos, msg, severity)
|
|
}
|
|
case _ => display(pos, msg, severity)
|
|
}
|
|
}
|
|
def problem(pos: Position, msg: String, sev: Severity): Problem =
|
|
new Problem
|
|
{
|
|
val position = pos
|
|
val message = msg
|
|
val severity = sev
|
|
}
|
|
|
|
def testAndLog(pos: Position, severity: Severity): Boolean =
|
|
{
|
|
if(pos.offset.isEmpty || pos.sourceFile.isEmpty)
|
|
false
|
|
else
|
|
{
|
|
val key = new PositionKey(pos)
|
|
if(positions.get(key).map(_.ordinal >= severity.ordinal).getOrElse(false))
|
|
true
|
|
else
|
|
{
|
|
positions(key) = severity
|
|
false
|
|
}
|
|
}
|
|
}
|
|
} |