From a76d75bca695b51aec0838da04693fec95d67169 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Sat, 23 Oct 2010 16:34:22 -0400 Subject: [PATCH] more flexible scalac logging 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' --- compile/AnalyzingCompiler.scala | 19 ++- compile/LoggerReporter.scala | 153 ++++++++++++++++++ compile/RawCompiler.scala | 1 + compile/interface/CompilerInterface.scala | 10 +- compile/interface/ConsoleInterface.scala | 2 +- compile/interface/DelegatingReporter.scala | 119 ++++++++++++++ compile/interface/LoggerReporter.scala | 118 -------------- compile/interface/ScaladocInterface.scala | 17 +- .../src/main/java/xsbti/CompileFailed.java | 1 + interface/src/main/java/xsbti/Maybe.java | 30 ++++ interface/src/main/java/xsbti/Position.java | 18 +++ interface/src/main/java/xsbti/Problem.java | 11 ++ interface/src/main/java/xsbti/Reporter.java | 20 +++ interface/src/main/java/xsbti/Severity.java | 9 ++ main/Compile.scala | 10 ++ sbt/package.scala | 2 + tasks/Incomplete.scala | 3 + 17 files changed, 405 insertions(+), 138 deletions(-) create mode 100644 compile/LoggerReporter.scala create mode 100644 compile/interface/DelegatingReporter.scala delete mode 100644 compile/interface/LoggerReporter.scala create mode 100644 interface/src/main/java/xsbti/Maybe.java create mode 100644 interface/src/main/java/xsbti/Position.java create mode 100644 interface/src/main/java/xsbti/Problem.java create mode 100644 interface/src/main/java/xsbti/Reporter.java create mode 100644 interface/src/main/java/xsbti/Severity.java diff --git a/compile/AnalyzingCompiler.scala b/compile/AnalyzingCompiler.scala index 82f85ae54..e858ee6a1 100644 --- a/compile/AnalyzingCompiler.scala +++ b/compile/AnalyzingCompiler.scala @@ -4,7 +4,7 @@ package sbt package compile - import xsbti.{AnalysisCallback, Logger => xLogger} + import xsbti.{AnalysisCallback, Logger => xLogger, Reporter} import java.io.File import java.net.{URL, URLClassLoader} @@ -12,7 +12,7 @@ package compile * provided by scalaInstance. This class requires a ComponentManager in order to obtain the interface code to scalac and * the analysis plugin. Because these call Scala code for a different Scala version than the one used for this class, they must * be compiled for the version of Scala being used.*/ -class AnalyzingCompiler(val scalaInstance: ScalaInstance, val manager: ComponentManager, val cp: ClasspathOptions, log: Logger) extends NotNull +class AnalyzingCompiler(val scalaInstance: ScalaInstance, val manager: ComponentManager, val cp: ClasspathOptions, log: Logger) { def this(scalaInstance: ScalaInstance, manager: ComponentManager, log: Logger) = this(scalaInstance, manager, ClasspathOptions.auto, log) def apply(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], callback: AnalysisCallback, maximumErrors: Int, log: Logger) @@ -20,16 +20,23 @@ class AnalyzingCompiler(val scalaInstance: ScalaInstance, val manager: Component val arguments = (new CompilerArguments(scalaInstance, cp))(sources, classpath, outputDirectory, options) compile(arguments, callback, maximumErrors, log) } - def compile(arguments: Seq[String], callback: AnalysisCallback, maximumErrors: Int, log: Logger) + + def compile(arguments: Seq[String], callback: AnalysisCallback, maximumErrors: Int, log: Logger): Unit = + compile(arguments, callback, log, new LoggerReporter(maximumErrors, log)) + def compile(arguments: Seq[String], callback: AnalysisCallback, log: Logger, reporter: Reporter) { call("xsbt.CompilerInterface", log)( - classOf[Array[String]], classOf[AnalysisCallback], classOf[Int], classOf[xLogger] ) ( - arguments.toArray[String] : Array[String], callback, maximumErrors: java.lang.Integer, log ) + classOf[Array[String]], classOf[AnalysisCallback], classOf[xLogger], classOf[Reporter] ) ( + arguments.toArray[String] : Array[String], callback, log, reporter ) } + def doc(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], maximumErrors: Int, log: Logger): Unit = + doc(sources, classpath, outputDirectory, options, log, new LoggerReporter(maximumErrors, log)) + def doc(sources: Seq[File], classpath: Seq[File], outputDirectory: File, options: Seq[String], log: Logger, reporter: Reporter): Unit = { val arguments = (new CompilerArguments(scalaInstance, cp))(sources, classpath, outputDirectory, options) - call("xsbt.ScaladocInterface", log) (classOf[Array[String]], classOf[Int], classOf[xLogger]) (arguments.toArray[String] : Array[String], maximumErrors: java.lang.Integer, log) + call("xsbt.ScaladocInterface", log) (classOf[Array[String]], classOf[xLogger], classOf[Reporter]) ( + arguments.toArray[String] : Array[String], log, reporter) } def console(classpath: Seq[File], options: Seq[String], initialCommands: String, log: Logger)(loader: Option[ClassLoader] = None, bindings: Seq[(String, Any)] = Nil): Unit = { diff --git a/compile/LoggerReporter.scala b/compile/LoggerReporter.scala new file mode 100644 index 000000000..23ad160b9 --- /dev/null +++ b/compile/LoggerReporter.scala @@ -0,0 +1,153 @@ +/* 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 + } + } + } +} \ No newline at end of file diff --git a/compile/RawCompiler.scala b/compile/RawCompiler.scala index c0241da86..02aa4a60a 100644 --- a/compile/RawCompiler.scala +++ b/compile/RawCompiler.scala @@ -36,5 +36,6 @@ class RawCompiler(val scalaInstance: ScalaInstance, cp: ClasspathOptions, log: L } class CompileFailed(val arguments: Array[String], override val toString: String) extends xsbti.CompileFailed { + def problems = Array() override def fillInStackTrace = this } \ No newline at end of file diff --git a/compile/interface/CompilerInterface.scala b/compile/interface/CompilerInterface.scala index adab14236..918b3f855 100644 --- a/compile/interface/CompilerInterface.scala +++ b/compile/interface/CompilerInterface.scala @@ -3,13 +3,13 @@ */ package xsbt -import xsbti.{AnalysisCallback,Logger} +import xsbti.{AnalysisCallback,Logger,Problem,Reporter} import scala.tools.nsc.{Phase, SubComponent} import Log.debug class CompilerInterface { - def run(args: Array[String], callback: AnalysisCallback, maximumErrors: Int, log: Logger) + def run(args: Array[String], callback: AnalysisCallback, log: Logger, delegate: Reporter) { import scala.tools.nsc.{Global, Settings} @@ -17,7 +17,7 @@ class CompilerInterface val settings = new Settings(Log.settingsError(log)) val command = Command(args.toList, settings) - val reporter = LoggerReporter(settings, maximumErrors, log) + val reporter = DelegatingReporter(settings, delegate) def noErrors = !reporter.hasErrors && command.ok val phasesSet = new scala.collection.mutable.HashSet[Any] // 2.7 compatibility @@ -84,8 +84,8 @@ class CompilerInterface if(!noErrors) { debug(log, "Compilation failed (CompilerInterface)") - throw new InterfaceCompileFailed(args, "Compilation failed") + throw new InterfaceCompileFailed(args, reporter.problems, "Compilation failed") } } } -class InterfaceCompileFailed(val arguments: Array[String], override val toString: String) extends xsbti.CompileFailed \ No newline at end of file +class InterfaceCompileFailed(val arguments: Array[String], val problems: Array[Problem], override val toString: String) extends xsbti.CompileFailed \ No newline at end of file diff --git a/compile/interface/ConsoleInterface.scala b/compile/interface/ConsoleInterface.scala index 6acf881e6..ae6a37f41 100644 --- a/compile/interface/ConsoleInterface.scala +++ b/compile/interface/ConsoleInterface.scala @@ -59,6 +59,6 @@ object MakeSettings if(command.ok) command.settings else - throw new InterfaceCompileFailed(Array(), command.usageMsg) + throw new InterfaceCompileFailed(Array(), Array(), command.usageMsg) } } diff --git a/compile/interface/DelegatingReporter.scala b/compile/interface/DelegatingReporter.scala new file mode 100644 index 000000000..d791f95fe --- /dev/null +++ b/compile/interface/DelegatingReporter.scala @@ -0,0 +1,119 @@ +/* sbt -- Simple Build Tool + * Copyright 2008, 2009, 2010 Mark Harrah + */ +package xsbt + + import xsbti.{F0,Logger,Maybe} + import java.io.File + +private object DelegatingReporter +{ + def apply(settings: scala.tools.nsc.Settings, delegate: xsbti.Reporter): DelegatingReporter = + new DelegatingReporter(Command.getWarnFatal(settings), delegate) +} + +private trait ReporterCompat27 { + // this method is not in 2.7.7, so we need to have a dummy interface or scalac complains nothing is overridden + def hasWarnings: Boolean +} +// The following code is based on scala.tools.nsc.reporters.{AbstractReporter, ConsoleReporter} +// Copyright 2002-2009 LAMP/EPFL +// Original author: Martin Odersky +private final class DelegatingReporter(warnFatal: Boolean, delegate: xsbti.Reporter) extends scala.tools.nsc.reporters.Reporter with ReporterCompat27 +{ + import scala.tools.nsc.util.{FakePos,NoPosition,Position} + + def error(msg: String) { error(FakePos("scalac"), msg) } + + def printSummary() = delegate.printSummary() + + // this helps keep source compatibility with the changes in 2.8 : Position.{source,line,column} are no longer Option[X]s, just plain Xs + // so, we normalize to Option[X] + private def o[T](t: Option[T]): Option[T] = t + private def o[T](t: T): Option[T] = Some(t) + + override def hasErrors = delegate.hasErrors + override def hasWarnings = delegate.hasWarnings + def problems = delegate.problems + + override def reset = + { + super.reset + delegate.reset + } + protected def info0(pos: Position, msg: String, rawSeverity: Severity, force: Boolean) + { + val severity = if(warnFatal && rawSeverity == WARNING) ERROR else rawSeverity + delegate.log(convert(pos), msg, convert(rawSeverity)) + } + private[this] def convert(posIn: Position): xsbti.Position = + { + val pos = + posIn match + { + case null | NoPosition => NoPosition + case x: FakePos => x + case x => + posIn.inUltimateSource(o(posIn.source).get) + } + pos match + { + case NoPosition | FakePos(_) => position(None, None, None, "", None, None, None) + case _ => makePosition(pos) + } + } + private[this] def makePosition(pos: Position): xsbti.Position = + { + val srcO = o(pos.source) + val opt(sourcePath, sourceFile) = for(src <- srcO) yield (src.file.path, src.file.file) + val line = o(pos.line) + if(!line.isEmpty) + { + val lineContent = pos.lineContent.stripLineEnd + val offsetO = o(pos.offset) + val opt(pointer, pointerSpace) = + for(offset <- offsetO; src <- srcO) yield + { + val pointer = offset - src.lineToOffset(src.offsetToLine(offset)) + val pointerSpace = ((lineContent: Seq[Char]).take(pointer).map { case '\t' => '\t'; case x => ' ' }).mkString + (pointer, pointerSpace) + } + position(sourcePath, sourceFile, line, lineContent, offsetO, pointer, pointerSpace) + } + else + position(sourcePath, sourceFile, line, "", None, None, None) + } + private[this] object opt + { + def unapply[A,B](o: Option[(A,B)]): Some[(Option[A], Option[B])] = + Some(o match + { + case Some((a,b)) => (Some(a), Some(b)) + case None => (None, None) + }) + } + private[this] def position(sourcePath0: Option[String], sourceFile0: Option[File], line0: Option[Int], lineContent0: String, offset0: Option[Int], pointer0: Option[Int], pointerSpace0: Option[String]) = + new xsbti.Position + { + val line = o2mi(line0) + val lineContent = lineContent0 + val offset = o2mi(offset0) + val sourcePath = o2m(sourcePath0) + val sourceFile = o2m(sourceFile0) + val pointer = o2mi(pointer0) + val pointerSpace = o2m(pointerSpace0) + } + + import xsbti.Severity.{Info, Warn, Error} + private[this] def convert(sev: Severity): xsbti.Severity = + sev match + { + case INFO => Info + case WARNING => Warn + case ERROR => Error + } + + import java.lang.{Integer => I} + private[this] def o2mi(opt: Option[Int]): Maybe[I] = opt match { case None => Maybe.nothing[I]; case Some(s) => Maybe.just[I](s) } + private[this] def o2m[S](opt: Option[S]): Maybe[S] = opt match { case None => Maybe.nothing[S]; case Some(s) => Maybe.just(s) } +} \ No newline at end of file diff --git a/compile/interface/LoggerReporter.scala b/compile/interface/LoggerReporter.scala deleted file mode 100644 index 2455cbb03..000000000 --- a/compile/interface/LoggerReporter.scala +++ /dev/null @@ -1,118 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package xsbt - -import xsbti.{F0,Logger} - -private object LoggerReporter -{ - def apply(settings: scala.tools.nsc.Settings, maximumErrors: Int, log: Logger): LoggerReporter = - new LoggerReporter(Command.getWarnFatal(settings), maximumErrors, log) -} - -// The following code is based on scala.tools.nsc.reporters.{AbstractReporter, ConsoleReporter} -// Copyright 2002-2009 LAMP/EPFL -// Original author: Martin Odersky -private final class LoggerReporter(warnFatal: Boolean, maximumErrors: Int, log: Logger) extends scala.tools.nsc.reporters.Reporter -{ - import scala.tools.nsc.util.{FakePos,NoPosition,Position} - private val positions = new scala.collection.mutable.HashMap[Position, Severity] - - def error(msg: String) { error(FakePos("scalac"), msg) } - - def printSummary() - { - if(WARNING.count > 0) - log.warn(Message(countElementsAsString(WARNING.count, "warning") + " found")) - if(ERROR.count > 0) - log.error(Message(countElementsAsString(ERROR.count, "error") + " found")) - } - - def display(pos: Position, msg: String, severity: Severity) - { - severity.count += 1 - if(severity != ERROR || maximumErrors < 0 || severity.count <= maximumErrors) - print(severityLogger(severity), pos, msg) - } - private def severityLogger(severity: Severity) = - (m: F0[String]) => - { - (severity match - { - case ERROR => log.error(m) - case WARNING => log.warn(m) - case INFO => log.info(m) - }) - } - - // this helps keep source compatibility with the changes in 2.8 : Position.{source,line,column} are no longer Option[X]s, just plain Xs - // so, we normalize to Option[X] - private def o[T](t: Option[T]): Option[T] = t - private def o[T](t: T): Option[T] = Some(t) - private def print(logger: F0[String] => Unit, posIn: Position, msg: String) - { - def log(s: => String) = logger(Message(s)) - val pos = - posIn match - { - case null | NoPosition => NoPosition - case x: FakePos => x - case x => - posIn.inUltimateSource(o(posIn.source).get) - } - pos match - { - case NoPosition => log(msg) - case FakePos(fmsg) => log(fmsg+" "+msg) - case _ => - val sourcePrefix = o(pos.source).map(_.file.path).getOrElse("") - val lineNumberString = o(pos.line).map(line => ":" + line + ":").getOrElse(":") + " " - log(sourcePrefix + lineNumberString + msg) - if (!o(pos.line).isEmpty) - { - val lineContent = pos.lineContent.stripLineEnd - log(lineContent) // source line with error/warning - for(offset <- o(pos.offset); src <- o(pos.source)) - { - val pointer = offset - src.lineToOffset(src.offsetToLine(offset)) - val pointerSpace = (lineContent: Seq[Char]).take(pointer).map { case '\t' => '\t'; case x => ' ' } - log(pointerSpace.mkString + "^") // pointer to the column position of the error/warning - } - } - } - } - override def reset = - { - super.reset - positions.clear - } - - - protected def info0(pos: Position, msg: String, rawSeverity: Severity, force: Boolean) - { - val severity = if(warnFatal && rawSeverity == WARNING) ERROR else rawSeverity - severity match - { - case WARNING | ERROR => - { - if(!testAndLog(pos, severity)) - display(pos, msg, severity) - } - case _ => display(pos, msg, severity) - } - } - - private def testAndLog(pos: Position, severity: Severity): Boolean = - { - if(pos == null || pos.offset.isEmpty) - false - else if(positions.get(pos).map(_ >= severity).getOrElse(false)) - true - else - { - positions(pos) = severity - false - } - } -} \ No newline at end of file diff --git a/compile/interface/ScaladocInterface.scala b/compile/interface/ScaladocInterface.scala index 22d2edecd..a546795f1 100644 --- a/compile/interface/ScaladocInterface.scala +++ b/compile/interface/ScaladocInterface.scala @@ -3,19 +3,20 @@ */ package xsbt -import xsbti.Logger -import Log.debug + import xsbti.Logger + import Log.debug class ScaladocInterface { - def run(args: Array[String], maximumErrors: Int, log: Logger) = (new Runner(args, maximumErrors, log)).run + def run(args: Array[String], log: Logger, delegate: xsbti.Reporter) = (new Runner(args, log, delegate)).run } -private class Runner(args: Array[String], maximumErrors: Int, log: Logger) +private class Runner(args: Array[String], log: Logger, delegate: xsbti.Reporter) { - import scala.tools.nsc.{doc, Global} + import scala.tools.nsc.{doc, Global, reporters} + import reporters.Reporter val docSettings: doc.Settings = new doc.Settings(Log.settingsError(log)) val command = Command(args.toList, docSettings) - val reporter = LoggerReporter(docSettings, maximumErrors, log) + val reporter = DelegatingReporter(docSettings, delegate) def noErrors = !reporter.hasErrors && command.ok import forScope._ @@ -29,12 +30,12 @@ private class Runner(args: Array[String], maximumErrors: Int, log: Logger) processor.document(command.files) } reporter.printSummary() - if(!noErrors) throw new InterfaceCompileFailed(args, "Scaladoc generation failed") + if(!noErrors) throw new InterfaceCompileFailed(args, reporter.problems, "Scaladoc generation failed") } object forScope { - class DocFactory(reporter: LoggerReporter, docSettings: doc.Settings) // 2.7 compatibility + class DocFactory(reporter: Reporter, docSettings: doc.Settings) // 2.7 compatibility { object compiler extends Global(command.settings, reporter) { diff --git a/interface/src/main/java/xsbti/CompileFailed.java b/interface/src/main/java/xsbti/CompileFailed.java index bb5b2a93a..f1cbbc61b 100644 --- a/interface/src/main/java/xsbti/CompileFailed.java +++ b/interface/src/main/java/xsbti/CompileFailed.java @@ -3,4 +3,5 @@ package xsbti; public abstract class CompileFailed extends RuntimeException { public abstract String[] arguments(); + public abstract Problem[] problems(); } \ No newline at end of file diff --git a/interface/src/main/java/xsbti/Maybe.java b/interface/src/main/java/xsbti/Maybe.java new file mode 100644 index 000000000..f730ef918 --- /dev/null +++ b/interface/src/main/java/xsbti/Maybe.java @@ -0,0 +1,30 @@ +/* sbt -- Simple Build Tool + * Copyright 2008, 2009, 2010 Mark Harrah + */ +package xsbti; + +/** Intended as a lightweight carrier for scala.Option. */ +public abstract class Maybe +{ + // private pending Scala bug #3642 + protected Maybe() {} + + public static Maybe just(final s v) + { + return new Maybe() { + public boolean isDefined() { return true; } + public s get() { return v; } + }; + } + public static Maybe nothing() + { + return new Maybe() { + public boolean isDefined() { return false; } + public s get() { throw new UnsupportedOperationException("nothing.get"); } + }; + } + + public final boolean isEmpty() { return !isDefined(); } + public abstract boolean isDefined(); + public abstract t get(); +} \ No newline at end of file diff --git a/interface/src/main/java/xsbti/Position.java b/interface/src/main/java/xsbti/Position.java new file mode 100644 index 000000000..96c60ebb2 --- /dev/null +++ b/interface/src/main/java/xsbti/Position.java @@ -0,0 +1,18 @@ +/* sbt -- Simple Build Tool + * Copyright 2008, 2009, 2010 Mark Harrah + */ +package xsbti; + +public interface Position +{ + Maybe line(); + String lineContent(); + Maybe offset(); + + // pointer to the column position of the error/warning + Maybe pointer(); + Maybe pointerSpace(); + + Maybe sourcePath(); + Maybe sourceFile(); +} \ No newline at end of file diff --git a/interface/src/main/java/xsbti/Problem.java b/interface/src/main/java/xsbti/Problem.java new file mode 100644 index 000000000..cf2641900 --- /dev/null +++ b/interface/src/main/java/xsbti/Problem.java @@ -0,0 +1,11 @@ +/* sbt -- Simple Build Tool + * Copyright 2008, 2009, 2010 Mark Harrah + */ +package xsbti; + +public interface Problem +{ + Severity severity(); + String message(); + Position position(); +} \ No newline at end of file diff --git a/interface/src/main/java/xsbti/Reporter.java b/interface/src/main/java/xsbti/Reporter.java new file mode 100644 index 000000000..8556cbe8a --- /dev/null +++ b/interface/src/main/java/xsbti/Reporter.java @@ -0,0 +1,20 @@ +/* sbt -- Simple Build Tool + * Copyright 2008, 2009, 2010 Mark Harrah + */ +package xsbti; + +public interface Reporter +{ + /** Resets logging, including any accumulated errors, warnings, messages, and counts.*/ + public void reset(); + /** Returns true if this logger has seen any errors since the last call to reset.*/ + public boolean hasErrors(); + /** Returns true if this logger has seen any warnings since the last call to reset.*/ + public boolean hasWarnings(); + /** Logs a summary of logging since the last reset.*/ + public void printSummary(); + /** Returns a list of warnings and errors since the last reset.*/ + public Problem[] problems(); + /** Logs a message.*/ + public void log(Position pos, String msg, Severity sev); +} \ No newline at end of file diff --git a/interface/src/main/java/xsbti/Severity.java b/interface/src/main/java/xsbti/Severity.java new file mode 100644 index 000000000..09aed574b --- /dev/null +++ b/interface/src/main/java/xsbti/Severity.java @@ -0,0 +1,9 @@ +/* sbt -- Simple Build Tool + * Copyright 2008, 2009, 2010 Mark Harrah + */ +package xsbti; + +public enum Severity +{ + Info, Warn, Error +} \ No newline at end of file diff --git a/main/Compile.scala b/main/Compile.scala index 8bbb7752c..c6fd53eae 100644 --- a/main/Compile.scala +++ b/main/Compile.scala @@ -12,6 +12,16 @@ object Compile { val DefaultMaxErrors = 100 + def allProblems(inc: Incomplete): Seq[Problem] = + allProblems(inc :: Nil) + def allProblems(incs: Seq[Incomplete]): Seq[Problem] = + problems(Incomplete.allExceptions(incs).toSeq) + def problems(es: Seq[Throwable]): Seq[Problem] = + es flatMap { + case cf: xsbti.CompileFailed => cf.problems + case _ => Nil + } + final class Inputs(val compilers: Compilers, val config: Options, val incSetup: IncSetup, val log: Logger) final class Options(val classpath: Seq[File], val sources: Seq[File], val classesDirectory: File, val options: Seq[String], val javacOptions: Seq[String], val maxErrors: Int) final class IncSetup(val javaSrcBases: Seq[File], val analysisMap: Map[File, Analysis], val cacheDirectory: File) diff --git a/sbt/package.scala b/sbt/package.scala index 502329835..1a46c2b4c 100644 --- a/sbt/package.scala +++ b/sbt/package.scala @@ -4,4 +4,6 @@ package object sbt extends sbt.std.TaskExtra with sbt.Types with sbt.ProcessExtra with sbt.impl.DependencyBuilders with sbt.PathExtra { type File = java.io.File + implicit def maybeToOption[S](m: xsbti.Maybe[S]): Option[S] = + if(m.isDefined) Some(m.get) else None } diff --git a/tasks/Incomplete.scala b/tasks/Incomplete.scala index 9d08cdb47..c3fa5fc23 100644 --- a/tasks/Incomplete.scala +++ b/tasks/Incomplete.scala @@ -16,6 +16,9 @@ object Incomplete extends Enumeration { val causeStr = if(i.causes.isEmpty) "" else (i.causes.length + " cause(s)") "Incomplete (" + show(i.tpe) + ") " + i.message.getOrElse("") + causeStr + "\n" + traces } + + def allExceptions(is: Seq[Incomplete]): Iterable[Throwable] = + allExceptions(new Incomplete(causes = is)) def allExceptions(i: Incomplete): Iterable[Throwable] = { val exceptions = IDSet.create[Throwable]