Merge pull request #6638 from hmemcpy/buildTarget/cleanCache

bsp: Implementing `buildTarget/cleanCache`
This commit is contained in:
Adrien Piquerez 2021-09-06 12:25:06 +02:00 committed by GitHub
commit d197b5b77b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 233 additions and 2 deletions

View File

@ -402,7 +402,7 @@ object Keys {
val bspTargetIdentifier = settingKey[BuildTargetIdentifier]("Build target identifier of a project and configuration.").withRank(DSetting)
val bspWorkspace = settingKey[Map[BuildTargetIdentifier, Scope]]("Mapping of BSP build targets to sbt scopes").withRank(DSetting)
private[sbt] val bspFullWorkspace = settingKey[BspFullWorkspace]("Mapping of BSP build targets to sbt scopes and meta-targets for the SBT build itself").withRank(DSetting)
val bspInternalDependencyConfigurations = settingKey[Seq[(ProjectRef, Set[ConfigKey])]]("The project configurations that this configuration depends on, possibly transitivly").withRank(DSetting)
val bspInternalDependencyConfigurations = settingKey[Seq[(ProjectRef, Set[ConfigKey])]]("The project configurations that this configuration depends on, possibly transitively").withRank(DSetting)
val bspWorkspaceBuildTargets = taskKey[Seq[BuildTarget]]("List all the BSP build targets").withRank(DTask)
val bspBuildTarget = taskKey[BuildTarget]("Description of the BSP build targets").withRank(DTask)
val bspBuildTargetSources = inputKey[Unit]("").withRank(DTask)
@ -415,6 +415,7 @@ object Keys {
val bspBuildTargetCompileItem = taskKey[Int]("").withRank(DTask)
val bspBuildTargetTest = inputKey[Unit]("Corresponds to buildTarget/test request").withRank(DTask)
val bspBuildTargetRun = inputKey[Unit]("Corresponds to buildTarget/run request").withRank(DTask)
val bspBuildTargetCleanCache = inputKey[Unit]("Corresponds to buildTarget/cleanCache request").withRank(DTask)
val bspBuildTargetScalacOptions = inputKey[Unit]("").withRank(DTask)
val bspBuildTargetScalacOptionsItem = taskKey[ScalacOptionsItem]("").withRank(DTask)
val bspScalaTestClasses = inputKey[Unit]("Corresponds to buildTarget/scalaTestClasses request").withRank(DTask)

View File

@ -186,6 +186,25 @@ object BuildServerProtocol {
bspBuildTargetCompile / aggregate := false,
bspBuildTargetTest := bspTestTask.evaluated,
bspBuildTargetTest / aggregate := false,
bspBuildTargetCleanCache := Def.inputTaskDyn {
val s: State = state.value
val targets = spaceDelimited().parsed.map(uri => BuildTargetIdentifier(URI.create(uri)))
val workspace = bspFullWorkspace.value.filter(targets)
workspace.warnIfBuildsNonEmpty(Method.CleanCache, s.log)
val filter = ScopeFilter.in(workspace.scopes.values.toList)
Def.task {
val results = Keys.clean.result.all(filter).value
val successes = anyOrThrow(results).size
// When asking to Rebuild Project, IntelliJ sends the root build as an additional target, however it is
// not returned as part of the results. In this case, there's 1 build entry in the workspace, and we're
// checking that the executed results plus this entry is equal to the total number of targets.
// When rebuilding a single module, the root build isn't sent, just the requested targets.
val cleaned = successes + workspace.builds.size == targets.size
s.respondEvent(CleanCacheResult(None, cleaned))
}
}.evaluated,
bspBuildTargetCleanCache / aggregate := false,
bspBuildTargetScalacOptions := Def.inputTaskDyn {
val s = state.value
@ -310,6 +329,7 @@ object BuildServerProtocol {
final val Compile = "buildTarget/compile"
final val Test = "buildTarget/test"
final val Run = "buildTarget/run"
final val CleanCache = "buildTarget/cleanCache"
final val ScalacOptions = "buildTarget/scalacOptions"
final val ScalaTestClasses = "buildTarget/scalaTestClasses"
final val ScalaMainClasses = "buildTarget/scalaMainClasses"
@ -397,6 +417,12 @@ object BuildServerProtocol {
Some(r.id)
)
case r if r.method == Method.CleanCache =>
val param = Converter.fromJson[CleanCacheParams](json(r)).get
val targets = param.targets.map(_.uri).mkString(" ")
val command = Keys.bspBuildTargetCleanCache.key
val _ = callback.appendExec(s"$command $targets", Some(r.id))
case r if r.method == Method.ScalacOptions =>
val param = Converter.fromJson[ScalacOptionsParams](json(r)).get
val targets = param.targets.map(_.uri).mkString(" ")

View File

@ -0,0 +1,36 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp
/**
* Clean Cache Request
* @param targets A sequence of build targets to clean
*/
final class CleanCacheParams private (
val targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]) extends Serializable {
override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match {
case x: CleanCacheParams => (this.targets == x.targets)
case _ => false
})
override def hashCode: Int = {
37 * (37 * (17 + "sbt.internal.bsp.CleanCacheParams".##) + targets.##)
}
override def toString: String = {
"CleanCacheParams(" + targets + ")"
}
private[this] def copy(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier] = targets): CleanCacheParams = {
new CleanCacheParams(targets)
}
def withTargets(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]): CleanCacheParams = {
copy(targets = targets)
}
}
object CleanCacheParams {
def apply(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]): CleanCacheParams = new CleanCacheParams(targets)
}

View File

@ -0,0 +1,45 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp
/**
* Clean Cache Response
* @param message Optional message to display to the user
* @param cleaned Indicates whether the clean cache request was performed or not
*/
final class CleanCacheResult private (
val message: Option[String],
val cleaned: Boolean) extends Serializable {
override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match {
case x: CleanCacheResult => (this.message == x.message) && (this.cleaned == x.cleaned)
case _ => false
})
override def hashCode: Int = {
37 * (37 * (37 * (17 + "sbt.internal.bsp.CleanCacheResult".##) + message.##) + cleaned.##)
}
override def toString: String = {
"CleanCacheResult(" + message + ", " + cleaned + ")"
}
private[this] def copy(message: Option[String] = message, cleaned: Boolean = cleaned): CleanCacheResult = {
new CleanCacheResult(message, cleaned)
}
def withMessage(message: Option[String]): CleanCacheResult = {
copy(message = message)
}
def withMessage(message: String): CleanCacheResult = {
copy(message = Option(message))
}
def withCleaned(cleaned: Boolean): CleanCacheResult = {
copy(cleaned = cleaned)
}
}
object CleanCacheResult {
def apply(message: Option[String], cleaned: Boolean): CleanCacheResult = new CleanCacheResult(message, cleaned)
def apply(message: String, cleaned: Boolean): CleanCacheResult = new CleanCacheResult(Option(message), cleaned)
}

View File

@ -0,0 +1,27 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait CleanCacheParamsFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol =>
implicit lazy val CleanCacheParamsFormat: JsonFormat[sbt.internal.bsp.CleanCacheParams] = new JsonFormat[sbt.internal.bsp.CleanCacheParams] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.CleanCacheParams = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val targets = unbuilder.readField[Vector[sbt.internal.bsp.BuildTargetIdentifier]]("targets")
unbuilder.endObject()
sbt.internal.bsp.CleanCacheParams(targets)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.bsp.CleanCacheParams, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("targets", obj.targets)
builder.endObject()
}
}
}

View File

@ -0,0 +1,29 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait CleanCacheResultFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val CleanCacheResultFormat: JsonFormat[sbt.internal.bsp.CleanCacheResult] = new JsonFormat[sbt.internal.bsp.CleanCacheResult] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.CleanCacheResult = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val message = unbuilder.readField[Option[String]]("message")
val cleaned = unbuilder.readField[Boolean]("cleaned")
unbuilder.endObject()
sbt.internal.bsp.CleanCacheResult(message, cleaned)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.bsp.CleanCacheResult, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("message", obj.message)
builder.addField("cleaned", obj.cleaned)
builder.endObject()
}
}
}

