diff --git a/build.sbt b/build.sbt index 1199e135b..bba828f2a 100644 --- a/build.sbt +++ b/build.sbt @@ -176,6 +176,7 @@ def mimaSettingsSince(versions: Seq[String]): Seq[Def.Setting[_]] = Def settings exclude[DirectMissingMethodProblem]("sbt.PluginData.apply"), exclude[DirectMissingMethodProblem]("sbt.PluginData.copy"), exclude[DirectMissingMethodProblem]("sbt.PluginData.this"), + exclude[IncompatibleResultTypeProblem]("sbt.PluginData.copy$default$10") ), ) diff --git a/main/src/main/scala/sbt/EvaluateTask.scala b/main/src/main/scala/sbt/EvaluateTask.scala index 29749cfde..eea3b357a 100644 --- a/main/src/main/scala/sbt/EvaluateTask.scala +++ b/main/src/main/scala/sbt/EvaluateTask.scala @@ -148,18 +148,20 @@ final case class PluginData( resolvers: Option[Vector[Resolver]], report: Option[UpdateReport], scalacOptions: Seq[String], + javacOptions: Seq[String], unmanagedSourceDirectories: Seq[File], unmanagedSources: Seq[File], managedSourceDirectories: Seq[File], managedSources: Seq[File], + classDirectory: Option[File], buildTarget: Option[BuildTargetIdentifier] ) { val classpath: Seq[Attributed[File]] = definitionClasspath ++ dependencyClasspath } object PluginData { - private[sbt] def apply(dependencyClasspath: Def.Classpath): PluginData = - PluginData(dependencyClasspath, Nil, None, None, Nil, Nil, Nil, Nil, Nil, None) + private[sbt] def apply(depClasspath: Def.Classpath): PluginData = + PluginData(depClasspath, Nil, None, None, Nil, Nil, Nil, Nil, Nil, Nil, None, None) } object EvaluateTask { diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 0fb3c3bdc..3720a3693 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -419,19 +419,21 @@ object Keys { val bspBuildTargetOutputPathsItem = taskKey[OutputPathsItem]("").withRank(DTask) val bspBuildTargetCompile = inputKey[Unit]("").withRank(DTask) 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 bspBuildTargetTest = inputKey[Unit]("Implementation of buildTarget/test").withRank(DTask) + val bspBuildTargetRun = inputKey[Unit]("Implementation of buildTarget/run").withRank(DTask) + val bspBuildTargetCleanCache = inputKey[Unit]("Implementation of buildTarget/cleanCache").withRank(DTask) val bspBuildTargetScalacOptions = inputKey[Unit]("").withRank(DTask) val bspBuildTargetScalacOptionsItem = taskKey[ScalacOptionsItem]("").withRank(DTask) + val bspBuildTargetJavacOptions = inputKey[Unit]("Implementation of buildTarget/javacOptions").withRank(DTask) + val bspBuildTargetJavacOptionsItem = taskKey[JavacOptionsItem]("Item of buildTarget/javacOptions").withRank(DTask) - val bspBuildTargetJVMRunEnvironment = inputKey[Unit]("Corresponds to the buildTarget/jvmRunEnvironment request").withRank(DTask) - val bspBuildTargetJVMTestEnvironment = inputKey[Unit]("Corresponds to the buildTarget/jvmTestEnvironment request").withRank(DTask) + val bspBuildTargetJVMRunEnvironment = inputKey[Unit]("Implementation of buildTarget/jvmRunEnvironment").withRank(DTask) + val bspBuildTargetJVMTestEnvironment = inputKey[Unit]("Implementation of buildTarget/jvmTestEnvironment").withRank(DTask) val bspBuildTargetJvmEnvironmentItem = taskKey[JvmEnvironmentItem]("Computes JVM environment item").withRank(DTask) - val bspScalaTestClasses = inputKey[Unit]("Corresponds to buildTarget/scalaTestClasses request").withRank(DTask) + val bspScalaTestClasses = inputKey[Unit]("Implementation of buildTarget/scalaTestClasses").withRank(DTask) val bspScalaTestClassesItem = taskKey[Seq[ScalaTestClassesItem]]("").withRank(DTask) - val bspScalaMainClasses = inputKey[Unit]("Corresponds to buildTarget/scalaMainClasses request").withRank(DTask) + val bspScalaMainClasses = inputKey[Unit]("Implementation of buildTarget/scalaMainClasses").withRank(DTask) val bspScalaMainClassesItem = taskKey[ScalaMainClassesItem]("").withRank(DTask) val bspReporter = taskKey[BuildServerReporter]("").withRank(DTask) diff --git a/main/src/main/scala/sbt/internal/Load.scala b/main/src/main/scala/sbt/internal/Load.scala index 3a3da3810..b00e0a911 100755 --- a/main/src/main/scala/sbt/internal/Load.scala +++ b/main/src/main/scala/sbt/internal/Load.scala @@ -1176,21 +1176,25 @@ private[sbt] object Load { val prod = (Configurations.Runtime / exportedProducts).value val cp = (Configurations.Runtime / fullClasspath).value val opts = (Configurations.Compile / scalacOptions).value + val javaOpts = (Configurations.Compile / javacOptions).value val unmanagedSrcDirs = (Configurations.Compile / unmanagedSourceDirectories).value val unmanagedSrcs = (Configurations.Compile / unmanagedSources).value val managedSrcDirs = (Configurations.Compile / managedSourceDirectories).value val managedSrcs = (Configurations.Compile / managedSources).value val buildTarget = (Configurations.Compile / bspTargetIdentifier).value + val clsDir = (Configurations.Compile / classDirectory).value PluginData( removeEntries(cp, prod), prod, Some(fullResolvers.value.toVector), Some(update.value), opts, + javaOpts, unmanagedSrcDirs, unmanagedSrcs, managedSrcDirs, managedSrcs, + Some(clsDir), Some(buildTarget) ) }, @@ -1244,11 +1248,7 @@ private[sbt] object Load { } def noPlugins(dir: File, config: LoadBuildConfiguration): LoadedPlugins = - loadPluginDefinition( - dir, - config, - PluginData(config.globalPluginClasspath, Nil, None, None, Nil, Nil, Nil, Nil, Nil, None) - ) + loadPluginDefinition(dir, config, PluginData(config.globalPluginClasspath)) def buildPlugins(dir: File, s: State, config: LoadBuildConfiguration): LoadedPlugins = loadPluginDefinition(dir, config, buildPluginDefinition(dir, s, config)) @@ -1444,6 +1444,8 @@ final case class LoadBuildConfiguration( Nil, Nil, Nil, + Nil, + None, None ) case None => PluginData(globalPluginClasspath) diff --git a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala index e54a0139b..2d89185d5 100644 --- a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala @@ -201,24 +201,12 @@ object BuildServerProtocol { }.evaluated, bspBuildTargetCleanCache / aggregate := false, bspBuildTargetScalacOptions := bspInputTask { (state, _, workspace, filter) => - val builds = workspace.builds Def.task { val items = bspBuildTargetScalacOptionsItem.result.all(filter).value val appProvider = appConfiguration.value.provider() val sbtJars = appProvider.mainClasspath() - val buildItems = builds.map { - build => - val plugins: LoadedPlugins = build._2.unit.plugins - val scalacOptions = plugins.pluginData.scalacOptions - val pluginClassPath = plugins.classpath - val classpath = (pluginClassPath ++ sbtJars).map(_.toURI).toVector - val item = ScalacOptionsItem( - build._1, - scalacOptions.toVector, - classpath, - new File(build._2.localBase, "project/target").toURI - ) - Value(item) + val buildItems = workspace.builds.map { + case (targetId, build) => Value(scalacOptionsBuildItem(sbtJars, targetId, build)) } val successfulItems = anyOrThrow(items ++ buildItems) val result = ScalacOptionsResult(successfulItems.toVector) @@ -226,6 +214,20 @@ object BuildServerProtocol { } }.evaluated, bspBuildTargetScalacOptions / aggregate := false, + bspBuildTargetJavacOptions := bspInputTask { (state, _, workspace, filter) => + Def.task { + val items = bspBuildTargetJavacOptionsItem.result.all(filter).value + val appProvider = appConfiguration.value.provider() + val sbtJars = appProvider.mainClasspath() + val buildItems = workspace.builds.map { + case (targetId, build) => Value(javacOptionsBuildItem(sbtJars, targetId, build)) + } + val successfulItems = anyOrThrow(items ++ buildItems) + val result = JavacOptionsResult(successfulItems.toVector) + state.respondEvent(result) + } + }.evaluated, + bspBuildTargetJavacOptions / aggregate := false, bspScalaTestClasses := bspInputTask { (state, _, workspace, filter) => workspace.warnIfBuildsNonEmpty(Method.ScalaTestClasses, state.log) Def.task { @@ -286,7 +288,20 @@ object BuildServerProtocol { }, bspBuildTargetCompileItem := bspCompileTask.value, bspBuildTargetRun := bspRunTask.evaluated, - bspBuildTargetScalacOptionsItem := scalacOptionsTask.value, + bspBuildTargetScalacOptionsItem := { + val target = Keys.bspTargetIdentifier.value + val scalacOptions = Keys.scalacOptions.value.toVector + val classDirectory = Keys.classDirectory.value + val classpath = classpathTask.value + ScalacOptionsItem(target, scalacOptions, classpath, classDirectory.toURI) + }, + bspBuildTargetJavacOptionsItem := { + val target = Keys.bspTargetIdentifier.value + val javacOptions = Keys.javacOptions.value.toVector + val classDirectory = Keys.classDirectory.value + val classpath = classpathTask.value + JavacOptionsItem(target, javacOptions, classpath, classDirectory.toURI) + }, bspBuildTargetJVMRunEnvironment := bspInputTask { (state, _, _, filter) => Def.task { val items = bspBuildTargetJvmEnvironmentItem.result.all(filter).value @@ -328,7 +343,7 @@ object BuildServerProtocol { } } ) - private object Method { + private[sbt] object Method { final val Initialize = "build/initialize" final val BuildTargets = "workspace/buildTargets" final val Reload = "workspace/reload" @@ -665,6 +680,34 @@ object BuildServerProtocol { ) } + private def scalacOptionsBuildItem( + sbtJars: Seq[File], + targetId: BuildTargetIdentifier, + build: LoadedBuildUnit + ): ScalacOptionsItem = { + val plugins: LoadedPlugins = build.unit.plugins + val scalacOptions = plugins.pluginData.scalacOptions.toVector + val pluginClassPath = plugins.classpath + val classpath = (pluginClassPath ++ sbtJars).map(_.toURI).toVector + val classDirectory = plugins.pluginData.classDirectory.map(_.toURI) + val item = ScalacOptionsItem(targetId, scalacOptions, classpath, classDirectory) + item + } + + private def javacOptionsBuildItem( + sbtJars: Seq[File], + targetId: BuildTargetIdentifier, + build: LoadedBuildUnit + ): JavacOptionsItem = { + val plugins: LoadedPlugins = build.unit.plugins + val javacOptions = plugins.pluginData.javacOptions.toVector + val pluginClassPath = plugins.classpath + val classpath = (pluginClassPath ++ sbtJars).map(_.toURI).toVector + val classDirectory = plugins.pluginData.classDirectory.map(_.toURI) + val item = JavacOptionsItem(targetId, javacOptions, classpath, classDirectory) + item + } + private def bspInputTask[T]( taskImpl: ( State, @@ -697,26 +740,17 @@ object BuildServerProtocol { ) } - private def scalacOptionsTask: Def.Initialize[Task[ScalacOptionsItem]] = Def.taskDyn { - val target = Keys.bspTargetIdentifier.value - val scalacOptions = Keys.scalacOptions.value - val classDirectory = Keys.classDirectory.value + private lazy val classpathTask: Def.Initialize[Task[Vector[URI]]] = Def.taskDyn { val externalDependencyClasspath = Keys.externalDependencyClasspath.value - val internalDependencyClasspath = for { (ref, configs) <- bspInternalDependencyConfigurations.value config <- configs } yield ref / config / Keys.classDirectory - Def.task { val classpath = internalDependencyClasspath.join.value.distinct ++ externalDependencyClasspath.map(_.data) - ScalacOptionsItem( - target, - scalacOptions.toVector, - classpath.map(_.toURI).toVector, - classDirectory.toURI - ) + + classpath.map(_.toURI).toVector } } diff --git a/main/src/test/scala/PluginCommandTest.scala b/main/src/test/scala/PluginCommandTest.scala index b0fa5662b..d49950a6b 100644 --- a/main/src/test/scala/PluginCommandTest.scala +++ b/main/src/test/scala/PluginCommandTest.scala @@ -115,7 +115,7 @@ object FakeState { Nil ) - val pluginData = PluginData(Nil, Nil, None, None, Nil, Nil, Nil, Nil, Nil, None) + val pluginData = PluginData(Nil, Nil, None, None, Nil, Nil, Nil, Nil, Nil, Nil, None, None) val builds: DetectedModules[BuildDef] = new DetectedModules[BuildDef](Nil) val detectedAutoPlugins: Seq[DetectedAutoPlugin] = diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/JavacOptionsItem.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/JavacOptionsItem.scala new file mode 100644 index 000000000..30157dfb5 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/JavacOptionsItem.scala @@ -0,0 +1,57 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp +/** + * @param options Additional arguments to the compiler. + For example, -deprecation. + * @param classpath The dependency classpath for this target, must be + identical to what is passed as arguments to + the -classpath flag in the command line interface + of scalac. + * @param classDirectory The output directory for classfiles produced by this target + */ +final class JavacOptionsItem private ( + val target: sbt.internal.bsp.BuildTargetIdentifier, + val options: Vector[String], + val classpath: Vector[java.net.URI], + val classDirectory: Option[java.net.URI]) extends Serializable { + + + + override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { + case x: JavacOptionsItem => (this.target == x.target) && (this.options == x.options) && (this.classpath == x.classpath) && (this.classDirectory == x.classDirectory) + case _ => false + }) + override def hashCode: Int = { + 37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.JavacOptionsItem".##) + target.##) + options.##) + classpath.##) + classDirectory.##) + } + override def toString: String = { + "JavacOptionsItem(" + target + ", " + options + ", " + classpath + ", " + classDirectory + ")" + } + private[this] def copy(target: sbt.internal.bsp.BuildTargetIdentifier = target, options: Vector[String] = options, classpath: Vector[java.net.URI] = classpath, classDirectory: Option[java.net.URI] = classDirectory): JavacOptionsItem = { + new JavacOptionsItem(target, options, classpath, classDirectory) + } + def withTarget(target: sbt.internal.bsp.BuildTargetIdentifier): JavacOptionsItem = { + copy(target = target) + } + def withOptions(options: Vector[String]): JavacOptionsItem = { + copy(options = options) + } + def withClasspath(classpath: Vector[java.net.URI]): JavacOptionsItem = { + copy(classpath = classpath) + } + def withClassDirectory(classDirectory: Option[java.net.URI]): JavacOptionsItem = { + copy(classDirectory = classDirectory) + } + def withClassDirectory(classDirectory: java.net.URI): JavacOptionsItem = { + copy(classDirectory = Option(classDirectory)) + } +} +object JavacOptionsItem { + + def apply(target: sbt.internal.bsp.BuildTargetIdentifier, options: Vector[String], classpath: Vector[java.net.URI], classDirectory: Option[java.net.URI]): JavacOptionsItem = new JavacOptionsItem(target, options, classpath, classDirectory) + def apply(target: sbt.internal.bsp.BuildTargetIdentifier, options: Vector[String], classpath: Vector[java.net.URI], classDirectory: java.net.URI): JavacOptionsItem = new JavacOptionsItem(target, options, classpath, Option(classDirectory)) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/JavacOptionsParams.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/JavacOptionsParams.scala new file mode 100644 index 000000000..4cc1ebbe2 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/JavacOptionsParams.scala @@ -0,0 +1,37 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp +/** + * Javac options + * The build target javac options request is sent from the client to the server + * to query for the list of compiler options necessary to compile in a given list of targets. + */ +final class JavacOptionsParams 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: JavacOptionsParams => (this.targets == x.targets) + case _ => false + }) + override def hashCode: Int = { + 37 * (37 * (17 + "sbt.internal.bsp.JavacOptionsParams".##) + targets.##) + } + override def toString: String = { + "JavacOptionsParams(" + targets + ")" + } + private[this] def copy(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier] = targets): JavacOptionsParams = { + new JavacOptionsParams(targets) + } + def withTargets(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]): JavacOptionsParams = { + copy(targets = targets) + } +} +object JavacOptionsParams { + + def apply(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]): JavacOptionsParams = new JavacOptionsParams(targets) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/JavacOptionsResult.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/JavacOptionsResult.scala new file mode 100644 index 000000000..fb89063b4 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/JavacOptionsResult.scala @@ -0,0 +1,32 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp +final class JavacOptionsResult private ( + val items: Vector[sbt.internal.bsp.JavacOptionsItem]) extends Serializable { + + + + override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { + case x: JavacOptionsResult => (this.items == x.items) + case _ => false + }) + override def hashCode: Int = { + 37 * (37 * (17 + "sbt.internal.bsp.JavacOptionsResult".##) + items.##) + } + override def toString: String = { + "JavacOptionsResult(" + items + ")" + } + private[this] def copy(items: Vector[sbt.internal.bsp.JavacOptionsItem] = items): JavacOptionsResult = { + new JavacOptionsResult(items) + } + def withItems(items: Vector[sbt.internal.bsp.JavacOptionsItem]): JavacOptionsResult = { + copy(items = items) + } +} +object JavacOptionsResult { + + def apply(items: Vector[sbt.internal.bsp.JavacOptionsItem]): JavacOptionsResult = new JavacOptionsResult(items) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JavacOptionsItemFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JavacOptionsItemFormats.scala new file mode 100644 index 000000000..2605a99dc --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JavacOptionsItemFormats.scala @@ -0,0 +1,33 @@ +/** + * 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 JavacOptionsItemFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val JavacOptionsItemFormat: JsonFormat[sbt.internal.bsp.JavacOptionsItem] = new JsonFormat[sbt.internal.bsp.JavacOptionsItem] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.JavacOptionsItem = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val target = unbuilder.readField[sbt.internal.bsp.BuildTargetIdentifier]("target") + val options = unbuilder.readField[Vector[String]]("options") + val classpath = unbuilder.readField[Vector[java.net.URI]]("classpath") + val classDirectory = unbuilder.readField[Option[java.net.URI]]("classDirectory") + unbuilder.endObject() + sbt.internal.bsp.JavacOptionsItem(target, options, classpath, classDirectory) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.JavacOptionsItem, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("target", obj.target) + builder.addField("options", obj.options) + builder.addField("classpath", obj.classpath) + builder.addField("classDirectory", obj.classDirectory) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JavacOptionsParamsFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JavacOptionsParamsFormats.scala new file mode 100644 index 000000000..91bd41177 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JavacOptionsParamsFormats.scala @@ -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 JavacOptionsParamsFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val JavacOptionsParamsFormat: JsonFormat[sbt.internal.bsp.JavacOptionsParams] = new JsonFormat[sbt.internal.bsp.JavacOptionsParams] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.JavacOptionsParams = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val targets = unbuilder.readField[Vector[sbt.internal.bsp.BuildTargetIdentifier]]("targets") + unbuilder.endObject() + sbt.internal.bsp.JavacOptionsParams(targets) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.JavacOptionsParams, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("targets", obj.targets) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JavacOptionsResultFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JavacOptionsResultFormats.scala new file mode 100644 index 000000000..ee381306a --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JavacOptionsResultFormats.scala @@ -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 JavacOptionsResultFormats { self: sbt.internal.bsp.codec.JavacOptionsItemFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val JavacOptionsResultFormat: JsonFormat[sbt.internal.bsp.JavacOptionsResult] = new JsonFormat[sbt.internal.bsp.JavacOptionsResult] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.JavacOptionsResult = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val items = unbuilder.readField[Vector[sbt.internal.bsp.JavacOptionsItem]]("items") + unbuilder.endObject() + sbt.internal.bsp.JavacOptionsResult(items) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.JavacOptionsResult, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("items", obj.items) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JsonProtocol.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JsonProtocol.scala index 3a3ff0906..f6cae48d8 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JsonProtocol.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JsonProtocol.scala @@ -56,10 +56,13 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol with sbt.internal.bsp.codec.RunParamsFormats with sbt.internal.bsp.codec.RunResultFormats with sbt.internal.bsp.codec.ScalaBuildTargetFormats - with sbt.internal.bsp.codec.SbtBuildTargetFormats with sbt.internal.bsp.codec.ScalacOptionsParamsFormats with sbt.internal.bsp.codec.ScalacOptionsItemFormats with sbt.internal.bsp.codec.ScalacOptionsResultFormats + with sbt.internal.bsp.codec.JavacOptionsParamsFormats + with sbt.internal.bsp.codec.JavacOptionsItemFormats + with sbt.internal.bsp.codec.JavacOptionsResultFormats + with sbt.internal.bsp.codec.SbtBuildTargetFormats with sbt.internal.bsp.codec.BspConnectionDetailsFormats with sbt.internal.bsp.codec.MetalsMetadataFormats with sbt.internal.bsp.codec.ScalaTestClassesItemFormats diff --git a/protocol/src/main/contraband/bsp.contra b/protocol/src/main/contraband/bsp.contra index 033143005..dd621786a 100644 --- a/protocol/src/main/contraband/bsp.contra +++ b/protocol/src/main/contraband/bsp.contra @@ -625,31 +625,6 @@ type ScalaBuildTarget { jars: [String]! } -# sbt Extension - -## Contains sbt-specific metadata for providing editor support for sbt build files. -## This metadata is embedded in the data: Option[Json] field of the BuildTarget definition -## when the dataKind field contains "sbt". -type SbtBuildTarget { - ## The sbt version. Useful to support version-dependent syntax. - sbtVersion: String! - - ## A sequence of Scala imports that are automatically imported in the sbt build files. - autoImports: [String]! - - ## The Scala build target describing the scala - ## version and scala jars used by this sbt version. - scalaBuildTarget: sbt.internal.bsp.ScalaBuildTarget! - - ## An optional parent if the target has an sbt meta project. - parent: sbt.internal.bsp.BuildTargetIdentifier - - ## The inverse of parent, list of targets that have this build target - ## defined as their parent. It can contain normal project targets or - ## sbt build targets if this target represents an sbt meta-meta build. - children: [sbt.internal.bsp.BuildTargetIdentifier]! -} - ## Scalac options ## The build target scalac options request is sent from the client to the server ## to query for the list of compiler options necessary to compile in a given list of targets. @@ -678,6 +653,61 @@ type ScalacOptionsItem { classDirectory: java.net.URI } +# Java Extension + +## Javac options +## The build target javac options request is sent from the client to the server +## to query for the list of compiler options necessary to compile in a given list of targets. +type JavacOptionsParams { + targets: [sbt.internal.bsp.BuildTargetIdentifier] +} + +type JavacOptionsResult { + items: [sbt.internal.bsp.JavacOptionsItem] +} + +type JavacOptionsItem { + target: sbt.internal.bsp.BuildTargetIdentifier! + + ## Additional arguments to the compiler. + ## For example, -deprecation. + options: [String] + + ## The dependency classpath for this target, must be + ## identical to what is passed as arguments to + ## the -classpath flag in the command line interface + ## of scalac. + classpath: [java.net.URI] + + ## The output directory for classfiles produced by this target + classDirectory: java.net.URI +} + +# sbt Extension + +## Contains sbt-specific metadata for providing editor support for sbt build files. +## This metadata is embedded in the data: Option[Json] field of the BuildTarget definition +## when the dataKind field contains "sbt". +type SbtBuildTarget { + ## The sbt version. Useful to support version-dependent syntax. + sbtVersion: String! + + ## A sequence of Scala imports that are automatically imported in the sbt build files. + autoImports: [String]! + + ## The Scala build target describing the scala + ## version and scala jars used by this sbt version. + scalaBuildTarget: sbt.internal.bsp.ScalaBuildTarget! + + ## An optional parent if the target has an sbt meta project. + parent: sbt.internal.bsp.BuildTargetIdentifier + + ## The inverse of parent, list of targets that have this build target + ## defined as their parent. It can contain normal project targets or + ## sbt build targets if this target represents an sbt meta-meta build. + children: [sbt.internal.bsp.BuildTargetIdentifier]! +} + ## https://build-server-protocol.github.io/docs/server-discovery.html type BspConnectionDetails { ## The name of the build tool diff --git a/server-test/src/test/scala/testpkg/BuildServerTest.scala b/server-test/src/test/scala/testpkg/BuildServerTest.scala index c694f1334..dc1486786 100644 --- a/server-test/src/test/scala/testpkg/BuildServerTest.scala +++ b/server-test/src/test/scala/testpkg/BuildServerTest.scala @@ -32,8 +32,7 @@ object BuildServerTest extends AbstractServerTest { private def nextId(): Int = idGen.getAndIncrement() test("build/initialize") { _ => - val id = nextId() - initializeRequest(id) + val id = initializeRequest() assert(svr.waitForString(10.seconds) { s => (s contains s""""id":"${id}"""") && (s contains """"resourcesProvider":true""") && @@ -42,10 +41,7 @@ object BuildServerTest extends AbstractServerTest { } test("workspace/buildTargets") { _ => - svr.sendJsonRpc( - s"""{ "jsonrpc": "2.0", "id": "${nextId()}", "method": "workspace/buildTargets", "params": {} }""" - ) - assert(processing("workspace/buildTargets")) + sendRequest("workspace/buildTargets") val result = svr.waitFor[WorkspaceBuildTargetsResult](10.seconds) val utilTarget = result.targets.find(_.displayName.contains("util")).get assert(utilTarget.id.uri.toString.endsWith("#util/Compile")) @@ -58,16 +54,14 @@ object BuildServerTest extends AbstractServerTest { test("buildTarget/sources") { _ => val buildTarget = buildTargetUri("util", "Compile") val badBuildTarget = buildTargetUri("badBuildTarget", "Compile") - svr.sendJsonRpc(buildTargetSources(Seq(buildTarget, badBuildTarget))) - assert(processing("buildTarget/sources")) + buildTargetSources(Seq(buildTarget, badBuildTarget)) val s = svr.waitFor[SourcesResult](10.seconds) val sources = s.items.head.sources.map(_.uri) assert(sources.contains(new File(svr.baseDirectory, "util/src/main/scala").toURI)) } test("buildTarget/sources: base sources") { _ => val buildTarget = buildTargetUri("buildserver", "Compile") - svr.sendJsonRpc(buildTargetSources(Seq(buildTarget))) - assert(processing("buildTarget/sources")) + buildTargetSources(Seq(buildTarget)) val s = svr.waitFor[SourcesResult](10.seconds) val sources = s.items.head.sources val expectedSource = SourceItem( @@ -80,8 +74,7 @@ object BuildServerTest extends AbstractServerTest { test("buildTarget/sources: sbt") { _ => val x = new URI(s"${svr.baseDirectory.getAbsoluteFile.toURI}#buildserver-build") - svr.sendJsonRpc(buildTargetSources(Seq(x))) - assert(processing("buildTarget/sources")) + buildTargetSources(Seq(x)) val s = svr.waitFor[SourcesResult](10.seconds) val sources = s.items.head.sources.map(_.uri).sorted val expectedSources = Vector( @@ -99,17 +92,13 @@ object BuildServerTest extends AbstractServerTest { test("buildTarget/compile") { _ => val buildTarget = buildTargetUri("util", "Compile") - - compile(buildTarget, id = nextId()) - - assert(processing("buildTarget/compile")) + compile(buildTarget) val res = svr.waitFor[BspCompileResult](10.seconds) assert(res.statusCode == StatusCode.Success) } test("buildTarget/compile - reports compilation progress") { _ => val buildTarget = buildTargetUri("runAndTest", "Compile") - compile(buildTarget) // This doesn't always come back in 10s on CI. @@ -271,18 +260,19 @@ object BuildServerTest extends AbstractServerTest { ) } - test("buildTarget/scalacOptions") { _ => + test("buildTarget/scalacOptions, buildTarget/javacOptions") { _ => val buildTarget = buildTargetUri("util", "Compile") val badBuildTarget = buildTargetUri("badBuildTarget", "Compile") - val id = nextId() - svr.sendJsonRpc( - s"""{ "jsonrpc": "2.0", "id": "$id", "method": "buildTarget/scalacOptions", "params": { - | "targets": [{ "uri": "$buildTarget" }, { "uri": "$badBuildTarget" }] - |} }""".stripMargin - ) - assert(processing("buildTarget/scalacOptions")) + val id1 = scalacOptions(Seq(buildTarget, badBuildTarget)) + assert(svr.waitForString(10.seconds) { s => - (s contains s""""id":"$id"""") && + (s contains s""""id":"$id1"""") && + (s contains "scala-library-2.13.11.jar") + }) + + val id2 = javacOptions(Seq(buildTarget, badBuildTarget)) + assert(svr.waitForString(10.seconds) { s => + (s contains s""""id":"$id2"""") && (s contains "scala-library-2.13.11.jar") }) } @@ -306,7 +296,7 @@ object BuildServerTest extends AbstractServerTest { | "targets": [{ "uri": "$buildTarget" }] |} }""".stripMargin ) - assert(processing("buildTarget/cleanCache")) + assertProcessing("buildTarget/cleanCache") val res = svr.waitFor[CleanCacheResult](10.seconds) assert(res.cleaned) assert(targetDir.list().isEmpty) @@ -316,7 +306,7 @@ object BuildServerTest extends AbstractServerTest { svr.sendJsonRpc( s"""{ "jsonrpc": "2.0", "id": "${nextId()}", "method": "workspace/buildTargets", "params": {} }""" ) - assert(processing("workspace/buildTargets")) + assertProcessing("workspace/buildTargets") val result = svr.waitFor[WorkspaceBuildTargetsResult](10.seconds) val allTargets = result.targets.map(_.id.uri) @@ -327,7 +317,7 @@ object BuildServerTest extends AbstractServerTest { | ] |} }""".stripMargin ) - assert(processing("buildTarget/cleanCache")) + assertProcessing("buildTarget/cleanCache") val res = svr.waitFor[CleanCacheResult](10.seconds) assert(res.cleaned) } @@ -337,7 +327,7 @@ object BuildServerTest extends AbstractServerTest { svr.sendJsonRpc( s"""{ "jsonrpc": "2.0", "id": "$id", "method": "workspace/reload"}""" ) - assert(processing("workspace/reload")) + assertProcessing("workspace/reload") assert(svr.waitForString(10.seconds) { s => (s contains s""""id":"$id"""") && (s contains """"result":null""") @@ -355,9 +345,8 @@ object BuildServerTest extends AbstractServerTest { |) |""".stripMargin ) - val id = nextId() + val id = reloadWorkspace() // reload - reloadWorkspace(id) assert( svr.waitForString(10.seconds) { s => s.contains(s""""buildTarget":{"uri":"$metaBuildTarget"}""") && @@ -405,7 +394,7 @@ object BuildServerTest extends AbstractServerTest { | "targets": [{ "uri": "$buildTarget" }, { "uri": "$badBuildTarget" }] |} }""".stripMargin ) - assert(processing("buildTarget/scalaMainClasses")) + assertProcessing("buildTarget/scalaMainClasses") assert(svr.waitForString(30.seconds) { s => (s contains s""""id":"$id"""") && (s contains """"class":"main.Main"""") @@ -422,7 +411,7 @@ object BuildServerTest extends AbstractServerTest { | "data": { "class": "main.Main" } |} }""".stripMargin ) - assert(processing("buildTarget/run")) + assertProcessing("buildTarget/run") assert(svr.waitForString(10.seconds) { s => (s contains "build/logMessage") && (s contains """"message":"Hello World!"""") @@ -443,7 +432,7 @@ object BuildServerTest extends AbstractServerTest { | "params": { "targets": [{ "uri": "$buildTarget" }] } |}""".stripMargin ) - assert(processing("buildTarget/jvmRunEnvironment")) + assertProcessing("buildTarget/jvmRunEnvironment") assert { svr.waitForString(10.seconds) { s => (s contains s""""id":"$id"""") && @@ -465,7 +454,7 @@ object BuildServerTest extends AbstractServerTest { | "params": { "targets": [{ "uri": "$buildTarget" }] } |}""".stripMargin ) - assert(processing("buildTarget/jvmTestEnvironment")) + assertProcessing("buildTarget/jvmTestEnvironment") assert { svr.waitForString(10.seconds) { s => (s contains s""""id":"$id"""") && @@ -487,7 +476,7 @@ object BuildServerTest extends AbstractServerTest { | "targets": [{ "uri": "$buildTarget" }, { "uri": "$badBuildTarget" }] |} }""".stripMargin ) - assert(processing("buildTarget/scalaTestClasses")) + assertProcessing("buildTarget/scalaTestClasses") assert(svr.waitForString(10.seconds) { s => (s contains s""""id":"$id"""") && (s contains """"tests.FailingTest"""") && @@ -504,7 +493,7 @@ object BuildServerTest extends AbstractServerTest { | "targets": [{ "uri": "$buildTarget" }] |} }""".stripMargin ) - assert(processing("buildTarget/test")) + assertProcessing("buildTarget/test") assert(svr.waitForString(10.seconds) { s => (s contains s""""id":"$id"""") && (s contains """"statusCode":2""") @@ -528,7 +517,7 @@ object BuildServerTest extends AbstractServerTest { | } |} }""".stripMargin ) - assert(processing("buildTarget/test")) + assertProcessing("buildTarget/test") assert(svr.waitForString(10.seconds) { s => (s contains s""""id":"$id"""") && (s contains """"statusCode":1""") @@ -557,8 +546,7 @@ object BuildServerTest extends AbstractServerTest { test("buildTarget/compile: respond error") { _ => val buildTarget = buildTargetUri("respondError", "Compile") - val id = nextId() - compile(buildTarget, id) + val id = compile(buildTarget) assert(svr.waitForString(10.seconds) { s => s.contains(s""""id":"$id"""") && s.contains(""""error"""") && @@ -576,7 +564,7 @@ object BuildServerTest extends AbstractServerTest { | "targets": [{ "uri": "$buildTarget" }, { "uri": "$badBuildTarget" }] |} }""".stripMargin ) - assert(processing("buildTarget/resources")) + assertProcessing("buildTarget/resources") assert(svr.waitForString(10.seconds) { s => (s contains s""""id":"$id"""") && (s contains "util/src/main/resources/") }) @@ -590,7 +578,7 @@ object BuildServerTest extends AbstractServerTest { | "targets": [{ "uri": "$buildTarget" }, { "uri": "$badBuildTarget" }] |} }""".stripMargin ) - assert(processing("buildTarget/outputPaths")) + assertProcessing("buildTarget/outputPaths") val actualResult = svr.waitFor[OutputPathsResult](10.seconds) val expectedResult = OutputPathsResult( items = Vector( @@ -608,7 +596,7 @@ object BuildServerTest extends AbstractServerTest { assert(actualResult == expectedResult) } - private def initializeRequest(id: Int): Unit = { + private def initializeRequest(): Int = { val params = InitializeBuildParams( "test client", "1.0.0", @@ -617,35 +605,55 @@ object BuildServerTest extends AbstractServerTest { BuildClientCapabilities(Vector("scala")), None ) - svr.sendJsonRpc(request(id, "build/initialize", params)) + sendRequest("build/initialize", params) } - private def processing(method: String, debug: Boolean = false): Boolean = { - svr.waitForString(10.seconds) { msg => + private def assertProcessing(method: String, debug: Boolean = false): Unit = { + assert(svr.waitForString(10.seconds) { msg => if (debug) println(msg) - msg.contains("build/logMessage") && - msg.contains(s""""message":"Processing $method"""") - } + msg.contains("build/logMessage") && msg.contains(s""""message":"Processing $method"""") + }) } - private def reloadWorkspace(id: Int = nextId()): Unit = - svr.sendJsonRpc(s"""{ "jsonrpc": "2.0", "id": "$id", "method": "workspace/reload"}""") + private def reloadWorkspace(): Int = + sendRequest("workspace/reload") - private def compile(buildTarget: URI, id: Int = nextId()): Unit = { + private def compile(buildTarget: URI): Int = { val params = CompileParams(targets = Vector(BuildTargetIdentifier(buildTarget)), None, Vector.empty) - svr.sendJsonRpc(request(id, "buildTarget/compile", params)) + sendRequest("buildTarget/compile", params) } - private def buildTargetSources(buildTargets: Seq[URI], id: Int = nextId()): String = { + private def scalacOptions(buildTargets: Seq[URI]): Int = { val targets = buildTargets.map(BuildTargetIdentifier.apply).toVector - request(id, "buildTarget/sources", SourcesParams(targets)) + sendRequest("buildTarget/scalacOptions", ScalacOptionsParams(targets)) } - private def request[T: JsonWriter](id: Int, method: String, params: T): String = { - val request = JsonRpcRequestMessage("2.0", id.toString, method, Converter.toJson(params).get) - val json = Converter.toJson(request).get - CompactPrinter(json) + private def javacOptions(buildTargets: Seq[URI]): Int = { + val targets = buildTargets.map(BuildTargetIdentifier.apply).toVector + sendRequest("buildTarget/scalacOptions", ScalacOptionsParams(targets)) + } + + private def buildTargetSources(buildTargets: Seq[URI]): Int = { + val targets = buildTargets.map(BuildTargetIdentifier.apply).toVector + sendRequest("buildTarget/sources", SourcesParams(targets)) + } + + private def sendRequest(method: String): Int = { + val id = nextId() + val msg = JsonRpcRequestMessage("2.0", id.toString, method, None) + val json = Converter.toJson(msg).get + svr.sendJsonRpc(CompactPrinter(json)) + id + } + + private def sendRequest[T: JsonWriter](method: String, params: T): Int = { + val id = nextId() + val msg = JsonRpcRequestMessage("2.0", id.toString, method, Converter.toJson(params).get) + val json = Converter.toJson(msg).get + svr.sendJsonRpc(CompactPrinter(json)) + if (method != "build/initialize") assertProcessing(method) + id } private def buildTargetUri(project: String, config: String): URI =