Merge pull request #5948 from adpi2/fix-custom-reporter

Fix #5935: Introduce bspReporter key
This commit is contained in:
eugene yokota 2020-10-11 13:10:34 -04:00 committed by GitHub
commit 916498c0c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 80 additions and 68 deletions

View File

@ -1933,7 +1933,7 @@ object Defaults extends BuildCommon {
val cp = data(dependencyClasspath.value).toList
val label = nameForSrc(configuration.value.name)
val fiOpts = fileInputOptions.value
val reporter = (compilerReporter in compile).value
val reporter = (compile / bspReporter).value
val converter = fileConverter.value
(hasScala, hasJava) match {
case (true, _) =>
@ -2096,7 +2096,8 @@ object Defaults extends BuildCommon {
compileIncrementalTaskImpl(
streams.value,
(compile / compileInputs).value,
earlyOutputPing.value
earlyOutputPing.value,
(compile / bspReporter).value
)
}
}
@ -2106,6 +2107,7 @@ object Defaults extends BuildCommon {
val r = compileScalaBackend.value
val in0 = (compileJava / compileInputs).value
val in = in0.withPreviousResult(PreviousResult.of(r.analysis, r.setup))
val reporter = (compile / bspReporter).value
try {
if (r.hasModified) {
val result0 = incCompiler
@ -2114,15 +2116,14 @@ object Defaults extends BuildCommon {
result0.withHasModified(result0.hasModified || r.hasModified)
} else r
} finally {
in.setup.reporter match {
case r: BuildServerReporter => r.sendFinalReport()
}
reporter.sendFinalReport()
}
}
private[this] def compileIncrementalTaskImpl(
s: TaskStreams,
ci: Inputs,
promise: PromiseWrap[Boolean]
promise: PromiseWrap[Boolean],
reporter: BuildServerReporter
): CompileResult = {
lazy val x = s.text(ExportStream)
def onArgs(cs: Compilers) = {
@ -2143,9 +2144,7 @@ object Defaults extends BuildCommon {
ConcurrentRestrictions.cancelAllSentinels()
throw e
} finally {
i.setup.reporter match {
case r: BuildServerReporter => r.sendFinalReport()
}
reporter.sendFinalReport()
x.close() // workaround for #937
}
}
@ -2173,7 +2172,7 @@ object Defaults extends BuildCommon {
compileAnalysisFile.value.toPath,
compilerCache.value,
incOptions.value,
(compilerReporter in compile).value,
(compile / bspReporter).value,
Some((compile / compileProgress).value).toOptional,
eaOpt.toOptional,
extra.toArray,
@ -2210,12 +2209,10 @@ object Defaults extends BuildCommon {
)
},
compilerReporter := {
new BuildServerReporter(
bspTargetIdentifier.value,
new ManagedLoggedReporter(
maxErrors.value,
streams.value.log,
foldMappers(sourcePositionMappers.value),
sources.value
foldMappers(sourcePositionMappers.value)
)
},
compileInputs := {

View File

@ -26,7 +26,7 @@ import sbt.internal.inc.ScalaInstance
import sbt.internal.io.WatchState
import sbt.internal.librarymanagement.{ CompatibilityWarningOptions, IvySbt }
import sbt.internal.remotecache.RemoteCacheArtifact
import sbt.internal.server.ServerHandler
import sbt.internal.server.{ BuildServerReporter, ServerHandler }
import sbt.internal.util.{ AttributeKey, ProgressState, SourcePosition }
import sbt.io._
import sbt.librarymanagement.Configurations.CompilerPlugin
@ -411,6 +411,7 @@ object Keys {
val bspScalaTestClassesItem = taskKey[ScalaTestClassesItem]("").withRank(DTask)
val bspScalaMainClasses = inputKey[Unit]("Corresponds to buildTarget/scalaMainClasses request").withRank(DTask)
val bspScalaMainClassesItem = taskKey[ScalaMainClassesItem]("").withRank(DTask)
val bspReporter = taskKey[BuildServerReporter]("").withRank(DTask)
val useCoursier = settingKey[Boolean]("Use Coursier for dependency resolution.").withRank(BSetting)
val csrCacheDirectory = settingKey[File]("Coursier cache directory. Uses -Dsbt.coursier.home or Coursier's default.").withRank(CSetting)

View File

@ -15,6 +15,7 @@ import sbt.BasicCommandStrings.Shutdown
import sbt.BuildSyntax._
import sbt.Def._
import sbt.Keys._
import sbt.Project._
import sbt.ScopeFilter.Make._
import sbt.SlashSyntax0._
import sbt.StandardMain.exchange
@ -24,14 +25,13 @@ import sbt.internal.protocol.JsonRpcRequestMessage
import sbt.internal.util.Attributed
import sbt.internal.util.complete.{ Parser, Parsers }
import sbt.librarymanagement.Configuration
import sbt.std.TaskExtra
import sbt.util.Logger
import sjsonnew.shaded.scalajson.ast.unsafe.{ JNull, JValue }
import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter, Parser => JsonParser }
import scala.util.{ Failure, Success, Try }
import scala.util.control.NonFatal
import sbt.Project._
import sbt.std.TaskExtra
import scala.util.{ Failure, Success, Try }
object BuildServerProtocol {
import sbt.internal.bsp.codec.JsonProtocol._
@ -198,7 +198,18 @@ object BuildServerProtocol {
bspBuildTargetScalacOptionsItem := scalacOptionsTask.value,
bspInternalDependencyConfigurations := internalDependencyConfigurationsSetting.value,
bspScalaTestClassesItem := scalaTestClassesTask.value,
bspScalaMainClassesItem := scalaMainClassesTask.value
bspScalaMainClassesItem := scalaMainClassesTask.value,
Keys.compile / bspReporter := {
val targetId = bspTargetIdentifier.value
val underlying = (Keys.compile / compilerReporter).value
val logger = streams.value.log
val srcs = sources.value
if (bspEnabled.value) {
new BuildServerReporterImpl(targetId, logger, underlying, srcs)
} else {
new BuildServerForwarder(logger, underlying)
}
}
)
def handler(

View File

@ -11,37 +11,57 @@ import java.io.File
import sbt.StandardMain
import sbt.internal.bsp._
import sbt.internal.inc.LoggedReporter
import sbt.internal.util.ManagedLogger
import xsbti.{ Problem, Severity, Position => XPosition }
import xsbti.{ Problem, Reporter, Severity, Position => XPosition }
import scala.collection.mutable
/**
* Defines a compiler reporter that uses event logging provided by a `ManagedLogger`.
*
* @param maximumErrors The maximum errors.
* @param logger The event managed logger.
* @param sourcePositionMapper The position mapper.
*/
class BuildServerReporter(
buildTarget: BuildTargetIdentifier,
maximumErrors: Int,
logger: ManagedLogger,
sourcePositionMapper: XPosition => XPosition = identity[XPosition],
sources: Seq[File]
) extends LoggedReporter(maximumErrors, logger, sourcePositionMapper) {
import LoggedReporter.problemFormats._
import LoggedReporter.problemStringFormats._
logger.registerStringCodec[Problem]
sealed trait BuildServerReporter extends Reporter {
private final val sigFilesWritten = "[sig files written]"
protected def logger: ManagedLogger
protected def underlying: Reporter
protected def publishDiagnostic(problem: Problem): Unit
def sendFinalReport(): Unit
override def reset(): Unit = underlying.reset()
override def hasErrors: Boolean = underlying.hasErrors
override def hasWarnings: Boolean = underlying.hasWarnings
override def printSummary(): Unit = underlying.printSummary()
override def problems(): Array[Problem] = underlying.problems()
override def log(problem: Problem): Unit = {
if (problem.message == sigFilesWritten) {
logger.debug(sigFilesWritten)
} else {
publishDiagnostic(problem)
underlying.log(problem)
}
}
override def comment(pos: XPosition, msg: String): Unit = underlying.comment(pos, msg)
}
final class BuildServerReporterImpl(
buildTarget: BuildTargetIdentifier,
protected override val logger: ManagedLogger,
protected override val underlying: Reporter,
sources: Seq[File]
) extends BuildServerReporter {
import sbt.internal.bsp.codec.JsonProtocol._
import sbt.internal.inc.JavaInterfaceUtil._
private lazy val exchange = StandardMain.exchange
private val problemsByFile = mutable.Map[File, Vector[Diagnostic]]()
private[sbt] def sendFinalReport(): Unit = {
override def sendFinalReport(): Unit = {
for (source <- sources) {
val diagnostics = problemsByFile.getOrElse(source, Vector())
val params = PublishDiagnosticsParams(
@ -55,34 +75,7 @@ class BuildServerReporter(
}
}
override def logError(problem: Problem): Unit = {
publishDiagnostic(problem)
// console channel can keep using the xsbi.Problem
logger.errorEvent(problem)
}
override def logWarning(problem: Problem): Unit = {
publishDiagnostic(problem)
// console channel can keep using the xsbi.Problem
logger.warnEvent(problem)
}
override def logInfo(problem: Problem): Unit = {
// demote this message https://github.com/scala/bug/issues/12097
val sigFilesWritten = "[sig files written]"
if (problem.message == sigFilesWritten) {
logger.debug(sigFilesWritten)
} else {
publishDiagnostic(problem)
// console channel can keep using the xsbi.Problem
logger.infoEvent(problem)
}
}
private def publishDiagnostic(problem: Problem): Unit = {
protected override def publishDiagnostic(problem: Problem): Unit = {
for {
source <- problem.position.sourceFile.toOption
diagnostic <- toDiagnostic(problem)
@ -127,9 +120,19 @@ class BuildServerReporter(
}
}
private[sbt] def toDiagnosticSeverity(severity: Severity): Long = severity match {
private def toDiagnosticSeverity(severity: Severity): Long = severity match {
case Severity.Info => DiagnosticSeverity.Information
case Severity.Warn => DiagnosticSeverity.Warning
case Severity.Error => DiagnosticSeverity.Error
}
}
final class BuildServerForwarder(
protected override val logger: ManagedLogger,
protected override val underlying: Reporter
) extends BuildServerReporter {
override def sendFinalReport(): Unit = ()
protected override def publishDiagnostic(problem: Problem): Unit = ()
}