add noOp field in BSP compile report

This commit is contained in:
Adrien Piquerez 2024-02-15 15:36:05 +01:00
parent f08032af26
commit 02df82840a
6 changed files with 55 additions and 31 deletions

View File

@ -2316,11 +2316,11 @@ object Defaults extends BuildCommon {
val ci = (compile / compileInputs).value
val ping = earlyOutputPing.value
val reporter = (compile / bspReporter).value
BspCompileTask.compute(bspTargetIdentifier.value, thisProjectRef.value, configuration.value) {
task =>
BspCompileTask
.compute(bspTargetIdentifier.value, thisProjectRef.value, configuration.value, ci) { task =>
// TODO - Should readAnalysis + saveAnalysis be scoped by the compile task too?
compileIncrementalTaskImpl(task, s, ci, ping, reporter)
}
}
}
private val incCompiler = ZincUtil.defaultIncrementalCompiler
private[sbt] def compileJavaTask: Initialize[Task[CompileResult]] = Def.task {

View File

@ -11,21 +11,31 @@ package sbt.internal.server
import sbt._
import sbt.internal.bsp._
import sbt.internal.io.Retry
import sbt.internal.server.BspCompileTask.{ compileReport, exchange }
import sbt.internal.server.BspCompileTask.compileReport
import sbt.internal.server.BspCompileTask.exchange
import sbt.librarymanagement.Configuration
import sbt.util.InterfaceUtil
import sjsonnew.support.scalajson.unsafe.Converter
import xsbti.CompileFailed
import xsbti.Problem
import xsbti.Severity
import xsbti.compile.CompileResult
import xsbti.{ CompileFailed, Problem, Severity }
import xsbti.compile.Inputs
import scala.util.control.NonFatal
object BspCompileTask {
private lazy val exchange = StandardMain.exchange
def compute(targetId: BuildTargetIdentifier, project: ProjectRef, config: Configuration)(
def compute(
targetId: BuildTargetIdentifier,
project: ProjectRef,
config: Configuration,
ci: Inputs
)(
compile: BspCompileTask => CompileResult
): CompileResult = {
val task = BspCompileTask(targetId, project, config)
val task = BspCompileTask(targetId, project, config, ci)
try {
task.notifyStart()
val result = Retry(compile(task))
@ -45,22 +55,24 @@ object BspCompileTask {
private def apply(
targetId: BuildTargetIdentifier,
project: ProjectRef,
config: Configuration
config: Configuration,
inputs: Inputs
): BspCompileTask = {
val taskId = TaskId(BuildServerTasks.uniqueId, Vector())
val targetName = BuildTargetName.fromScope(project.project, config.name)
BspCompileTask(targetId, targetName, taskId, System.currentTimeMillis())
new BspCompileTask(targetId, targetName, taskId, inputs, System.currentTimeMillis())
}
private def compileReport(
problems: Seq[Problem],
targetId: BuildTargetIdentifier,
elapsedTimeMillis: Long
elapsedTimeMillis: Long,
isNoOp: Option[Boolean]
): CompileReport = {
val countBySeverity = problems.groupBy(_.severity()).mapValues(_.size)
val warnings = countBySeverity.getOrElse(Severity.Warn, 0)
val errors = countBySeverity.getOrElse(Severity.Error, 0)
CompileReport(targetId, None, errors, warnings, Some(elapsedTimeMillis.toInt))
CompileReport(targetId, None, errors, warnings, Some(elapsedTimeMillis.toInt), isNoOp)
}
}
@ -68,6 +80,7 @@ case class BspCompileTask private (
targetId: BuildTargetIdentifier,
targetName: String,
id: TaskId,
inputs: Inputs,
startTimeMillis: Long
) {
import sbt.internal.bsp.codec.JsonProtocol._
@ -89,7 +102,8 @@ case class BspCompileTask private (
sourceInfos.values.flatMap(_.getReportedProblems).toSeq
case _ => Seq()
}
val report = compileReport(problems, targetId, elapsedTimeMillis)
val isNoOp = InterfaceUtil.toOption(inputs.previousResult.analysis).map(_ == result.analysis)
val report = compileReport(problems, targetId, elapsedTimeMillis, isNoOp)
val params = TaskFinishParams(
id,
endTimeMillis,
@ -122,7 +136,7 @@ case class BspCompileTask private (
val endTimeMillis = System.currentTimeMillis()
val elapsedTimeMillis = endTimeMillis - startTimeMillis
val problems = cause.map(_.problems().toSeq).getOrElse(Seq.empty[Problem])
val report = compileReport(problems, targetId, elapsedTimeMillis)
val report = compileReport(problems, targetId, elapsedTimeMillis, None)
val params = TaskFinishParams(
id,
endTimeMillis,

View File

@ -10,28 +10,30 @@ package sbt.internal.bsp
* @param errors The total number of reported errors compiling this target.
* @param warnings The total number of reported warnings compiling the target.
* @param time The total number of milliseconds it took to compile the target.
* @param noOp The compilation was a noOp compilation.
*/
final class CompileReport private (
val target: sbt.internal.bsp.BuildTargetIdentifier,
val originId: Option[String],
val errors: Int,
val warnings: Int,
val time: Option[Int]) extends Serializable {
val time: Option[Int],
val noOp: Option[Boolean]) extends Serializable {
override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match {
case x: CompileReport => (this.target == x.target) && (this.originId == x.originId) && (this.errors == x.errors) && (this.warnings == x.warnings) && (this.time == x.time)
case x: CompileReport => (this.target == x.target) && (this.originId == x.originId) && (this.errors == x.errors) && (this.warnings == x.warnings) && (this.time == x.time) && (this.noOp == x.noOp)
case _ => false
})
override def hashCode: Int = {
37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.CompileReport".##) + target.##) + originId.##) + errors.##) + warnings.##) + time.##)
37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.CompileReport".##) + target.##) + originId.##) + errors.##) + warnings.##) + time.##) + noOp.##)
}
override def toString: String = {
"CompileReport(" + target + ", " + originId + ", " + errors + ", " + warnings + ", " + time + ")"
"CompileReport(" + target + ", " + originId + ", " + errors + ", " + warnings + ", " + time + ", " + noOp + ")"
}
private[this] def copy(target: sbt.internal.bsp.BuildTargetIdentifier = target, originId: Option[String] = originId, errors: Int = errors, warnings: Int = warnings, time: Option[Int] = time): CompileReport = {
new CompileReport(target, originId, errors, warnings, time)
private[this] def copy(target: sbt.internal.bsp.BuildTargetIdentifier = target, originId: Option[String] = originId, errors: Int = errors, warnings: Int = warnings, time: Option[Int] = time, noOp: Option[Boolean] = noOp): CompileReport = {
new CompileReport(target, originId, errors, warnings, time, noOp)
}
def withTarget(target: sbt.internal.bsp.BuildTargetIdentifier): CompileReport = {
copy(target = target)
@ -54,9 +56,15 @@ final class CompileReport private (
def withTime(time: Int): CompileReport = {
copy(time = Option(time))
}
def withNoOp(noOp: Option[Boolean]): CompileReport = {
copy(noOp = noOp)
}
def withNoOp(noOp: Boolean): CompileReport = {
copy(noOp = Option(noOp))
}
}
object CompileReport {
def apply(target: sbt.internal.bsp.BuildTargetIdentifier, originId: Option[String], errors: Int, warnings: Int, time: Option[Int]): CompileReport = new CompileReport(target, originId, errors, warnings, time)
def apply(target: sbt.internal.bsp.BuildTargetIdentifier, originId: String, errors: Int, warnings: Int, time: Int): CompileReport = new CompileReport(target, Option(originId), errors, warnings, Option(time))
def apply(target: sbt.internal.bsp.BuildTargetIdentifier, originId: Option[String], errors: Int, warnings: Int, time: Option[Int], noOp: Option[Boolean]): CompileReport = new CompileReport(target, originId, errors, warnings, time, noOp)
def apply(target: sbt.internal.bsp.BuildTargetIdentifier, originId: String, errors: Int, warnings: Int, time: Int, noOp: Boolean): CompileReport = new CompileReport(target, Option(originId), errors, warnings, Option(time), Option(noOp))
}

View File

@ -16,8 +16,9 @@ implicit lazy val CompileReportFormat: JsonFormat[sbt.internal.bsp.CompileReport
val errors = unbuilder.readField[Int]("errors")
val warnings = unbuilder.readField[Int]("warnings")
val time = unbuilder.readField[Option[Int]]("time")
val noOp = unbuilder.readField[Option[Boolean]]("noOp")
unbuilder.endObject()
sbt.internal.bsp.CompileReport(target, originId, errors, warnings, time)
sbt.internal.bsp.CompileReport(target, originId, errors, warnings, time, noOp)
case None =>
deserializationError("Expected JsObject but found None")
}
@ -29,6 +30,7 @@ implicit lazy val CompileReportFormat: JsonFormat[sbt.internal.bsp.CompileReport
builder.addField("errors", obj.errors)
builder.addField("warnings", obj.warnings)
builder.addField("time", obj.time)
builder.addField("noOp", obj.noOp)
builder.endObject()
}
}

View File

@ -530,6 +530,9 @@ type CompileReport {
## The total number of milliseconds it took to compile the target.
time: Int
## The compilation was a noOp compilation.
noOp: Boolean
}
## Test Request

View File

@ -133,7 +133,6 @@ object BuildServerTest extends AbstractServerTest {
|}""".stripMargin
)
reloadWorkspace()
compile(buildTarget)
assertMessage(
@ -156,22 +155,20 @@ object BuildServerTest extends AbstractServerTest {
reloadWorkspace()
compile(buildTarget)
assertMessage(
"build/publishDiagnostics",
"Diagnostics.scala",
"\"diagnostics\":[]"
)(
assertMessage("build/publishDiagnostics", "Diagnostics.scala", "\"diagnostics\":[]")(
duration = 30.seconds,
message = "should send publishDiagnostics with empty diagnostics"
)
assertMessage("build/taskFinish", "\"noOp\":true")(debug = true)
// trigger no-op compilation
compile(buildTarget)
assert(
!svr.waitForString(20.seconds) { s =>
s.contains("build/publishDiagnostics") &&
s.contains("Diagnostics.scala")
svr.waitForString(20.seconds) { s =>
if (s.contains("build/publishDiagnostics") && s.contains("Diagnostics.scala"))
throw new Exception("shouldn't send publishDiagnostics if noOp compilation")
else s.contains("build/taskFinish") && s.contains("\"noOp\":true")
},
"shouldn't send publishDiagnostics if there's no change in diagnostics (were empty, are empty)"
)