View File

@ -36,6 +36,8 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
with sbt.internal.bsp.codec.TaskFinishParamsFormats
with sbt.internal.bsp.codec.CompileParamsFormats
with sbt.internal.bsp.codec.BspCompileResultFormats
with sbt.internal.bsp.codec.CleanCacheParamsFormats
with sbt.internal.bsp.codec.CleanCacheResultFormats
with sbt.internal.bsp.codec.CompileTaskFormats
with sbt.internal.bsp.codec.CompileReportFormats
with sbt.internal.bsp.codec.TestParamsFormats

View File

@ -368,6 +368,21 @@ type BspCompileResult {
# data: any
}
## Clean Cache Request
type CleanCacheParams {
## A sequence of build targets to clean
targets: [sbt.internal.bsp.BuildTargetIdentifier]
}
## Clean Cache Response
type CleanCacheResult {
## Optional message to display to the user
message: String
## Indicates whether the clean cache request was performed or not
cleaned: Boolean!
}
## Compile Notifications
type CompileTask {

View File

@ -7,11 +7,12 @@
package testpkg
import sbt.internal.bsp.{ BspCompileResult, SourcesResult, StatusCode, WorkspaceBuildTargetsResult }
import sbt.internal.bsp._
import sbt.internal.langserver.ErrorCodes
import sbt.IO
import java.io.File
import java.nio.file.Paths
import scala.concurrent.duration._
// starts svr using server-test/buildserver and perform custom server tests
@ -108,6 +109,55 @@ object BuildServerTest extends AbstractServerTest {
})
}
test("buildTarget/cleanCache") { _ =>
def targetDir =
Paths
.get(
svr.baseDirectory.getAbsoluteFile.toString,
"run-and-test/target/scala-2.13/classes/main"
)
.toFile
val buildTarget = buildTargetUri("runAndTest", "Compile")
svr.sendJsonRpc(
s"""{ "jsonrpc": "2.0", "id": "43", "method": "buildTarget/compile", "params": {
| "targets": [{ "uri": "$buildTarget" }]
|} }""".stripMargin
)
svr.waitFor[BspCompileResult](10.seconds)
assert(targetDir.list().contains("Main.class"))
svr.sendJsonRpc(
s"""{ "jsonrpc": "2.0", "id": "44", "method": "buildTarget/cleanCache", "params": {
| "targets": [{ "uri": "$buildTarget" }]
|} }""".stripMargin
)
assert(processing("buildTarget/cleanCache"))
val res = svr.waitFor[CleanCacheResult](10.seconds)
assert(res.cleaned)
assert(targetDir.list().isEmpty)
}
test("buildTarget/cleanCache: rebuild project") { _ =>
svr.sendJsonRpc(
"""{ "jsonrpc": "2.0", "id": "45", "method": "workspace/buildTargets", "params": {} }"""
)
assert(processing("workspace/buildTargets"))
val result = svr.waitFor[WorkspaceBuildTargetsResult](10.seconds)
val allTargets = result.targets.map(_.id.uri)
svr.sendJsonRpc(
s"""{ "jsonrpc": "2.0", "id": "46", "method": "buildTarget/cleanCache", "params": {
| "targets": [
| ${allTargets.map(uri => s"""{ "uri": "$uri" }""").mkString(",\n")}
| ]
|} }""".stripMargin
)
assert(processing("buildTarget/cleanCache"))
val res = svr.waitFor[CleanCacheResult](10.seconds)
assert(res.cleaned)
}
test("workspace/reload") { _ =>
svr.sendJsonRpc(
"""{ "jsonrpc": "2.0", "id": "48", "method": "workspace/reload"}"""