diff --git a/internal/zinc-compile-core/src/main/contraband/reporter.json b/internal/zinc-compile-core/src/main/contraband/reporter.json index 7519f31a3c..dbe50f8c69 100644 --- a/internal/zinc-compile-core/src/main/contraband/reporter.json +++ b/internal/zinc-compile-core/src/main/contraband/reporter.json @@ -9,7 +9,9 @@ { "name": "loggerName", "type": "String" }, { "name": "maximumErrors", "type": "int" }, { "name": "useColor", "type": "boolean" }, - { "name": "logLevel", "type": "java.util.logging.Level" }, + { "name": "msgFilters", "type": "java.util.regex.Pattern*" }, + { "name": "fileFilters", "type": "java.util.regex.Pattern*" }, + { "name": "logLevel", "type": "java.util.logging.Level" }, { "name": "positionMapper", "type": "java.util.function.Function" } ] } diff --git a/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/FilteredReporter.scala b/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/FilteredReporter.scala new file mode 100644 index 0000000000..a7d50194b7 --- /dev/null +++ b/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/FilteredReporter.scala @@ -0,0 +1,45 @@ +package sbt.internal.inc + +import sbt.internal.util.ManagedLogger +import xsbti.{ Position, Problem, Severity } + +import scala.util.matching.Regex + +/** + * Defines a filtered reporter as Pants Zinc's fork does letting users control which messages + * are reported or not. This implementation has been adapted from the Pants repository. + * + * @link https://github.com/pantsbuild/pants/blob/master/src/scala/org/pantsbuild/zinc/logging/Reporters.scala#L28 + * + * This reporter may be useful to companies that have domain-specific knowledge + * about compile messages that are not relevant and can be filtered out, or users + * that hold similar knowledge about the piece of code that they compile. + */ +class FilteredReporter( + fileFilters: Array[Regex], + msgFilters: Array[Regex], + maximumErrors: Int, + logger: ManagedLogger, + positionMapper: Position => Position +) extends LoggerReporter(maximumErrors, logger, positionMapper) { + private final def isFiltered(filters: Seq[Regex], str: String): Boolean = + filters.exists(_.findFirstIn(str).isDefined) + + private final def isFiltered(pos: Position, msg: String, severity: Severity): Boolean = { + severity != Severity.Error && ( + (pos.sourceFile.isPresent && isFiltered(fileFilters, pos.sourceFile.get.getPath)) || + (isFiltered(msgFilters, msg)) + ) + } + + /** + * Redefines display so that non-error messages whose paths match a regex in `fileFilters` + * or whose messages' content match `msgFilters` are not reported to the user. + */ + override def display(problem: Problem): Unit = { + val severity = problem.severity() + val dontShow = isFiltered(problem.position(), problem.message(), severity) + if (dontShow) inc(severity) + else super.display(problem) + } +} diff --git a/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/LoggerReporter.scala b/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/LoggerReporter.scala index 46c05d0cf7..11838dcb3e 100644 --- a/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/LoggerReporter.scala +++ b/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/LoggerReporter.scala @@ -103,7 +103,7 @@ class LoggerReporter( logger.error(countElementsAsString(errors, "error") + " found") } - private def inc(sev: Severity) = count.put(sev, count.get(sev) + 1) + protected def inc(sev: Severity) = count.put(sev, count.get(sev) + 1) // this is used by sbt private[sbt] def display(p: Problem): Unit = { diff --git a/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/ReporterManager.scala b/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/ReporterManager.scala index f3a3bc3194..c430e6709f 100644 --- a/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/ReporterManager.scala +++ b/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/ReporterManager.scala @@ -5,14 +5,16 @@ import java.nio.charset.StandardCharsets import java.util.logging.Level import sbt.internal.util.{ ConsoleAppender, MainAppender } -import sbt.util.{ LogExchange } +import sbt.util.LogExchange import sbt.util.{ Level => SbtLevel } import xsbti.{ Position, Reporter, ReporterConfig } +import scala.util.matching.Regex + object ReporterManager { import java.util.concurrent.atomic.AtomicInteger private val idGenerator: AtomicInteger = new AtomicInteger - private val DefaultReporterName = "zinc-out" + private val DefaultName = "zinc-out" private def generateZincReporterId(name: String): String = s"$name-${idGenerator.incrementAndGet}" @@ -45,7 +47,7 @@ object ReporterManager { } } - private val FallbackUseColor = ConsoleAppender.formatEnabled + private val UseColor = ConsoleAppender.formatEnabled private val NoPositionMapper = java.util.function.Function.identity[Position]() import java.util.function.{ Function => JavaFunction } @@ -56,7 +58,7 @@ object ReporterManager { /** Returns sane defaults with a long tradition in sbt. */ def getDefaultReporterConfig: ReporterConfig = - new ReporterConfig(DefaultReporterName, 100, FallbackUseColor, Level.INFO, NoPositionMapper) + new ReporterConfig(DefaultName, 100, UseColor, Array(), Array(), Level.INFO, NoPositionMapper) def getReporter(toOutput: PrintWriter, config: ReporterConfig): Reporter = { val printWriterToAppender = MainAppender.defaultBacked(config.useColor()) @@ -69,7 +71,16 @@ object ReporterManager { val sbtLogLevel = fromJavaLogLevel(config.logLevel()) val toAppend = List(appender -> sbtLogLevel) LogExchange.bindLoggerAppenders(loggerName, toAppend) - new LoggerReporter(config.maximumErrors(), logger, config.positionMapper().toScala) + + val maxErrors = config.maximumErrors() + val posMapper = config.positionMapper().toScala + if (config.fileFilters().isEmpty && config.msgFilters.isEmpty) + new LoggerReporter(maxErrors, logger, posMapper) + else { + implicit def scalaPatterns(patterns: Array[java.util.regex.Pattern]): Array[Regex] = + patterns.map(_.pattern().r) + new FilteredReporter(config.fileFilters, config.msgFilters, maxErrors, logger, posMapper) + } } def getReporter(toOutput: PrintStream, config: ReporterConfig): Reporter = {