From 1e89d713111fc48c11a1a102b4d70e4df0374078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pa=C5=82ka?= Date: Thu, 24 Mar 2022 15:35:57 +0100 Subject: [PATCH 01/22] Add support for scala-output-version flag in Scala 3 --- main/src/main/scala/sbt/Defaults.scala | 80 +++++++++++-- main/src/main/scala/sbt/Keys.scala | 6 +- .../scala/sbt/internal/ClassLoaders.scala | 4 +- .../build.sbt | 30 +++++ .../scala-output-version-redundant-flags/test | 3 + .../scala-output-version/Bar.scala | 5 + .../scala-output-version/build.sbt | 112 ++++++++++++++++++ .../foo/src/main/scala-2.13/Foo.scala | 11 ++ .../foo/src/main/scala-3/Foo.scala | 17 +++ .../foo/src/test/scala-2.13/FooTest.scala | 7 ++ .../foo/src/test/scala-3/FooTest.scala | 19 +++ .../scala-output-version/test | 35 ++++++ 12 files changed, 317 insertions(+), 12 deletions(-) create mode 100644 sbt-app/src/sbt-test/dependency-management/scala-output-version-redundant-flags/build.sbt create mode 100644 sbt-app/src/sbt-test/dependency-management/scala-output-version-redundant-flags/test create mode 100644 sbt-app/src/sbt-test/dependency-management/scala-output-version/Bar.scala create mode 100644 sbt-app/src/sbt-test/dependency-management/scala-output-version/build.sbt create mode 100644 sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/main/scala-2.13/Foo.scala create mode 100644 sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/main/scala-3/Foo.scala create mode 100644 sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/test/scala-2.13/FooTest.scala create mode 100644 sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/test/scala-3/FooTest.scala create mode 100644 sbt-app/src/sbt-test/dependency-management/scala-output-version/test diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index facf6637a..d7a1f6a4b 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -679,6 +679,15 @@ object Defaults extends BuildCommon { else topLoader }, scalaInstance := scalaInstanceTask.value, + runtimeScalaInstance := Def.taskDyn { + scalaOutputVersion.?.value + .map { version => + scalaInstanceTask0(version = version, isRuntimeInstance = true) + } + .getOrElse { + scalaInstance + } + }.value, crossVersion := (if (crossPaths.value) CrossVersion.binary else CrossVersion.disabled), pluginCrossBuild / sbtBinaryVersion := binarySbtVersion( (pluginCrossBuild / sbtVersion).value @@ -977,6 +986,49 @@ object Defaults extends BuildCommon { old ++ Seq("-Wconf:cat=unused-nowarn:s", "-Xsource:3") else old }, + scalacOptions := { + val old = scalacOptions.value + + // 3 possible sources of Scala output version + val fromCompiler = CrossVersion + .partialVersion(scalaVersion.value) + .map { case (major, minor) => s"${major}.${minor}" } + .getOrElse( + sys.error(s"Wrong value of `scalaVersion`: ${scalaVersion.value}") + ) + + val maybeFromSetting = scalaOutputVersion.?.value.map { version => + CrossVersion + .partialVersion(version) + .map { case (major, minor) => s"${major}.${minor}" } + .getOrElse( + sys.error(s"Wrong value of `scalaOutputVersion`: ${version}") + ) + } + + val maybeFromFlags = old.zipWithIndex.flatMap { + case (opt, idx) => + if (opt.startsWith("-scala-output-version:")) + Some(opt.stripPrefix("-scala-output-version:")) + else if (opt == "-scala-output-version" && idx + 1 < old.length) + Some(old(idx + 1)) + else None + }.lastOption + + // Add -scala-output-version flag when minor Scala versions are different + // unless the flag is already set properly + maybeFromSetting match { + case Some(fromSetting) if fromSetting != fromCompiler => + maybeFromFlags match { + case Some(fromFlags) if fromFlags == fromSetting => + old + case _ => + old ++ Seq("-scala-output-version", fromSetting) + } + case _ => + old + } + }, persistJarClasspath :== true, classpathEntryDefinesClassVF := { (if (persistJarClasspath.value) classpathDefinesClassCache.value @@ -1087,13 +1139,19 @@ object Defaults extends BuildCommon { } def scalaInstanceTask: Initialize[Task[ScalaInstance]] = Def.taskDyn { + scalaInstanceTask0(scalaVersion.value, false) + } + + private def scalaInstanceTask0( + version: String, + isRuntimeInstance: Boolean + ): Initialize[Task[ScalaInstance]] = Def.taskDyn { // if this logic changes, ensure that `unmanagedScalaInstanceOnly` and `update` are changed // appropriately to avoid cycles scalaHome.value match { case Some(h) => scalaInstanceFromHome(h) case None => val scalaProvider = appConfiguration.value.provider.scalaProvider - val version = scalaVersion.value if (version == scalaProvider.version) // use the same class loader as the Scala classes used by sbt Def.task { val allJars = scalaProvider.jars @@ -1111,7 +1169,7 @@ object Defaults extends BuildCommon { case _ => ScalaInstance(version, scalaProvider) } } else - scalaInstanceFromUpdate + scalaInstanceFromUpdate0(isRuntimeInstance) } } @@ -1132,22 +1190,29 @@ object Defaults extends BuildCommon { pre + post } - def scalaInstanceFromUpdate: Initialize[Task[ScalaInstance]] = Def.task { + def scalaInstanceFromUpdate: Initialize[Task[ScalaInstance]] = scalaInstanceFromUpdate0(false) + + private def scalaInstanceFromUpdate0( + isRuntimeInstance: Boolean + ): Initialize[Task[ScalaInstance]] = Def.task { val sv = scalaVersion.value val fullReport = update.value val toolReport = fullReport .configuration(Configurations.ScalaTool) .getOrElse(sys.error(noToolConfiguration(managedScalaInstance.value))) + lazy val runtimeReport = fullReport.configuration(Configurations.Runtime).get + val libraryReport = if (isRuntimeInstance) runtimeReport else toolReport - def file(id: String): File = { + def libraryFile(id: String): File = { val files = for { - m <- toolReport.modules if m.module.name.startsWith(id) + m <- libraryReport.modules if m.module.name.startsWith(id) (art, file) <- m.artifacts if art.`type` == Artifact.DefaultType } yield file files.headOption getOrElse sys.error(s"Missing $id jar file") } + val libraryJars = ScalaArtifacts.libraryIds(sv).map(libraryFile) val allCompilerJars = toolReport.modules.flatMap(_.artifacts.map(_._2)) val allDocJars = fullReport @@ -1155,7 +1220,6 @@ object Defaults extends BuildCommon { .toSeq .flatMap(_.modules) .flatMap(_.artifacts.map(_._2)) - val libraryJars = ScalaArtifacts.libraryIds(sv).map(file) makeScalaInstance( sv, @@ -2034,7 +2098,7 @@ object Defaults extends BuildCommon { def runnerInit: Initialize[Task[ScalaRun]] = Def.task { val tmp = taskTemporaryDirectory.value val resolvedScope = resolvedScoped.value.scope - val si = scalaInstance.value + val si = runtimeScalaInstance.value val s = streams.value val opts = forkOptions.value val options = javaOptions.value @@ -3255,7 +3319,7 @@ object Classpaths { autoScalaLibrary.value && scalaHome.value.isEmpty && managedScalaInstance.value, sbtPlugin.value, scalaOrganization.value, - scalaVersion.value + scalaOutputVersion.?.value.getOrElse(scalaVersion.value) ), // Override the default to handle mixing in the sbtPlugin + scala dependencies. allDependencies := { diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 2270ee573..a4fee0b85 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -186,9 +186,11 @@ object Keys { val compileOptions = taskKey[CompileOptions]("Collects basic options to configure compilers").withRank(DTask) val compileInputs = taskKey[Inputs]("Collects all inputs needed for compilation.").withRank(DTask) val scalaHome = settingKey[Option[File]]("If Some, defines the local Scala installation to use for compilation, running, and testing.").withRank(ASetting) - val scalaInstance = taskKey[ScalaInstance]("Defines the Scala instance to use for compilation, running, and testing.").withRank(DTask) + val scalaInstance = taskKey[ScalaInstance]("Defines the Scala instance to use for compilation").withRank(DTask) + val runtimeScalaInstance = taskKey[ScalaInstance]("Defines the Scala instance used at runtime (also for tests).").withRank(DTask) val scalaOrganization = settingKey[String]("Organization/group ID of the Scala used in the project. Default value is 'org.scala-lang'. This is an advanced setting used for clones of the Scala Language. It should be disregarded in standard use cases.").withRank(CSetting) - val scalaVersion = settingKey[String]("The version of Scala used for building.").withRank(APlusSetting) + val scalaVersion = settingKey[String]("The version of Scala compiler used for building this project.").withRank(APlusSetting) + val scalaOutputVersion = settingKey[String]("The version of Scala standard library used for running this project and declared as its transitive dependency.").withRank(APlusSetting) val scalaBinaryVersion = settingKey[String]("The Scala version substring describing binary compatibility.").withRank(BPlusSetting) val crossScalaVersions = settingKey[Seq[String]]("The versions of Scala used when cross-building.").withRank(BPlusSetting) val crossVersion = settingKey[CrossVersion]("Configures handling of the Scala version when cross-building.").withRank(CSetting) diff --git a/main/src/main/scala/sbt/internal/ClassLoaders.scala b/main/src/main/scala/sbt/internal/ClassLoaders.scala index 1c5df8aa0..0275ae9dd 100644 --- a/main/src/main/scala/sbt/internal/ClassLoaders.scala +++ b/main/src/main/scala/sbt/internal/ClassLoaders.scala @@ -35,7 +35,7 @@ private[sbt] object ClassLoaders { * Get the class loader for a test task. The configuration could be IntegrationTest or Test. */ private[sbt] def testTask: Def.Initialize[Task[ClassLoader]] = Def.task { - val si = scalaInstance.value + val si = runtimeScalaInstance.value val cp = fullClasspath.value.map(_.data) val dependencyStamps = modifiedTimes((dependencyClasspathFiles / outputFileStamps).value).toMap def getLm(f: File): Long = dependencyStamps.getOrElse(f, IO.getModifiedTimeOrZero(f)) @@ -64,7 +64,7 @@ private[sbt] object ClassLoaders { private[sbt] def runner: Def.Initialize[Task[ScalaRun]] = Def.taskDyn { val resolvedScope = resolvedScoped.value.scope - val instance = scalaInstance.value + val instance = runtimeScalaInstance.value val s = streams.value val opts = forkOptions.value val options = javaOptions.value diff --git a/sbt-app/src/sbt-test/dependency-management/scala-output-version-redundant-flags/build.sbt b/sbt-app/src/sbt-test/dependency-management/scala-output-version-redundant-flags/build.sbt new file mode 100644 index 000000000..e1bc21ae3 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/scala-output-version-redundant-flags/build.sbt @@ -0,0 +1,30 @@ +val checkOptions = taskKey[Unit]("") + +lazy val p1 = project + .settings( + scalaVersion := "3.0.2", + checkOptions := { + assert((Compile / scalacOptions).value == Seq()) + assert((Test / scalacOptions).value == Seq()) + } + ) + +lazy val p2 = project + .settings( + scalaVersion := "3.0.2", + scalaOutputVersion := "3.0.2", + checkOptions := { + assert((Compile / scalacOptions).value == Seq()) + assert((Test / scalacOptions).value == Seq()) + } + ) + +lazy val p3 = project + .settings( + scalaVersion := "3.1.2-RC2", + scalaOutputVersion := "3.0.2", + checkOptions := { + assert((Compile / scalacOptions).value == Seq("-scala-output-version", "3.0")) + assert((Test / scalacOptions).value == Seq("-scala-output-version", "3.0")) + } + ) diff --git a/sbt-app/src/sbt-test/dependency-management/scala-output-version-redundant-flags/test b/sbt-app/src/sbt-test/dependency-management/scala-output-version-redundant-flags/test new file mode 100644 index 000000000..500721a31 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/scala-output-version-redundant-flags/test @@ -0,0 +1,3 @@ +> p1 / checkOptions +> p2 / checkOptions +> p3 / checkOptions \ No newline at end of file diff --git a/sbt-app/src/sbt-test/dependency-management/scala-output-version/Bar.scala b/sbt-app/src/sbt-test/dependency-management/scala-output-version/Bar.scala new file mode 100644 index 000000000..772478e95 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/scala-output-version/Bar.scala @@ -0,0 +1,5 @@ +object Bar { + def main(args: Array[String]) = { + assert(foo.main.Foo.numbers == Seq(1, 2, 3)) + } +} diff --git a/sbt-app/src/sbt-test/dependency-management/scala-output-version/build.sbt b/sbt-app/src/sbt-test/dependency-management/scala-output-version/build.sbt new file mode 100644 index 000000000..78ad544f4 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/scala-output-version/build.sbt @@ -0,0 +1,112 @@ +// cases 1, 2, 3: check for scala version in bar +// case a: check locally published Ivy dependency +// case b: check locally published Maven dependency +// case c: check unpublished sibling module dependency + +val org = "org.example" +val fooName = "sbt-test-scala-output-version-foo" +val revision = "0.0.1-SNAPSHOT" + +ThisBuild / organization := org +ThisBuild / version := revision + +lazy val foo = project.in(file("foo")) + .settings( + name := fooName, + scalaVersion := "3.1.2-RC2", + crossScalaVersions := List("2.13.8", "3.1.2-RC2"), + scalaOutputVersion := "3.0.2", + scalaOutputVersion := { + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((3, _)) => "3.0.2" + case _ => scalaVersion.value + } + }, + libraryDependencies ++= Seq( + "org.scalameta" %% "munit" % "0.7.29" % Test + ), + TaskKey[Unit]("checkIvy") := { + val ivyFile = makeIvyXml.value + val ivyContent = IO.read(ivyFile) + val expectedContent = """""" + val hasCorrectStdlib = ivyContent.contains(expectedContent) + if (!hasCorrectStdlib) sys.error(s"The produced Ivy file is incorrect:\n\n${ivyContent}") + }, + TaskKey[Unit]("checkPom") := { + val pomFile = makePom.value + val pomContent = IO.read(pomFile) + val flatPom = pomContent.filterNot(_.isWhitespace) + val expectedContent = "org.scala-langscala3-library_33.0.2" + val hasCorrectStdlib = flatPom.contains(expectedContent) + if (!hasCorrectStdlib) sys.error(s"The produced POM file is incorrect:\n\n${pomContent}") + } + ) + +val scala3_1 = Seq(scalaVersion := "3.1.1") +val scala3_0 = Seq(scalaVersion := "3.0.2") +val scala2_13 = Seq(scalaVersion := "2.13.8") +val ivyFooDep = Seq( + libraryDependencies ++= Seq( + org %% fooName % revision + ), + resolvers := Seq(Resolver.defaultLocal) +) +val mavenFooDep = Seq( + libraryDependencies ++= Seq( + org %% fooName % revision + ), + resolvers := Seq(Resolver.mavenLocal) +) + +lazy val bar1a = project.in(file("bar1a")) + .settings( + scala3_1, + ivyFooDep + ) + +lazy val bar1b = project.in(file("bar1b")) + .settings( + scala3_1, + mavenFooDep + ) + +lazy val bar1c = project.in(file("bar1c")) + .settings( + scala3_1, + ).dependsOn(foo) + + +lazy val bar2a = project.in(file("bar2a")) + .settings( + scala3_0, + ivyFooDep + ) + +lazy val bar2b = project.in(file("bar2b")) + .settings( + scala3_0, + mavenFooDep + ) + +lazy val bar2c = project.in(file("bar2c")) + .settings( + scala3_0, + ).dependsOn(foo) + + +lazy val bar3a = project.in(file("bar3a")) + .settings( + scala2_13, + ivyFooDep + ) + +lazy val bar3b = project.in(file("bar3b")) + .settings( + scala2_13, + mavenFooDep + ) + +lazy val bar3c = project.in(file("bar3c")) + .settings( + scala2_13, + ).dependsOn(foo) diff --git a/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/main/scala-2.13/Foo.scala b/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/main/scala-2.13/Foo.scala new file mode 100644 index 000000000..6347cd083 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/main/scala-2.13/Foo.scala @@ -0,0 +1,11 @@ +package foo.main + +object Foo { + val numbers = Seq(1, 2, 3) +} + +object Run { + def main(args: Array[String]) = { + assert(Foo.numbers.length == 3) + } +} diff --git a/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/main/scala-3/Foo.scala b/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/main/scala-3/Foo.scala new file mode 100644 index 000000000..11ec280a3 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/main/scala-3/Foo.scala @@ -0,0 +1,17 @@ +package foo.main + +class MyException extends Exception("MyException") + +@annotation.experimental +object Exceptional: + import language.experimental.saferExceptions + def foo(): Unit throws MyException = // this requires at least 3.1.x to compile + throw new MyException + +object Foo: + val numbers = Seq(1, 2, 3) + +@main def run() = + val canEqualMethods = classOf[CanEqual.type].getMethods.toList + assert( canEqualMethods.exists(_.getName == "canEqualSeq")) // since 3.0.x + assert(!canEqualMethods.exists(_.getName == "canEqualSeqs")) // since 3.1.x diff --git a/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/test/scala-2.13/FooTest.scala b/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/test/scala-2.13/FooTest.scala new file mode 100644 index 000000000..4102861ae --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/test/scala-2.13/FooTest.scala @@ -0,0 +1,7 @@ +package foo.test + +class FooTest extends munit.FunSuite { + test("foo") { + assertEquals(foo.main.Foo.numbers, Seq(1, 2, 3)) + } +} diff --git a/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/test/scala-3/FooTest.scala b/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/test/scala-3/FooTest.scala new file mode 100644 index 000000000..cc0ee76b6 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/test/scala-3/FooTest.scala @@ -0,0 +1,19 @@ +package foo.test + +class MyException extends Exception("MyException") + +@annotation.experimental +object Exceptional: + import language.experimental.saferExceptions + def foo(): Unit throws MyException = // this requires at least 3.1.x to compile + throw new MyException + + +class FooTest extends munit.FunSuite: + test("foo") { + assertEquals(foo.main.Foo.numbers, Seq(1, 2, 3)) + + val canEqualMethods = classOf[CanEqual.type].getMethods.toList + assert( canEqualMethods.exists(_.getName == "canEqualSeq")) // since 3.0.x + assert(!canEqualMethods.exists(_.getName == "canEqualSeqs")) // since 3.1.x + } diff --git a/sbt-app/src/sbt-test/dependency-management/scala-output-version/test b/sbt-app/src/sbt-test/dependency-management/scala-output-version/test new file mode 100644 index 000000000..ee66638fb --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/scala-output-version/test @@ -0,0 +1,35 @@ +$ copy-file Bar.scala bar1a/src/main/scala/Bar.scala +$ copy-file Bar.scala bar1b/src/main/scala/Bar.scala +$ copy-file Bar.scala bar1c/src/main/scala/Bar.scala +$ copy-file Bar.scala bar2a/src/main/scala/Bar.scala +$ copy-file Bar.scala bar2b/src/main/scala/Bar.scala +$ copy-file Bar.scala bar2c/src/main/scala/Bar.scala +$ copy-file Bar.scala bar3a/src/main/scala/Bar.scala +$ copy-file Bar.scala bar3b/src/main/scala/Bar.scala +$ copy-file Bar.scala bar3c/src/main/scala/Bar.scala + +> ++3.1.2-RC2 +> foo / compile +> foo / run +> foo / test +> foo / publishLocal +> foo / checkIvy +> foo / publishM2 +> foo / checkPom + +> ++2.13.8 +> foo / compile +> foo / run +> foo / test +> foo / publishLocal +> foo / publishM2 + +> bar1a / run +> bar1b / run +> bar1c / run +> bar2a / run +> bar2b / run +> bar2c / run +> bar3a / run +> bar3b / run +> bar3c / run From 491f70cd30344b87bf52f972e903d52b3623d4ff Mon Sep 17 00:00:00 2001 From: Brice Jaglin Date: Wed, 23 Mar 2022 12:25:35 +0100 Subject: [PATCH 02/22] includePluginResolvers should work for coursier resolutions --- .../sbt/coursierint/CoursierRepositoriesTasks.scala | 11 +---------- .../dependency-management/resolvers-plugin/build.sbt | 7 +++++-- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/main/src/main/scala/sbt/coursierint/CoursierRepositoriesTasks.scala b/main/src/main/scala/sbt/coursierint/CoursierRepositoriesTasks.scala index 6545478a2..e05916408 100644 --- a/main/src/main/scala/sbt/coursierint/CoursierRepositoriesTasks.scala +++ b/main/src/main/scala/sbt/coursierint/CoursierRepositoriesTasks.scala @@ -57,16 +57,7 @@ object CoursierRepositoriesTasks { private final val keepPreloaded = false // coursierKeepPreloaded.value def coursierResolversTask: Def.Initialize[sbt.Task[Seq[Resolver]]] = Def.task { - val bootResOpt = bootResolvers.value - val overrideFlag = overrideBuildResolvers.value - val result0 = bootResOpt.filter(_ => overrideFlag) match { - case Some(r) => r - case None => - val extRes = externalResolvers.value - val isSbtPlugin = sbtPlugin.value - if (isSbtPlugin) sbtResolvers.value ++ extRes - else extRes - } + val result0 = fullResolvers.value.filterNot(_ == projectResolver.value) val reorderResolvers = true // coursierReorderResolvers.value val paths = ivyPaths.value diff --git a/sbt-app/src/sbt-test/dependency-management/resolvers-plugin/build.sbt b/sbt-app/src/sbt-test/dependency-management/resolvers-plugin/build.sbt index 5d76d6c17..cc96aa6cd 100644 --- a/sbt-app/src/sbt-test/dependency-management/resolvers-plugin/build.sbt +++ b/sbt-app/src/sbt-test/dependency-management/resolvers-plugin/build.sbt @@ -2,6 +2,9 @@ lazy val check = taskKey[Unit]("") ThisBuild / includePluginResolvers := true check := { - val rs = fullResolvers.value - assert(rs exists (_.name == "bintray-eed3si9n-sbt-plugins"), s"$rs does not include bintray") + val ivy = fullResolvers.value + assert(ivy exists (_.name == "bintray-eed3si9n-sbt-plugins"), s"$ivy does not include bintray") + + val cs = csrResolvers.value + assert(cs exists (_.name == "bintray-eed3si9n-sbt-plugins"), s"$cs does not include bintray") } From 862d373f02f178a844e7b48caa2f519386dc666b Mon Sep 17 00:00:00 2001 From: Rikito Taniguchi Date: Fri, 18 Mar 2022 02:23:08 +0900 Subject: [PATCH 03/22] Don't fire publishDiagnostic if there's no problems both in current and previous compilation --- main/src/main/scala/sbt/Defaults.scala | 8 +++-- .../internal/server/BuildServerReporter.scala | 30 +++++++++++-------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index d7a1f6a4b..fad4a6a0c 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -2374,10 +2374,11 @@ object Defaults extends BuildCommon { val ci = (compile / compileInputs).value val ping = earlyOutputPing.value val reporter = (compile / bspReporter).value + val prevAnalysis = previousCompile.value.analysis.toOption.getOrElse(Analysis.empty) BspCompileTask.compute(bspTargetIdentifier.value, thisProjectRef.value, configuration.value) { task => // TODO - Should readAnalysis + saveAnalysis be scoped by the compile task too? - compileIncrementalTaskImpl(task, s, ci, ping, reporter) + compileIncrementalTaskImpl(task, s, ci, ping, reporter, prevAnalysis) } } private val incCompiler = ZincUtil.defaultIncrementalCompiler @@ -2406,7 +2407,8 @@ object Defaults extends BuildCommon { s: TaskStreams, ci: Inputs, promise: PromiseWrap[Boolean], - reporter: BuildServerReporter + reporter: BuildServerReporter, + prev: CompileAnalysis ): CompileResult = { lazy val x = s.text(ExportStream) def onArgs(cs: Compilers) = { @@ -2428,7 +2430,7 @@ object Defaults extends BuildCommon { .withSetup(onProgress(setup)) try { val result = incCompiler.compile(i, s.log) - reporter.sendSuccessReport(result.getAnalysis) + reporter.sendSuccessReport(result.getAnalysis, prev) result } catch { case e: Throwable => diff --git a/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala b/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala index dba366578..8184b090c 100644 --- a/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala +++ b/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala @@ -38,7 +38,7 @@ sealed trait BuildServerReporter extends Reporter { protected def publishDiagnostic(problem: Problem): Unit - def sendSuccessReport(analysis: CompileAnalysis): Unit + def sendSuccessReport(analysis: CompileAnalysis, prev: CompileAnalysis): Unit def sendFailureReport(sources: Array[VirtualFile]): Unit @@ -86,20 +86,26 @@ final class BuildServerReporterImpl( if (ref.id().contains("<")) None else Some(converter.toPath(ref)) - override def sendSuccessReport(analysis: CompileAnalysis): Unit = { + override def sendSuccessReport(analysis: CompileAnalysis, prev: CompileAnalysis): Unit = { + val prevInfos = prev.readSourceInfos().getAllSourceInfos().asScala for { (source, infos) <- analysis.readSourceInfos.getAllSourceInfos.asScala filePath <- toSafePath(source) } { - val diagnostics = infos.getReportedProblems.toSeq.flatMap(toDiagnostic) - val params = PublishDiagnosticsParams( - textDocument = TextDocumentIdentifier(filePath.toUri), - buildTarget, - originId = None, - diagnostics.toVector, - reset = true - ) - exchange.notifyEvent("build/publishDiagnostics", params) + val prevProblems = prevInfos.get(source).map(_.getReportedProblems()).getOrElse(Array.empty) + val dontPublish = prevProblems.length == 0 && infos.getReportedProblems().length == 0 + + if (!dontPublish) { + val diagnostics = infos.getReportedProblems.toSeq.flatMap(toDiagnostic) + val params = PublishDiagnosticsParams( + textDocument = TextDocumentIdentifier(filePath.toUri), + buildTarget, + originId = None, + diagnostics.toVector, + reset = true + ) + exchange.notifyEvent("build/publishDiagnostics", params) + } } } @@ -179,7 +185,7 @@ final class BuildServerForwarder( protected override val underlying: Reporter ) extends BuildServerReporter { - override def sendSuccessReport(analysis: CompileAnalysis): Unit = () + override def sendSuccessReport(analysis: CompileAnalysis, prev: CompileAnalysis): Unit = () override def sendFailureReport(sources: Array[VirtualFile]): Unit = () From 620c55d6ac84bf10c992e2d7492c73703ec9362d Mon Sep 17 00:00:00 2001 From: Rikito Taniguchi Date: Fri, 18 Mar 2022 23:04:07 +0900 Subject: [PATCH 04/22] Re-publish warnings on BSP server startup Imitate https://github.com/scalacenter/bloop/commit/8aaf828b033c081f420c2c54879b0e28eeeae9df --- main/src/main/scala/sbt/Defaults.scala | 8 +++++-- .../internal/server/BuildServerProtocol.scala | 16 ++++++++++++++ .../internal/server/BuildServerReporter.scala | 21 +++++++++++++++---- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index fad4a6a0c..82724a3b5 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -2393,7 +2393,7 @@ object Defaults extends BuildCommon { val result0 = incCompiler .asInstanceOf[sbt.internal.inc.IncrementalCompilerImpl] .compileAllJava(in, s.log) - reporter.sendSuccessReport(result0.analysis()) + reporter.sendSuccessReport(result0.analysis(), Analysis.empty, false) result0.withHasModified(result0.hasModified || r.hasModified) } else r } catch { @@ -2430,7 +2430,11 @@ object Defaults extends BuildCommon { .withSetup(onProgress(setup)) try { val result = incCompiler.compile(i, s.log) - reporter.sendSuccessReport(result.getAnalysis, prev) + reporter.sendSuccessReport( + result.getAnalysis, + prev, + BuildServerProtocol.shouldReportAllPreviousProblems(task.targetId) + ) result } catch { case e: Throwable => diff --git a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala index 0c6655e2d..1cfae2054 100644 --- a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala @@ -39,11 +39,26 @@ import scala.collection.mutable import scala.util.control.NonFatal import scala.util.{ Failure, Success, Try } import scala.annotation.nowarn +import scala.collection.concurrent.TrieMap import sbt.testing.Framework object BuildServerProtocol { import sbt.internal.bsp.codec.JsonProtocol._ + /** + * Keep track of those projects that were compiled at least once so that we can + * decide to enable fresh reporting for projects that are compiled for the first time. + * + * see: https://github.com/scalacenter/bloop/issues/726 + */ + private val compiledTargetsAtLeastOnce = new TrieMap[bsp.BuildTargetIdentifier, Boolean]() + def shouldReportAllPreviousProblems(id: bsp.BuildTargetIdentifier): Boolean = { + compiledTargetsAtLeastOnce.putIfAbsent(id, true) match { + case Some(_) => false + case None => true + } + } + private val capabilities = BuildServerCapabilities( CompileProvider(BuildServerConnection.languages), TestProvider(BuildServerConnection.languages), @@ -354,6 +369,7 @@ object BuildServerProtocol { case r if r.method == Method.Initialize => val params = Converter.fromJson[InitializeBuildParams](json(r)).get checkMetalsCompatibility(semanticdbEnabled, semanticdbVersion, params, callback.log) + compiledTargetsAtLeastOnce.clear() val response = InitializeBuildResult( "sbt", diff --git a/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala b/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala index 8184b090c..98d59d645 100644 --- a/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala +++ b/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala @@ -38,7 +38,11 @@ sealed trait BuildServerReporter extends Reporter { protected def publishDiagnostic(problem: Problem): Unit - def sendSuccessReport(analysis: CompileAnalysis, prev: CompileAnalysis): Unit + def sendSuccessReport( + analysis: CompileAnalysis, + prev: CompileAnalysis, + reportAllPreviousProblems: Boolean + ): Unit def sendFailureReport(sources: Array[VirtualFile]): Unit @@ -86,7 +90,11 @@ final class BuildServerReporterImpl( if (ref.id().contains("<")) None else Some(converter.toPath(ref)) - override def sendSuccessReport(analysis: CompileAnalysis, prev: CompileAnalysis): Unit = { + override def sendSuccessReport( + analysis: CompileAnalysis, + prev: CompileAnalysis, + reportAllPreviousProblems: Boolean + ): Unit = { val prevInfos = prev.readSourceInfos().getAllSourceInfos().asScala for { (source, infos) <- analysis.readSourceInfos.getAllSourceInfos.asScala @@ -94,8 +102,9 @@ final class BuildServerReporterImpl( } { val prevProblems = prevInfos.get(source).map(_.getReportedProblems()).getOrElse(Array.empty) val dontPublish = prevProblems.length == 0 && infos.getReportedProblems().length == 0 + val shouldPublish = reportAllPreviousProblems || !dontPublish - if (!dontPublish) { + if (shouldPublish) { val diagnostics = infos.getReportedProblems.toSeq.flatMap(toDiagnostic) val params = PublishDiagnosticsParams( textDocument = TextDocumentIdentifier(filePath.toUri), @@ -185,7 +194,11 @@ final class BuildServerForwarder( protected override val underlying: Reporter ) extends BuildServerReporter { - override def sendSuccessReport(analysis: CompileAnalysis, prev: CompileAnalysis): Unit = () + override def sendSuccessReport( + analysis: CompileAnalysis, + prev: CompileAnalysis, + reportAllPreviousProblems: Boolean + ): Unit = () override def sendFailureReport(sources: Array[VirtualFile]): Unit = () From f5e9ab8424965683dac8e5d2ed6685a689a1fe3e Mon Sep 17 00:00:00 2001 From: Rikito Taniguchi Date: Mon, 21 Mar 2022 21:35:11 +0900 Subject: [PATCH 05/22] Add test to verify server doesn't send notifications --- .../src/test/scala/testpkg/BuildServerTest.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/server-test/src/test/scala/testpkg/BuildServerTest.scala b/server-test/src/test/scala/testpkg/BuildServerTest.scala index 9cbe43980..a6d900a16 100644 --- a/server-test/src/test/scala/testpkg/BuildServerTest.scala +++ b/server-test/src/test/scala/testpkg/BuildServerTest.scala @@ -125,10 +125,25 @@ object BuildServerTest extends AbstractServerTest { s.contains(""""message":"Compiling runAndTest (100%)"""") }) + assert(svr.waitForString(60.seconds) { s => + s.contains("build/publishDiagnostics") + s.contains(""""diagnostics":[]""") + }) + assert(svr.waitForString(60.seconds) { s => s.contains("build/taskFinish") && s.contains(""""message":"Compiled runAndTest"""") }) + + svr.sendJsonRpc( + s"""{ "jsonrpc": "2.0", "id": "34", "method": "buildTarget/compile", "params": { + | "targets": [{ "uri": "$buildTarget" }] + |} }""".stripMargin + ) + + assert(!svr.waitForString(30.seconds) { s => + s.contains("build/publishDiagnostics") + }, "shouldn't send publishDiagnostics if there's no change in diagnostics") } test("buildTarget/scalacOptions") { _ => From f90b09f1ee3ffd46100773d302d7aff60369f741 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Tue, 12 Apr 2022 10:50:28 +0200 Subject: [PATCH 06/22] feat: update Problem to account for related information and code This PR makes changes to the existing `xsbti.Problem` to account for an optional diagnostic code that the compiler can return for a given diagnostic and also related information. Given a piece of code like: ```scala try {} ``` You'll receive the following: ``` -- [E002] Syntax Warning: /Users/ckipp/Documents/scala-workspace/dotty-error-index/examples/002_EmptyCatchAndFinallyBlockID.scala:3:2 3 | try {} | ^^^^^^ | A try without catch or finally is equivalent to putting | its body in a block; no exceptions are handled. ``` The `E002` here is the actual code. Right now there would be no description. Some diagnostics have multiple positions that they need to represent. You can see an example of this [here](lampepfl/dotty#14002) in Dotty with the use of inlining. Instead of needing to rely on including all of that information in the diagnostic message it can now be extracted out into a `DiagnosticRelatedInformation`. These changes reference the conversation in #6868 --- .../src/main/java/xsbti/DiagnosticCode.java | 23 +++++++++++++++++++ .../xsbti/DiagnosticRelatedInformation.java | 20 ++++++++++++++++ .../src/main/java/xsbti/Problem.java | 22 ++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 internal/util-interface/src/main/java/xsbti/DiagnosticCode.java create mode 100644 internal/util-interface/src/main/java/xsbti/DiagnosticRelatedInformation.java diff --git a/internal/util-interface/src/main/java/xsbti/DiagnosticCode.java b/internal/util-interface/src/main/java/xsbti/DiagnosticCode.java new file mode 100644 index 000000000..6633e2b4e --- /dev/null +++ b/internal/util-interface/src/main/java/xsbti/DiagnosticCode.java @@ -0,0 +1,23 @@ +/* + * sbt + * Copyright 2011 - 2018, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package xsbti; + +import java.util.Optional; + +/** + * A DiagnosticCode is a unique identifier that the compiler can associate with a diagnostic. This + * is useful for tools to be able to quickly identify what diagnostic is being reported without + * having to rely on parsing the actual diagnostic message, which might not be stable. + */ +public interface DiagnosticCode { + /** The unique code. This is typically in the format of E000 */ + String code(); + + /** Possible explanation to explain the meaning of the code */ + Optional explanation(); +} diff --git a/internal/util-interface/src/main/java/xsbti/DiagnosticRelatedInformation.java b/internal/util-interface/src/main/java/xsbti/DiagnosticRelatedInformation.java new file mode 100644 index 000000000..786cecc52 --- /dev/null +++ b/internal/util-interface/src/main/java/xsbti/DiagnosticRelatedInformation.java @@ -0,0 +1,20 @@ +/* + * sbt + * Copyright 2011 - 2018, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package xsbti; + +/** + * Related information for a given diagnostic. At times this can be another place in your code + * contributing to the diagnostic or just relevant code relating to the diagnostic. + */ +public interface DiagnosticRelatedInformation { + /** Position of the related information */ + Position position(); + + /** Message indicating why this related information is attached to the diagnostic. */ + String message(); +} diff --git a/internal/util-interface/src/main/java/xsbti/Problem.java b/internal/util-interface/src/main/java/xsbti/Problem.java index 78c9145a3..e63a95b64 100644 --- a/internal/util-interface/src/main/java/xsbti/Problem.java +++ b/internal/util-interface/src/main/java/xsbti/Problem.java @@ -7,6 +7,8 @@ package xsbti; +import java.util.Collections; +import java.util.List; import java.util.Optional; public interface Problem { @@ -26,4 +28,24 @@ public interface Problem { default Optional rendered() { return Optional.empty(); } + + /** + * The unique code attached to the diagnostic being reported. + * + *

NOTE: To avoid breaking compatibility we provide a default to account for older Scala + * versions that do not have codes. + */ + default Optional diagnosticCode() { + return Optional.empty(); + } + + /** + * The possible releated information for the diagnostic being reported. + * + *

NOTE: To avoid breaking compatibility we provide a default to account for older Scala + * versions that do not have the concept of "related information". + */ + default List diagnosticRelatedInforamation() { + return Collections.emptyList(); + } } From 85efc87879d596cd519d56bc3e5b1bce3ca4f858 Mon Sep 17 00:00:00 2001 From: Kamil Podsiadlo Date: Thu, 31 Mar 2022 12:28:15 +0200 Subject: [PATCH 07/22] feat: generate JVM environment requests --- .../sbt/internal/bsp/JvmEnvironmentItem.scala | 48 +++++++++++++++++++ .../bsp/JvmRunEnvironmentParams.scala | 40 ++++++++++++++++ .../bsp/JvmRunEnvironmentResult.scala | 40 ++++++++++++++++ .../bsp/JvmTestEnvironmentParams.scala | 40 ++++++++++++++++ .../bsp/JvmTestEnvironmentResult.scala | 40 ++++++++++++++++ .../sbt/internal/bsp/codec/JsonProtocol.scala | 5 ++ .../bsp/codec/JvmEnvironmentItemFormats.scala | 35 ++++++++++++++ .../JvmRunEnvironmentParamsFormats.scala | 29 +++++++++++ .../JvmRunEnvironmentResultFormats.scala | 29 +++++++++++ .../JvmTestEnvironmentParamsFormats.scala | 29 +++++++++++ .../JvmTestEnvironmentResultFormats.scala | 29 +++++++++++ protocol/src/main/contraband/bsp.contra | 31 ++++++++++++ 12 files changed, 395 insertions(+) create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/JvmEnvironmentItem.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/JvmRunEnvironmentParams.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/JvmRunEnvironmentResult.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/JvmTestEnvironmentParams.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/JvmTestEnvironmentResult.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmEnvironmentItemFormats.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmRunEnvironmentParamsFormats.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmRunEnvironmentResultFormats.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmTestEnvironmentParamsFormats.scala create mode 100644 protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmTestEnvironmentResultFormats.scala diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/JvmEnvironmentItem.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/JvmEnvironmentItem.scala new file mode 100644 index 000000000..6e3d6b895 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/JvmEnvironmentItem.scala @@ -0,0 +1,48 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp +final class JvmEnvironmentItem private ( + val target: sbt.internal.bsp.BuildTargetIdentifier, + val classpath: Vector[java.net.URI], + val jvmOptions: Vector[String], + val workingDirectory: String, + val environmentVariables: scala.collection.immutable.Map[String, String]) extends Serializable { + + + + override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { + case x: JvmEnvironmentItem => (this.target == x.target) && (this.classpath == x.classpath) && (this.jvmOptions == x.jvmOptions) && (this.workingDirectory == x.workingDirectory) && (this.environmentVariables == x.environmentVariables) + case _ => false + }) + override def hashCode: Int = { + 37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.JvmEnvironmentItem".##) + target.##) + classpath.##) + jvmOptions.##) + workingDirectory.##) + environmentVariables.##) + } + override def toString: String = { + "JvmEnvironmentItem(" + target + ", " + classpath + ", " + jvmOptions + ", " + workingDirectory + ", " + environmentVariables + ")" + } + private[this] def copy(target: sbt.internal.bsp.BuildTargetIdentifier = target, classpath: Vector[java.net.URI] = classpath, jvmOptions: Vector[String] = jvmOptions, workingDirectory: String = workingDirectory, environmentVariables: scala.collection.immutable.Map[String, String] = environmentVariables): JvmEnvironmentItem = { + new JvmEnvironmentItem(target, classpath, jvmOptions, workingDirectory, environmentVariables) + } + def withTarget(target: sbt.internal.bsp.BuildTargetIdentifier): JvmEnvironmentItem = { + copy(target = target) + } + def withClasspath(classpath: Vector[java.net.URI]): JvmEnvironmentItem = { + copy(classpath = classpath) + } + def withJvmOptions(jvmOptions: Vector[String]): JvmEnvironmentItem = { + copy(jvmOptions = jvmOptions) + } + def withWorkingDirectory(workingDirectory: String): JvmEnvironmentItem = { + copy(workingDirectory = workingDirectory) + } + def withEnvironmentVariables(environmentVariables: scala.collection.immutable.Map[String, String]): JvmEnvironmentItem = { + copy(environmentVariables = environmentVariables) + } +} +object JvmEnvironmentItem { + + def apply(target: sbt.internal.bsp.BuildTargetIdentifier, classpath: Vector[java.net.URI], jvmOptions: Vector[String], workingDirectory: String, environmentVariables: scala.collection.immutable.Map[String, String]): JvmEnvironmentItem = new JvmEnvironmentItem(target, classpath, jvmOptions, workingDirectory, environmentVariables) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/JvmRunEnvironmentParams.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/JvmRunEnvironmentParams.scala new file mode 100644 index 000000000..f96b6a26e --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/JvmRunEnvironmentParams.scala @@ -0,0 +1,40 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp +final class JvmRunEnvironmentParams private ( + val targets: Vector[sbt.internal.bsp.BuildTargetIdentifier], + val originId: Option[String]) extends Serializable { + + + + override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { + case x: JvmRunEnvironmentParams => (this.targets == x.targets) && (this.originId == x.originId) + case _ => false + }) + override def hashCode: Int = { + 37 * (37 * (37 * (17 + "sbt.internal.bsp.JvmRunEnvironmentParams".##) + targets.##) + originId.##) + } + override def toString: String = { + "JvmRunEnvironmentParams(" + targets + ", " + originId + ")" + } + private[this] def copy(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier] = targets, originId: Option[String] = originId): JvmRunEnvironmentParams = { + new JvmRunEnvironmentParams(targets, originId) + } + def withTargets(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]): JvmRunEnvironmentParams = { + copy(targets = targets) + } + def withOriginId(originId: Option[String]): JvmRunEnvironmentParams = { + copy(originId = originId) + } + def withOriginId(originId: String): JvmRunEnvironmentParams = { + copy(originId = Option(originId)) + } +} +object JvmRunEnvironmentParams { + + def apply(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier], originId: Option[String]): JvmRunEnvironmentParams = new JvmRunEnvironmentParams(targets, originId) + def apply(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier], originId: String): JvmRunEnvironmentParams = new JvmRunEnvironmentParams(targets, Option(originId)) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/JvmRunEnvironmentResult.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/JvmRunEnvironmentResult.scala new file mode 100644 index 000000000..47ebb14ea --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/JvmRunEnvironmentResult.scala @@ -0,0 +1,40 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp +final class JvmRunEnvironmentResult private ( + val items: Vector[sbt.internal.bsp.JvmEnvironmentItem], + val originId: Option[String]) extends Serializable { + + + + override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { + case x: JvmRunEnvironmentResult => (this.items == x.items) && (this.originId == x.originId) + case _ => false + }) + override def hashCode: Int = { + 37 * (37 * (37 * (17 + "sbt.internal.bsp.JvmRunEnvironmentResult".##) + items.##) + originId.##) + } + override def toString: String = { + "JvmRunEnvironmentResult(" + items + ", " + originId + ")" + } + private[this] def copy(items: Vector[sbt.internal.bsp.JvmEnvironmentItem] = items, originId: Option[String] = originId): JvmRunEnvironmentResult = { + new JvmRunEnvironmentResult(items, originId) + } + def withItems(items: Vector[sbt.internal.bsp.JvmEnvironmentItem]): JvmRunEnvironmentResult = { + copy(items = items) + } + def withOriginId(originId: Option[String]): JvmRunEnvironmentResult = { + copy(originId = originId) + } + def withOriginId(originId: String): JvmRunEnvironmentResult = { + copy(originId = Option(originId)) + } +} +object JvmRunEnvironmentResult { + + def apply(items: Vector[sbt.internal.bsp.JvmEnvironmentItem], originId: Option[String]): JvmRunEnvironmentResult = new JvmRunEnvironmentResult(items, originId) + def apply(items: Vector[sbt.internal.bsp.JvmEnvironmentItem], originId: String): JvmRunEnvironmentResult = new JvmRunEnvironmentResult(items, Option(originId)) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/JvmTestEnvironmentParams.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/JvmTestEnvironmentParams.scala new file mode 100644 index 000000000..dd49a1474 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/JvmTestEnvironmentParams.scala @@ -0,0 +1,40 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp +final class JvmTestEnvironmentParams private ( + val targets: Vector[sbt.internal.bsp.BuildTargetIdentifier], + val originId: Option[String]) extends Serializable { + + + + override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { + case x: JvmTestEnvironmentParams => (this.targets == x.targets) && (this.originId == x.originId) + case _ => false + }) + override def hashCode: Int = { + 37 * (37 * (37 * (17 + "sbt.internal.bsp.JvmTestEnvironmentParams".##) + targets.##) + originId.##) + } + override def toString: String = { + "JvmTestEnvironmentParams(" + targets + ", " + originId + ")" + } + private[this] def copy(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier] = targets, originId: Option[String] = originId): JvmTestEnvironmentParams = { + new JvmTestEnvironmentParams(targets, originId) + } + def withTargets(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]): JvmTestEnvironmentParams = { + copy(targets = targets) + } + def withOriginId(originId: Option[String]): JvmTestEnvironmentParams = { + copy(originId = originId) + } + def withOriginId(originId: String): JvmTestEnvironmentParams = { + copy(originId = Option(originId)) + } +} +object JvmTestEnvironmentParams { + + def apply(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier], originId: Option[String]): JvmTestEnvironmentParams = new JvmTestEnvironmentParams(targets, originId) + def apply(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier], originId: String): JvmTestEnvironmentParams = new JvmTestEnvironmentParams(targets, Option(originId)) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/JvmTestEnvironmentResult.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/JvmTestEnvironmentResult.scala new file mode 100644 index 000000000..54fd64253 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/JvmTestEnvironmentResult.scala @@ -0,0 +1,40 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp +final class JvmTestEnvironmentResult private ( + val items: Vector[sbt.internal.bsp.JvmEnvironmentItem], + val originId: Option[String]) extends Serializable { + + + + override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { + case x: JvmTestEnvironmentResult => (this.items == x.items) && (this.originId == x.originId) + case _ => false + }) + override def hashCode: Int = { + 37 * (37 * (37 * (17 + "sbt.internal.bsp.JvmTestEnvironmentResult".##) + items.##) + originId.##) + } + override def toString: String = { + "JvmTestEnvironmentResult(" + items + ", " + originId + ")" + } + private[this] def copy(items: Vector[sbt.internal.bsp.JvmEnvironmentItem] = items, originId: Option[String] = originId): JvmTestEnvironmentResult = { + new JvmTestEnvironmentResult(items, originId) + } + def withItems(items: Vector[sbt.internal.bsp.JvmEnvironmentItem]): JvmTestEnvironmentResult = { + copy(items = items) + } + def withOriginId(originId: Option[String]): JvmTestEnvironmentResult = { + copy(originId = originId) + } + def withOriginId(originId: String): JvmTestEnvironmentResult = { + copy(originId = Option(originId)) + } +} +object JvmTestEnvironmentResult { + + def apply(items: Vector[sbt.internal.bsp.JvmEnvironmentItem], originId: Option[String]): JvmTestEnvironmentResult = new JvmTestEnvironmentResult(items, originId) + def apply(items: Vector[sbt.internal.bsp.JvmEnvironmentItem], originId: String): JvmTestEnvironmentResult = new JvmTestEnvironmentResult(items, Option(originId)) +} 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 c55580da2..7d26ae037 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 @@ -63,4 +63,9 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol with sbt.internal.bsp.codec.ResourcesParamsFormats with sbt.internal.bsp.codec.ResourcesItemFormats with sbt.internal.bsp.codec.ResourcesResultFormats + with sbt.internal.bsp.codec.JvmEnvironmentItemFormats + with sbt.internal.bsp.codec.JvmTestEnvironmentParamsFormats + with sbt.internal.bsp.codec.JvmTestEnvironmentResultFormats + with sbt.internal.bsp.codec.JvmRunEnvironmentParamsFormats + with sbt.internal.bsp.codec.JvmRunEnvironmentResultFormats object JsonProtocol extends JsonProtocol \ No newline at end of file diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmEnvironmentItemFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmEnvironmentItemFormats.scala new file mode 100644 index 000000000..c1ecd6ee1 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmEnvironmentItemFormats.scala @@ -0,0 +1,35 @@ +/** + * 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 JvmEnvironmentItemFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val JvmEnvironmentItemFormat: JsonFormat[sbt.internal.bsp.JvmEnvironmentItem] = new JsonFormat[sbt.internal.bsp.JvmEnvironmentItem] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.JvmEnvironmentItem = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val target = unbuilder.readField[sbt.internal.bsp.BuildTargetIdentifier]("target") + val classpath = unbuilder.readField[Vector[java.net.URI]]("classpath") + val jvmOptions = unbuilder.readField[Vector[String]]("jvmOptions") + val workingDirectory = unbuilder.readField[String]("workingDirectory") + val environmentVariables = unbuilder.readField[scala.collection.immutable.Map[String, String]]("environmentVariables") + unbuilder.endObject() + sbt.internal.bsp.JvmEnvironmentItem(target, classpath, jvmOptions, workingDirectory, environmentVariables) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.JvmEnvironmentItem, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("target", obj.target) + builder.addField("classpath", obj.classpath) + builder.addField("jvmOptions", obj.jvmOptions) + builder.addField("workingDirectory", obj.workingDirectory) + builder.addField("environmentVariables", obj.environmentVariables) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmRunEnvironmentParamsFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmRunEnvironmentParamsFormats.scala new file mode 100644 index 000000000..b1c5ce3e4 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmRunEnvironmentParamsFormats.scala @@ -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 JvmRunEnvironmentParamsFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val JvmRunEnvironmentParamsFormat: JsonFormat[sbt.internal.bsp.JvmRunEnvironmentParams] = new JsonFormat[sbt.internal.bsp.JvmRunEnvironmentParams] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.JvmRunEnvironmentParams = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val targets = unbuilder.readField[Vector[sbt.internal.bsp.BuildTargetIdentifier]]("targets") + val originId = unbuilder.readField[Option[String]]("originId") + unbuilder.endObject() + sbt.internal.bsp.JvmRunEnvironmentParams(targets, originId) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.JvmRunEnvironmentParams, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("targets", obj.targets) + builder.addField("originId", obj.originId) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmRunEnvironmentResultFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmRunEnvironmentResultFormats.scala new file mode 100644 index 000000000..6a9d40a5e --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmRunEnvironmentResultFormats.scala @@ -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 JvmRunEnvironmentResultFormats { self: sbt.internal.bsp.codec.JvmEnvironmentItemFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val JvmRunEnvironmentResultFormat: JsonFormat[sbt.internal.bsp.JvmRunEnvironmentResult] = new JsonFormat[sbt.internal.bsp.JvmRunEnvironmentResult] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.JvmRunEnvironmentResult = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val items = unbuilder.readField[Vector[sbt.internal.bsp.JvmEnvironmentItem]]("items") + val originId = unbuilder.readField[Option[String]]("originId") + unbuilder.endObject() + sbt.internal.bsp.JvmRunEnvironmentResult(items, originId) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.JvmRunEnvironmentResult, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("items", obj.items) + builder.addField("originId", obj.originId) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmTestEnvironmentParamsFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmTestEnvironmentParamsFormats.scala new file mode 100644 index 000000000..b02e289f2 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmTestEnvironmentParamsFormats.scala @@ -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 JvmTestEnvironmentParamsFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val JvmTestEnvironmentParamsFormat: JsonFormat[sbt.internal.bsp.JvmTestEnvironmentParams] = new JsonFormat[sbt.internal.bsp.JvmTestEnvironmentParams] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.JvmTestEnvironmentParams = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val targets = unbuilder.readField[Vector[sbt.internal.bsp.BuildTargetIdentifier]]("targets") + val originId = unbuilder.readField[Option[String]]("originId") + unbuilder.endObject() + sbt.internal.bsp.JvmTestEnvironmentParams(targets, originId) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.JvmTestEnvironmentParams, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("targets", obj.targets) + builder.addField("originId", obj.originId) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmTestEnvironmentResultFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmTestEnvironmentResultFormats.scala new file mode 100644 index 000000000..cd9c9d297 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmTestEnvironmentResultFormats.scala @@ -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 JvmTestEnvironmentResultFormats { self: sbt.internal.bsp.codec.JvmEnvironmentItemFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val JvmTestEnvironmentResultFormat: JsonFormat[sbt.internal.bsp.JvmTestEnvironmentResult] = new JsonFormat[sbt.internal.bsp.JvmTestEnvironmentResult] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.JvmTestEnvironmentResult = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val items = unbuilder.readField[Vector[sbt.internal.bsp.JvmEnvironmentItem]]("items") + val originId = unbuilder.readField[Option[String]]("originId") + unbuilder.endObject() + sbt.internal.bsp.JvmTestEnvironmentResult(items, originId) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.JvmTestEnvironmentResult, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("items", obj.items) + builder.addField("originId", obj.originId) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband/bsp.contra b/protocol/src/main/contraband/bsp.contra index 62438cff0..bc20a52e9 100644 --- a/protocol/src/main/contraband/bsp.contra +++ b/protocol/src/main/contraband/bsp.contra @@ -696,3 +696,34 @@ type ResourcesItem { ## List of resource files. resources: [java.net.URI] } + + +# JVM Environment requests + +type JvmEnvironmentItem { + target: sbt.internal.bsp.BuildTargetIdentifier! + classpath: [java.net.URI]! + jvmOptions: [String]! + workingDirectory: String! + environmentVariables: StringStringMap! +} + +type JvmTestEnvironmentParams { + targets: [sbt.internal.bsp.BuildTargetIdentifier]! + originId: String +} + +type JvmTestEnvironmentResult{ + items: [sbt.internal.bsp.JvmEnvironmentItem]! + originId: String +} + +type JvmRunEnvironmentParams { + targets: [sbt.internal.bsp.BuildTargetIdentifier]! + originId: String +} + +type JvmRunEnvironmentResult{ + items: [sbt.internal.bsp.JvmEnvironmentItem]! + originId: String +} \ No newline at end of file From ad4113caebc7e8486443d0e06ea10be2c80faba6 Mon Sep 17 00:00:00 2001 From: Kamil Podsiadlo Date: Thu, 31 Mar 2022 23:26:41 +0200 Subject: [PATCH 08/22] feat: implement BSP's JVM environment requests --- main/src/main/scala/sbt/Keys.scala | 6 ++ .../internal/server/BuildServerProtocol.scala | 67 +++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index a4fee0b85..68332cd13 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -398,6 +398,7 @@ object Keys { val usePipelining = settingKey[Boolean]("Use subproject pipelining for compilation.").withRank(BSetting) val exportPipelining = settingKey[Boolean]("Product early output so downstream subprojects can do pipelining.").withRank(BSetting) + // BSP keys val bspConfig = taskKey[Unit]("Create or update the BSP connection files").withRank(DSetting) val bspEnabled = SettingKey[Boolean](BasicKeys.bspEnabled) val bspSbtEnabled = settingKey[Boolean]("Should BSP export meta-targets for the SBT build itself?") @@ -420,6 +421,11 @@ object Keys { val bspBuildTargetCleanCache = inputKey[Unit]("Corresponds to buildTarget/cleanCache request").withRank(DTask) val bspBuildTargetScalacOptions = inputKey[Unit]("").withRank(DTask) val bspBuildTargetScalacOptionsItem = taskKey[ScalacOptionsItem]("").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 bspBuildTargetJvmEnvironmentItem = taskKey[JvmEnvironmentItem]("Computes JVM environment item").withRank(DTask) + val bspScalaTestClasses = inputKey[Unit]("Corresponds to buildTarget/scalaTestClasses request").withRank(DTask) val bspScalaTestClassesItem = taskKey[Seq[ScalaTestClassesItem]]("").withRank(DTask) val bspScalaMainClasses = inputKey[Unit]("Corresponds to buildTarget/scalaMainClasses request").withRank(DTask) diff --git a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala index 1cfae2054..78ffa74f5 100644 --- a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala @@ -315,6 +315,31 @@ object BuildServerProtocol { bspBuildTargetCompileItem := bspCompileTask.value, bspBuildTargetRun := bspRunTask.evaluated, bspBuildTargetScalacOptionsItem := scalacOptionsTask.value, + bspBuildTargetJVMRunEnvironment := Def.inputTaskDyn { + val s = state.value + val targets = spaceDelimited().parsed.map(uri => BuildTargetIdentifier(URI.create(uri))) + val workspace = bspFullWorkspace.value.filter(targets) + val filter = ScopeFilter.in(workspace.scopes.values.toList) + Def.task { + val items = bspBuildTargetJvmEnvironmentItem.result.all(filter).value + val successfulItems = anyOrThrow(items) + val result = JvmRunEnvironmentResult(successfulItems.toVector, None) + s.respondEvent(result) + } + }.evaluated, + bspBuildTargetJVMTestEnvironment := Def.inputTaskDyn { + val s = state.value + val targets = spaceDelimited().parsed.map(uri => BuildTargetIdentifier(URI.create(uri))) + val workspace = bspFullWorkspace.value.filter(targets) + val filter = ScopeFilter.in(workspace.scopes.values.toList) + Def.task { + val items = bspBuildTargetJvmEnvironmentItem.result.all(filter).value + val successfulItems = anyOrThrow(items) + val result = JvmTestEnvironmentResult(successfulItems.toVector, None) + s.respondEvent(result) + } + }.evaluated, + bspBuildTargetJvmEnvironmentItem := jvmEnvironmentItem().value, bspInternalDependencyConfigurations := internalDependencyConfigurationsSetting.value, bspScalaTestClassesItem := scalaTestClassesTask.value, bspScalaMainClassesItem := scalaMainClassesTask.value, @@ -344,6 +369,8 @@ object BuildServerProtocol { final val Run = "buildTarget/run" final val CleanCache = "buildTarget/cleanCache" final val ScalacOptions = "buildTarget/scalacOptions" + final val JvmRunEnvironment = "buildTarget/jvmRunEnvironment" + final val JvmTestEnvironment = "buildTarget/jvmTestEnvironment" final val ScalaTestClasses = "buildTarget/scalaTestClasses" final val ScalaMainClasses = "buildTarget/scalaMainClasses" final val Exit = "build/exit" @@ -443,6 +470,18 @@ object BuildServerProtocol { val command = Keys.bspBuildTargetScalacOptions.key val _ = callback.appendExec(s"$command $targets", Some(r.id)) + case r if r.method == Method.JvmRunEnvironment => + val param = Converter.fromJson[JvmRunEnvironmentParams](json(r)).get + val targets = param.targets.map(_.uri).mkString(" ") + val command = Keys.bspBuildTargetJVMRunEnvironment.key + val _ = callback.appendExec(s"$command $targets", Some(r.id)) + + case r if r.method == Method.JvmTestEnvironment => + val param = Converter.fromJson[JvmTestEnvironmentParams](json(r)).get + val targets = param.targets.map(_.uri).mkString(" ") + val command = Keys.bspBuildTargetJVMTestEnvironment.key + val _ = callback.appendExec(s"$command $targets", Some(r.id)) + case r if r.method == Method.ScalaTestClasses => val param = Converter.fromJson[ScalaTestClassesParams](json(r)).get val targets = param.targets.map(_.uri).mkString(" ") @@ -648,6 +687,33 @@ object BuildServerProtocol { ) } + private def jvmEnvironmentItem(): Initialize[Task[JvmEnvironmentItem]] = Def.taskDyn { + val target = Keys.bspTargetIdentifier.value + val baseDir = Keys.baseDirectory.value.toURI().toString() + val jvmOptions = Keys.javaOptions.value.toVector + val env = envVars.value + 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) + + JvmEnvironmentItem( + target, + classpath.map(_.toURI).toVector, + jvmOptions, + baseDir, + env + ) + } + } + private def scalacOptionsTask: Def.Initialize[Task[ScalacOptionsItem]] = Def.taskDyn { val target = Keys.bspTargetIdentifier.value val scalacOptions = Keys.scalacOptions.value @@ -878,6 +944,7 @@ object BuildServerProtocol { } } + // here private def scalaMainClassesTask: Initialize[Task[ScalaMainClassesItem]] = Def.task { val jvmOptions = Keys.javaOptions.value.toVector val mainClasses = Keys.discoveredMainClasses.value.map( From 6eb911ad15aa3c21d633fd2be079baaf78e57069 Mon Sep 17 00:00:00 2001 From: Kamil Podsiadlo Date: Tue, 5 Apr 2022 12:20:40 +0200 Subject: [PATCH 09/22] refactor: simplify JVm environment requests refactor: extract common logic to the `bspInputTask` --- .../internal/server/BuildServerProtocol.scala | 146 +++++++----------- 1 file changed, 54 insertions(+), 92 deletions(-) diff --git a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala index 78ffa74f5..2c7989ce2 100644 --- a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala @@ -119,11 +119,7 @@ object BuildServerProtocol { } }.value, // https://github.com/build-server-protocol/build-server-protocol/blob/master/docs/specification.md#build-target-sources-request - bspBuildTargetSources := Def.inputTaskDyn { - val s = state.value - val targets = spaceDelimited().parsed.map(uri => BuildTargetIdentifier(URI.create(uri))) - val workspace = bspFullWorkspace.value.filter(targets) - val filter = ScopeFilter.in(workspace.scopes.values.toList) + bspBuildTargetSources := bspInputTask { (state, _, workspace, filter) => // run the worker task concurrently Def.task { val items = bspBuildTargetSourcesItem.result.all(filter).value @@ -147,64 +143,48 @@ object BuildServerProtocol { } val successfulItems = anyOrThrow(items ++ buildItems) val result = SourcesResult(successfulItems.toVector) - s.respondEvent(result) + state.respondEvent(result) } }.evaluated, bspBuildTargetSources / aggregate := false, - bspBuildTargetResources := Def.inputTaskDyn { - val s = state.value - val targets = spaceDelimited().parsed.map(uri => BuildTargetIdentifier(URI.create(uri))) - val workspace = bspFullWorkspace.value.filter(targets) - workspace.warnIfBuildsNonEmpty(Method.Resources, s.log) - val filter = ScopeFilter.in(workspace.scopes.values.toList) + bspBuildTargetResources := bspInputTask { (state, _, workspace, filter) => + workspace.warnIfBuildsNonEmpty(Method.Resources, state.log) // run the worker task concurrently Def.task { val items = bspBuildTargetResourcesItem.result.all(filter).value val successfulItems = anyOrThrow(items) val result = ResourcesResult(successfulItems.toVector) - s.respondEvent(result) + state.respondEvent(result) } }.evaluated, bspBuildTargetResources / aggregate := false, - bspBuildTargetDependencySources := Def.inputTaskDyn { - val s = state.value - val targets = spaceDelimited().parsed.map(uri => BuildTargetIdentifier(URI.create(uri))) - val workspace = bspFullWorkspace.value.filter(targets) - val filter = ScopeFilter.in(workspace.scopes.values.toList) + bspBuildTargetDependencySources := bspInputTask { (state, _, workspace, filter) => // run the worker task concurrently Def.task { import sbt.internal.bsp.codec.JsonProtocol._ val items = bspBuildTargetDependencySourcesItem.result.all(filter).value val successfulItems = anyOrThrow(items) val result = DependencySourcesResult(successfulItems.toVector) - s.respondEvent(result) + state.respondEvent(result) } }.evaluated, bspBuildTargetDependencySources / aggregate := false, - bspBuildTargetCompile := 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.Compile, s.log) - val filter = ScopeFilter.in(workspace.scopes.values.toList) + bspBuildTargetCompile := bspInputTask { (state, _, workspace, filter) => + workspace.warnIfBuildsNonEmpty(Method.Compile, state.log) Def.task { val statusCodes = Keys.bspBuildTargetCompileItem.result.all(filter).value val aggregatedStatusCode = allOrThrow(statusCodes) match { case Seq() => StatusCode.Success case codes => codes.max } - s.respondEvent(BspCompileResult(None, aggregatedStatusCode)) + state.respondEvent(BspCompileResult(None, aggregatedStatusCode)) } }.evaluated, 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) + bspBuildTargetCleanCache := bspInputTask { (state, targets, workspace, filter) => + workspace.warnIfBuildsNonEmpty(Method.CleanCache, state.log) Def.task { val results = Keys.clean.result.all(filter).value val successes = anyOrThrow(results).size @@ -214,18 +194,12 @@ object BuildServerProtocol { // 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)) + state.respondEvent(CleanCacheResult(None, cleaned)) } }.evaluated, bspBuildTargetCleanCache / aggregate := false, - bspBuildTargetScalacOptions := Def.inputTaskDyn { - val s = state.value - - val targets = spaceDelimited().parsed.map(uri => BuildTargetIdentifier(URI.create(uri))) - val workspace = bspFullWorkspace.value.filter(targets) + bspBuildTargetScalacOptions := bspInputTask { (state, _, workspace, filter) => val builds = workspace.builds - - val filter = ScopeFilter.in(workspace.scopes.values.toList) Def.task { val items = bspBuildTargetScalacOptionsItem.result.all(filter).value val appProvider = appConfiguration.value.provider() @@ -246,34 +220,26 @@ object BuildServerProtocol { } val successfulItems = anyOrThrow(items ++ buildItems) val result = ScalacOptionsResult(successfulItems.toVector) - s.respondEvent(result) + state.respondEvent(result) } }.evaluated, bspBuildTargetScalacOptions / aggregate := false, - bspScalaTestClasses := Def.inputTaskDyn { - val s = state.value - val targets = spaceDelimited().parsed.map(uri => BuildTargetIdentifier(URI.create(uri))) - val workspace = bspFullWorkspace.value.filter(targets) - workspace.warnIfBuildsNonEmpty(Method.ScalaTestClasses, s.log) - val filter = ScopeFilter.in(workspace.scopes.values.toList) + bspScalaTestClasses := bspInputTask { (state, _, workspace, filter) => + workspace.warnIfBuildsNonEmpty(Method.ScalaTestClasses, state.log) Def.task { val items = bspScalaTestClassesItem.result.all(filter).value val successfulItems = anyOrThrow(items).flatten.toVector val result = ScalaTestClassesResult(successfulItems.toVector, None) - s.respondEvent(result) + state.respondEvent(result) } }.evaluated, - bspScalaMainClasses := Def.inputTaskDyn { - val s = state.value - val targets = spaceDelimited().parsed.map(uri => BuildTargetIdentifier(URI.create(uri))) - val workspace = bspFullWorkspace.value.filter(targets) - workspace.warnIfBuildsNonEmpty(Method.ScalaMainClasses, s.log) - val filter = ScopeFilter.in(workspace.scopes.values.toList) + bspScalaMainClasses := bspInputTask { (state, _, workspace, filter) => + workspace.warnIfBuildsNonEmpty(Method.ScalaMainClasses, state.log) Def.task { val items = bspScalaMainClassesItem.result.all(filter).value val successfulItems = anyOrThrow(items) val result = ScalaMainClassesResult(successfulItems.toVector, None) - s.respondEvent(result) + state.respondEvent(result) } }.evaluated, bspScalaMainClasses / aggregate := false @@ -315,28 +281,20 @@ object BuildServerProtocol { bspBuildTargetCompileItem := bspCompileTask.value, bspBuildTargetRun := bspRunTask.evaluated, bspBuildTargetScalacOptionsItem := scalacOptionsTask.value, - bspBuildTargetJVMRunEnvironment := Def.inputTaskDyn { - val s = state.value - val targets = spaceDelimited().parsed.map(uri => BuildTargetIdentifier(URI.create(uri))) - val workspace = bspFullWorkspace.value.filter(targets) - val filter = ScopeFilter.in(workspace.scopes.values.toList) + bspBuildTargetJVMRunEnvironment := bspInputTask { (state, _, _, filter) => Def.task { val items = bspBuildTargetJvmEnvironmentItem.result.all(filter).value val successfulItems = anyOrThrow(items) val result = JvmRunEnvironmentResult(successfulItems.toVector, None) - s.respondEvent(result) + state.respondEvent(result) } }.evaluated, - bspBuildTargetJVMTestEnvironment := Def.inputTaskDyn { - val s = state.value - val targets = spaceDelimited().parsed.map(uri => BuildTargetIdentifier(URI.create(uri))) - val workspace = bspFullWorkspace.value.filter(targets) - val filter = ScopeFilter.in(workspace.scopes.values.toList) + bspBuildTargetJVMTestEnvironment := bspInputTask { (state, _, _, filter) => Def.task { val items = bspBuildTargetJvmEnvironmentItem.result.all(filter).value val successfulItems = anyOrThrow(items) val result = JvmTestEnvironmentResult(successfulItems.toVector, None) - s.respondEvent(result) + state.respondEvent(result) } }.evaluated, bspBuildTargetJvmEnvironmentItem := jvmEnvironmentItem().value, @@ -687,31 +645,36 @@ object BuildServerProtocol { ) } - private def jvmEnvironmentItem(): Initialize[Task[JvmEnvironmentItem]] = Def.taskDyn { - val target = Keys.bspTargetIdentifier.value - val baseDir = Keys.baseDirectory.value.toURI().toString() - val jvmOptions = Keys.javaOptions.value.toVector - val env = envVars.value - 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) - - JvmEnvironmentItem( - target, - classpath.map(_.toURI).toVector, - jvmOptions, - baseDir, - env - ) + private def bspInputTask[T]( + taskImpl: ( + State, + Seq[BuildTargetIdentifier], + BspFullWorkspace, + ScopeFilter + ) => Def.Initialize[Task[T]] + ): Def.Initialize[InputTask[T]] = + Def.inputTaskDyn { + val s = state.value + val targets = spaceDelimited().parsed.map(uri => BuildTargetIdentifier(URI.create(uri))) + val workspace: BspFullWorkspace = bspFullWorkspace.value.filter(targets) + val filter = ScopeFilter.in(workspace.scopes.values.toList) + taskImpl(s, targets, workspace, filter) } + + private def jvmEnvironmentItem(): Initialize[Task[JvmEnvironmentItem]] = Def.task { + val target = Keys.bspTargetIdentifier.value + val classpath = Keys.fullClasspath.value.map(_.data.toURI).toVector + val jvmOptions = Keys.javaOptions.value.toVector + val baseDir = Keys.baseDirectory.value.toURI().toString() + val env = envVars.value + + JvmEnvironmentItem( + target, + classpath, + jvmOptions, + baseDir, + env + ) } private def scalacOptionsTask: Def.Initialize[Task[ScalacOptionsItem]] = Def.taskDyn { @@ -944,7 +907,6 @@ object BuildServerProtocol { } } - // here private def scalaMainClassesTask: Initialize[Task[ScalaMainClassesItem]] = Def.task { val jvmOptions = Keys.javaOptions.value.toVector val mainClasses = Keys.discoveredMainClasses.value.map( From f16412c3dd5df52cb53a6e3642894ce04673ded6 Mon Sep 17 00:00:00 2001 From: Kamil Podsiadlo Date: Tue, 5 Apr 2022 19:29:00 +0200 Subject: [PATCH 10/22] tests: run&test environment requests --- .../src/server-test/buildserver/build.sbt | 8 +++- .../test/scala/testpkg/BuildServerTest.scala | 45 ++++++++++++++++++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/server-test/src/server-test/buildserver/build.sbt b/server-test/src/server-test/buildserver/build.sbt index 52691a0a5..9d2de35fc 100644 --- a/server-test/src/server-test/buildserver/build.sbt +++ b/server-test/src/server-test/buildserver/build.sbt @@ -1,10 +1,16 @@ -ThisBuild / scalaVersion := "2.13.1" +ThisBuild / scalaVersion := "2.13.8" Global / serverLog / logLevel := Level.Debug lazy val runAndTest = project.in(file("run-and-test")) .settings( + libraryDependencies += "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.13.11", libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.8" % "test", + Compile / javaOptions := Vector("Xmx256M"), + Compile / envVars := Map("KEY" -> "VALUE"), + + Test / javaOptions := Vector("Xmx512M"), + Test / envVars := Map("KEY_TEST" -> "VALUE_TEST"), ) .dependsOn(util) diff --git a/server-test/src/test/scala/testpkg/BuildServerTest.scala b/server-test/src/test/scala/testpkg/BuildServerTest.scala index a6d900a16..817b0e6ad 100644 --- a/server-test/src/test/scala/testpkg/BuildServerTest.scala +++ b/server-test/src/test/scala/testpkg/BuildServerTest.scala @@ -157,7 +157,7 @@ object BuildServerTest extends AbstractServerTest { assert(processing("buildTarget/scalacOptions")) assert(svr.waitForString(10.seconds) { s => (s contains """"id":"40"""") && - (s contains "scala-library-2.13.1.jar") + (s contains "scala-library-2.13.8.jar") }) } @@ -311,6 +311,49 @@ object BuildServerTest extends AbstractServerTest { }) } + test("buildTarget/jvmRunEnvironment") { _ => + val buildTarget = buildTargetUri("runAndTest", "Compile") + svr.sendJsonRpc( + s"""|{ "jsonrpc": "2.0", + | "id": "97", + | "method": "buildTarget/jvmRunEnvironment", + | "params": { "targets": [{ "uri": "$buildTarget" }] } + |}""".stripMargin + ) + assert(processing("buildTarget/jvmRunEnvironment")) + assert { + svr.waitForString(10.seconds) { s => + (s contains """"id":"97"""") && + (s contains "jsoniter-scala-core_2.13-2.13.11.jar") && // compile dependency + (s contains "\"jvmOptions\":[\"Xmx256M\"]") && + (s contains "\"environmentVariables\":{\"KEY\":\"VALUE\"}") && + (s contains "/buildserver/run-and-test/") // working directory + } + } + } + + test("buildTarget/jvmTestEnvironment") { _ => + val buildTarget = buildTargetUri("runAndTest", "Test") + svr.sendJsonRpc( + s"""|{ "jsonrpc": "2.0", + | "id": "98", + | "method": "buildTarget/jvmTestEnvironment", + | "params": { "targets": [{ "uri": "$buildTarget" }] } + |}""".stripMargin + ) + assert(processing("buildTarget/jvmTestEnvironment")) + assert { + svr.waitForString(10.seconds) { s => + (s contains """"id":"98"""") && + // test depends on compile so it has dependencies from both + (s contains "jsoniter-scala-core_2.13-2.13.11.jar") && // compile dependency + (s contains "scalatest_2.13-3.0.8.jar") && // test dependency + (s contains "\"jvmOptions\":[\"Xmx512M\"]") && + (s contains "\"environmentVariables\":{\"KEY_TEST\":\"VALUE_TEST\"}") + } + } + } + test("buildTarget/scalaTestClasses") { _ => val buildTarget = buildTargetUri("runAndTest", "Test") val badBuildTarget = buildTargetUri("badBuildTarget", "Test") From faf8dfde72910b78562bf6aede03163bc7ce1261 Mon Sep 17 00:00:00 2001 From: Kamil Podsiadlo Date: Sat, 16 Apr 2022 13:46:12 +0200 Subject: [PATCH 11/22] bsp: add JVM test/run env capabilities to BSP --- .../internal/server/BuildServerProtocol.scala | 4 ++- .../bsp/BuildServerCapabilities.scala | 30 ++++++++++++++----- .../BuildServerCapabilitiesFormats.scala | 6 +++- protocol/src/main/contraband/bsp.contra | 9 ++++++ 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala index 2c7989ce2..f288d05a3 100644 --- a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala @@ -65,7 +65,9 @@ object BuildServerProtocol { RunProvider(BuildServerConnection.languages), dependencySourcesProvider = true, resourcesProvider = true, - canReload = true + canReload = true, + jvmRunEnvironmentProvider = true, + jvmTestEnvironmentProvider = true, ) private val bspReload = "bspReload" diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/BuildServerCapabilities.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/BuildServerCapabilities.scala index ccb10343c..82f88a090 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/BuildServerCapabilities.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/BuildServerCapabilities.scala @@ -17,22 +17,24 @@ final class BuildServerCapabilities private ( val runProvider: Option[sbt.internal.bsp.RunProvider], val dependencySourcesProvider: Option[Boolean], val resourcesProvider: Option[Boolean], - val canReload: Option[Boolean]) extends Serializable { + val canReload: Option[Boolean], + val jvmRunEnvironmentProvider: Option[Boolean], + val jvmTestEnvironmentProvider: Option[Boolean]) extends Serializable { override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { - case x: BuildServerCapabilities => (this.compileProvider == x.compileProvider) && (this.testProvider == x.testProvider) && (this.runProvider == x.runProvider) && (this.dependencySourcesProvider == x.dependencySourcesProvider) && (this.resourcesProvider == x.resourcesProvider) && (this.canReload == x.canReload) + case x: BuildServerCapabilities => (this.compileProvider == x.compileProvider) && (this.testProvider == x.testProvider) && (this.runProvider == x.runProvider) && (this.dependencySourcesProvider == x.dependencySourcesProvider) && (this.resourcesProvider == x.resourcesProvider) && (this.canReload == x.canReload) && (this.jvmRunEnvironmentProvider == x.jvmRunEnvironmentProvider) && (this.jvmTestEnvironmentProvider == x.jvmTestEnvironmentProvider) case _ => false }) override def hashCode: Int = { - 37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.BuildServerCapabilities".##) + compileProvider.##) + testProvider.##) + runProvider.##) + dependencySourcesProvider.##) + resourcesProvider.##) + canReload.##) + 37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.BuildServerCapabilities".##) + compileProvider.##) + testProvider.##) + runProvider.##) + dependencySourcesProvider.##) + resourcesProvider.##) + canReload.##) + jvmRunEnvironmentProvider.##) + jvmTestEnvironmentProvider.##) } override def toString: String = { - "BuildServerCapabilities(" + compileProvider + ", " + testProvider + ", " + runProvider + ", " + dependencySourcesProvider + ", " + resourcesProvider + ", " + canReload + ")" + "BuildServerCapabilities(" + compileProvider + ", " + testProvider + ", " + runProvider + ", " + dependencySourcesProvider + ", " + resourcesProvider + ", " + canReload + ", " + jvmRunEnvironmentProvider + ", " + jvmTestEnvironmentProvider + ")" } - private[this] def copy(compileProvider: Option[sbt.internal.bsp.CompileProvider] = compileProvider, testProvider: Option[sbt.internal.bsp.TestProvider] = testProvider, runProvider: Option[sbt.internal.bsp.RunProvider] = runProvider, dependencySourcesProvider: Option[Boolean] = dependencySourcesProvider, resourcesProvider: Option[Boolean] = resourcesProvider, canReload: Option[Boolean] = canReload): BuildServerCapabilities = { - new BuildServerCapabilities(compileProvider, testProvider, runProvider, dependencySourcesProvider, resourcesProvider, canReload) + private[this] def copy(compileProvider: Option[sbt.internal.bsp.CompileProvider] = compileProvider, testProvider: Option[sbt.internal.bsp.TestProvider] = testProvider, runProvider: Option[sbt.internal.bsp.RunProvider] = runProvider, dependencySourcesProvider: Option[Boolean] = dependencySourcesProvider, resourcesProvider: Option[Boolean] = resourcesProvider, canReload: Option[Boolean] = canReload, jvmRunEnvironmentProvider: Option[Boolean] = jvmRunEnvironmentProvider, jvmTestEnvironmentProvider: Option[Boolean] = jvmTestEnvironmentProvider): BuildServerCapabilities = { + new BuildServerCapabilities(compileProvider, testProvider, runProvider, dependencySourcesProvider, resourcesProvider, canReload, jvmRunEnvironmentProvider, jvmTestEnvironmentProvider) } def withCompileProvider(compileProvider: Option[sbt.internal.bsp.CompileProvider]): BuildServerCapabilities = { copy(compileProvider = compileProvider) @@ -70,9 +72,21 @@ final class BuildServerCapabilities private ( def withCanReload(canReload: Boolean): BuildServerCapabilities = { copy(canReload = Option(canReload)) } + def withJvmRunEnvironmentProvider(jvmRunEnvironmentProvider: Option[Boolean]): BuildServerCapabilities = { + copy(jvmRunEnvironmentProvider = jvmRunEnvironmentProvider) + } + def withJvmRunEnvironmentProvider(jvmRunEnvironmentProvider: Boolean): BuildServerCapabilities = { + copy(jvmRunEnvironmentProvider = Option(jvmRunEnvironmentProvider)) + } + def withJvmTestEnvironmentProvider(jvmTestEnvironmentProvider: Option[Boolean]): BuildServerCapabilities = { + copy(jvmTestEnvironmentProvider = jvmTestEnvironmentProvider) + } + def withJvmTestEnvironmentProvider(jvmTestEnvironmentProvider: Boolean): BuildServerCapabilities = { + copy(jvmTestEnvironmentProvider = Option(jvmTestEnvironmentProvider)) + } } object BuildServerCapabilities { - def apply(compileProvider: Option[sbt.internal.bsp.CompileProvider], testProvider: Option[sbt.internal.bsp.TestProvider], runProvider: Option[sbt.internal.bsp.RunProvider], dependencySourcesProvider: Option[Boolean], resourcesProvider: Option[Boolean], canReload: Option[Boolean]): BuildServerCapabilities = new BuildServerCapabilities(compileProvider, testProvider, runProvider, dependencySourcesProvider, resourcesProvider, canReload) - def apply(compileProvider: sbt.internal.bsp.CompileProvider, testProvider: sbt.internal.bsp.TestProvider, runProvider: sbt.internal.bsp.RunProvider, dependencySourcesProvider: Boolean, resourcesProvider: Boolean, canReload: Boolean): BuildServerCapabilities = new BuildServerCapabilities(Option(compileProvider), Option(testProvider), Option(runProvider), Option(dependencySourcesProvider), Option(resourcesProvider), Option(canReload)) + def apply(compileProvider: Option[sbt.internal.bsp.CompileProvider], testProvider: Option[sbt.internal.bsp.TestProvider], runProvider: Option[sbt.internal.bsp.RunProvider], dependencySourcesProvider: Option[Boolean], resourcesProvider: Option[Boolean], canReload: Option[Boolean], jvmRunEnvironmentProvider: Option[Boolean], jvmTestEnvironmentProvider: Option[Boolean]): BuildServerCapabilities = new BuildServerCapabilities(compileProvider, testProvider, runProvider, dependencySourcesProvider, resourcesProvider, canReload, jvmRunEnvironmentProvider, jvmTestEnvironmentProvider) + def apply(compileProvider: sbt.internal.bsp.CompileProvider, testProvider: sbt.internal.bsp.TestProvider, runProvider: sbt.internal.bsp.RunProvider, dependencySourcesProvider: Boolean, resourcesProvider: Boolean, canReload: Boolean, jvmRunEnvironmentProvider: Boolean, jvmTestEnvironmentProvider: Boolean): BuildServerCapabilities = new BuildServerCapabilities(Option(compileProvider), Option(testProvider), Option(runProvider), Option(dependencySourcesProvider), Option(resourcesProvider), Option(canReload), Option(jvmRunEnvironmentProvider), Option(jvmTestEnvironmentProvider)) } diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildServerCapabilitiesFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildServerCapabilitiesFormats.scala index 0947bd565..c23b3db80 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildServerCapabilitiesFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildServerCapabilitiesFormats.scala @@ -17,8 +17,10 @@ implicit lazy val BuildServerCapabilitiesFormat: JsonFormat[sbt.internal.bsp.Bui val dependencySourcesProvider = unbuilder.readField[Option[Boolean]]("dependencySourcesProvider") val resourcesProvider = unbuilder.readField[Option[Boolean]]("resourcesProvider") val canReload = unbuilder.readField[Option[Boolean]]("canReload") + val jvmRunEnvironmentProvider = unbuilder.readField[Option[Boolean]]("jvmRunEnvironmentProvider") + val jvmTestEnvironmentProvider = unbuilder.readField[Option[Boolean]]("jvmTestEnvironmentProvider") unbuilder.endObject() - sbt.internal.bsp.BuildServerCapabilities(compileProvider, testProvider, runProvider, dependencySourcesProvider, resourcesProvider, canReload) + sbt.internal.bsp.BuildServerCapabilities(compileProvider, testProvider, runProvider, dependencySourcesProvider, resourcesProvider, canReload, jvmRunEnvironmentProvider, jvmTestEnvironmentProvider) case None => deserializationError("Expected JsObject but found None") } @@ -31,6 +33,8 @@ implicit lazy val BuildServerCapabilitiesFormat: JsonFormat[sbt.internal.bsp.Bui builder.addField("dependencySourcesProvider", obj.dependencySourcesProvider) builder.addField("resourcesProvider", obj.resourcesProvider) builder.addField("canReload", obj.canReload) + builder.addField("jvmRunEnvironmentProvider", obj.jvmRunEnvironmentProvider) + builder.addField("jvmTestEnvironmentProvider", obj.jvmTestEnvironmentProvider) builder.endObject() } } diff --git a/protocol/src/main/contraband/bsp.contra b/protocol/src/main/contraband/bsp.contra index bc20a52e9..79734b10f 100644 --- a/protocol/src/main/contraband/bsp.contra +++ b/protocol/src/main/contraband/bsp.contra @@ -208,6 +208,15 @@ type BuildServerCapabilities { # The server sends notifications to the client on build # target change events via buildTarget/didChange # buildTargetChangedProvider: Boolean + + + # The JVM run/test environment request is sent from the client to the server + # in order to gather information required to launch a Java process. + # This is useful when the client wants to control the Java process execution, + # for example to enable custom Java agents or launch a custom main class during + # unit testing or debugging + jvmRunEnvironmentProvider: Boolean + jvmTestEnvironmentProvider: Boolean } type CompileProvider { From 64c7071ff291aef8259628b9cce151885aed7858 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Sat, 9 Apr 2022 15:12:20 -0700 Subject: [PATCH 12/22] Move on-termination test --- sbt-app/src/sbt-test/watch/{on-termination => on-error}/build.sbt | 0 .../watch/{on-termination => on-error}/project/Build.scala | 0 sbt-app/src/sbt-test/watch/{on-termination => on-error}/test | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename sbt-app/src/sbt-test/watch/{on-termination => on-error}/build.sbt (100%) rename sbt-app/src/sbt-test/watch/{on-termination => on-error}/project/Build.scala (100%) rename sbt-app/src/sbt-test/watch/{on-termination => on-error}/test (100%) diff --git a/sbt-app/src/sbt-test/watch/on-termination/build.sbt b/sbt-app/src/sbt-test/watch/on-error/build.sbt similarity index 100% rename from sbt-app/src/sbt-test/watch/on-termination/build.sbt rename to sbt-app/src/sbt-test/watch/on-error/build.sbt diff --git a/sbt-app/src/sbt-test/watch/on-termination/project/Build.scala b/sbt-app/src/sbt-test/watch/on-error/project/Build.scala similarity index 100% rename from sbt-app/src/sbt-test/watch/on-termination/project/Build.scala rename to sbt-app/src/sbt-test/watch/on-error/project/Build.scala diff --git a/sbt-app/src/sbt-test/watch/on-termination/test b/sbt-app/src/sbt-test/watch/on-error/test similarity index 100% rename from sbt-app/src/sbt-test/watch/on-termination/test rename to sbt-app/src/sbt-test/watch/on-error/test From ca7c872e27609bb54c0c6478b02fae2fab31f57d Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Sat, 9 Apr 2022 15:05:54 -0700 Subject: [PATCH 13/22] Restore watchOnTermination At some point the watchOnTermination callback stopped working. I'm not exactly sure how or why that happened but it is fairly straightforward to restore. The one tricky thing was that the callback has the signature (Watch.Action, _, _, _) => State, which requires propagating the action to the failWatch command. The easiest way to do this was to add a mutable field to the ContinuousState. This is rather ugly and reflects some poor design choices but a more comprehensive refactor is out of the scope of this fix. This commit adds a scripted test that ensures that the callback is invoked both in the successful and unsuccessful watch cases. In each case the callback deletes a file and we ensure that the file is indeed absent after the watch exits. --- .../main/scala/sbt/internal/Continuous.scala | 68 +++++++++++++------ .../src/sbt-test/watch/on-termination/bar.txt | 0 .../sbt-test/watch/on-termination/build.sbt | 15 ++++ .../src/sbt-test/watch/on-termination/foo.txt | 0 .../src/sbt-test/watch/on-termination/test | 8 +++ 5 files changed, 70 insertions(+), 21 deletions(-) create mode 100644 sbt-app/src/sbt-test/watch/on-termination/bar.txt create mode 100644 sbt-app/src/sbt-test/watch/on-termination/build.sbt create mode 100644 sbt-app/src/sbt-test/watch/on-termination/foo.txt create mode 100644 sbt-app/src/sbt-test/watch/on-termination/test diff --git a/main/src/main/scala/sbt/internal/Continuous.scala b/main/src/main/scala/sbt/internal/Continuous.scala index c206cb71e..68c2d4a87 100644 --- a/main/src/main/scala/sbt/internal/Continuous.scala +++ b/main/src/main/scala/sbt/internal/Continuous.scala @@ -1128,7 +1128,28 @@ private[sbt] object Continuous extends DeprecatedContinuous { val callbacks: Callbacks, val dynamicInputs: mutable.Set[DynamicInput], val pending: Boolean, + var failAction: Option[Watch.Action], ) { + def this( + count: Int, + commands: Seq[String], + beforeCommandImpl: (State, mutable.Set[DynamicInput]) => State, + afterCommand: State => State, + afterWatch: State => State, + callbacks: Callbacks, + dynamicInputs: mutable.Set[DynamicInput], + pending: Boolean, + ) = this( + count, + commands, + beforeCommandImpl, + afterCommand, + afterWatch, + callbacks, + dynamicInputs, + pending, + None + ) def beforeCommand(state: State): State = beforeCommandImpl(state, dynamicInputs) def incremented: ContinuousState = withCount(count + 1) def withPending(p: Boolean) = @@ -1323,7 +1344,8 @@ private[sbt] object ContinuousCommands { case Watch.Prompt => stop.map(_ :: s"$PromptChannel ${channel.name}" :: Nil mkString ";") case Watch.Run(commands) => stop.map(_ +: commands.map(_.commandLine).filter(_.nonEmpty) mkString "; ") - case Watch.HandleError(_) => + case a @ Watch.HandleError(_) => + cs.failAction = Some(a) stop.map(_ :: s"$failWatch ${channel.name}" :: Nil mkString "; ") case _ => stop } @@ -1353,27 +1375,31 @@ private[sbt] object ContinuousCommands { } cs.afterCommand(postState) } - private[sbt] val stopWatchCommand = watchCommand(stopWatch) { (channel, state) => - state.get(watchStates).flatMap(_.get(channel)) match { - case Some(cs) => - val afterWatchState = cs.afterWatch(state) - cs.callbacks.onExit() - StandardMain.exchange - .channelForName(channel) - .foreach { c => - c.terminal.setPrompt(Prompt.Pending) - c.unprompt(ConsoleUnpromptEvent(Some(CommandSource(channel)))) + private[this] val exitWatchShared = (error: Boolean) => + (channel: String, state: State) => + state.get(watchStates).flatMap(_.get(channel)) match { + case Some(cs) => + val afterWatchState = cs.afterWatch(state) + cs.callbacks.onExit() + StandardMain.exchange + .channelForName(channel) + .foreach { c => + c.terminal.setPrompt(Prompt.Pending) + c.unprompt(ConsoleUnpromptEvent(Some(CommandSource(channel)))) + } + val newState = afterWatchState.get(watchStates) match { + case None => afterWatchState + case Some(w) => afterWatchState.put(watchStates, w - channel) } - afterWatchState.get(watchStates) match { - case None => afterWatchState - case Some(w) => afterWatchState.put(watchStates, w - channel) - } - case _ => state - } - } - private[sbt] val failWatchCommand = watchCommand(failWatch) { (channel, state) => - state.fail - } + val commands = cs.commands.mkString("; ") + val count = cs.count + val action = cs.failAction.getOrElse(Watch.CancelWatch) + val st = cs.callbacks.onTermination(action, commands, count, newState) + if (error) st.fail else st + case _ => if (error) state.fail else state + } + private[sbt] val stopWatchCommand = watchCommand(stopWatch)(exitWatchShared(false)) + private[sbt] val failWatchCommand = watchCommand(failWatch)(exitWatchShared(true)) /* * Creates a FileTreeRepository where it is safe to call close without inadvertently cancelling * still active watches. diff --git a/sbt-app/src/sbt-test/watch/on-termination/bar.txt b/sbt-app/src/sbt-test/watch/on-termination/bar.txt new file mode 100644 index 000000000..e69de29bb diff --git a/sbt-app/src/sbt-test/watch/on-termination/build.sbt b/sbt-app/src/sbt-test/watch/on-termination/build.sbt new file mode 100644 index 000000000..798f18016 --- /dev/null +++ b/sbt-app/src/sbt-test/watch/on-termination/build.sbt @@ -0,0 +1,15 @@ +watchOnIteration := { (count, project, commands) => + Watch.CancelWatch +} +watchOnTermination := { (action, count, command, state) => + action match { + case Watch.CancelWatch => + java.nio.file.Files.delete(java.nio.file.Paths.get("foo.txt")) + case Watch.HandleError(e) => + if (e.getMessage == "fail") + java.nio.file.Files.delete(java.nio.file.Paths.get("bar.txt")) + else + throw new IllegalStateException("unexpected error") + } + state +} diff --git a/sbt-app/src/sbt-test/watch/on-termination/foo.txt b/sbt-app/src/sbt-test/watch/on-termination/foo.txt new file mode 100644 index 000000000..e69de29bb diff --git a/sbt-app/src/sbt-test/watch/on-termination/test b/sbt-app/src/sbt-test/watch/on-termination/test new file mode 100644 index 000000000..1634ee847 --- /dev/null +++ b/sbt-app/src/sbt-test/watch/on-termination/test @@ -0,0 +1,8 @@ +$ exists foo.txt +> ~compile +$ absent foo.txt +> set watchOnIteration := { (_, _, _) => new Watch.HandleError(new IllegalStateException("fail")) } +$ exists bar.txt +-> ~compile +$ absent bar.txt + From 45518c7f24a216afeb742bec2df135db0a2e0994 Mon Sep 17 00:00:00 2001 From: xuwei-k <6b656e6a69@gmail.com> Date: Sat, 16 Apr 2022 21:53:32 +0900 Subject: [PATCH 14/22] Update semanticdbVersion --- build.sbt | 2 +- main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index a33ac73ae..c2e3feb81 100644 --- a/build.sbt +++ b/build.sbt @@ -46,7 +46,7 @@ ThisBuild / resolvers += Resolver.mavenLocal Global / semanticdbEnabled := !(Global / insideCI).value // Change main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala too, if you change this. -Global / semanticdbVersion := "4.4.28" +Global / semanticdbVersion := "4.5.4" val excludeLint = SettingKey[Set[Def.KeyedInitialize[_]]]("excludeLintKeys") Global / excludeLint := (Global / excludeLint).?.value.getOrElse(Set.empty) Global / excludeLint += componentID diff --git a/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala b/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala index 76b9c8f1b..f8f2eebde 100644 --- a/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala +++ b/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala @@ -26,7 +26,7 @@ object SemanticdbPlugin extends AutoPlugin { semanticdbEnabled := SysProp.semanticdb, semanticdbIncludeInJar := false, semanticdbOptions := List(), - semanticdbVersion := "4.4.28" + semanticdbVersion := "4.5.4" ) override lazy val projectSettings: Seq[Def.Setting[_]] = Seq( From 20a9269e791747d01721e65b4829d0a22aef9e96 Mon Sep 17 00:00:00 2001 From: Philippus Date: Sun, 17 Apr 2022 12:59:12 +0200 Subject: [PATCH 15/22] Add SBTN_AUTO_COMPLETE environment variable --- .../src/main/scala/sbt/internal/client/NetworkClient.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala b/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala index 71a11e6fd..eb7fc322e 100644 --- a/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala +++ b/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala @@ -139,7 +139,7 @@ class NetworkClient( private val rebooting = new AtomicBoolean(false) private lazy val noTab = arguments.completionArguments.contains("--no-tab") private lazy val noStdErr = arguments.completionArguments.contains("--no-stderr") && - !sys.env.contains("SBTC_AUTO_COMPLETE") + !sys.env.contains("SBTN_AUTO_COMPLETE") && !sys.env.contains("SBTC_AUTO_COMPLETE") private def mkSocket(file: File): (Socket, Option[String]) = ClientSocket.socket(file, useJNI) From 11275f604cdfd435d7042badfd136b3ff1586e0a Mon Sep 17 00:00:00 2001 From: Philippus Date: Sun, 17 Apr 2022 12:56:06 +0200 Subject: [PATCH 16/22] Fix typos --- .../main/scala/sbt/internal/client/NetworkClient.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala b/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala index eb7fc322e..8909d6d42 100644 --- a/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala +++ b/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala @@ -67,7 +67,7 @@ trait ConsoleInterface { } /** - * A NetworkClient connects to a running an sbt instance or starts a + * A NetworkClient connects to a running sbt instance or starts a * new instance if there isn't already one running. Once connected, * it can send commands for sbt to run, it can send completions to sbt * and print the completions to stdout so that a shell can consume @@ -78,15 +78,15 @@ trait ConsoleInterface { * needs to start it. It also contains the sbt command * arguments to send to the server if any are present. * @param console a logging instance. This can use a ConsoleAppender or - * just simply print to a PrintSream. + * just simply print to a PrintStream. * @param inputStream the InputStream from which the client reads bytes. It * is not hardcoded to System.in so that a NetworkClient * can be remotely controlled by a java process, which - * is useful in test. + * is useful in testing. * @param errorStream the sink for messages that we always want to be printed. * It is usually System.err but could be overridden in tests * or set to a null OutputStream if the NetworkClient needs - * to be silent + * to be silent. * @param printStream the sink for standard out messages. It is typically * System.out but in the case of completions, the bytes written * to System.out are usually treated as completion results From 053834aea07a0bc89f88e0fdcce84a133b17340a Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sun, 17 Apr 2022 23:26:40 -0400 Subject: [PATCH 17/22] Zinc 1.7.0-M2 --- build.sbt | 2 +- project/Dependencies.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index c2e3feb81..f59529599 100644 --- a/build.sbt +++ b/build.sbt @@ -10,7 +10,7 @@ import scala.util.Try // ThisBuild settings take lower precedence, // but can be shared across the multi projects. ThisBuild / version := { - val v = "1.6.3-SNAPSHOT" + val v = "1.7.0-SNAPSHOT" nightlyVersion.getOrElse(v) } ThisBuild / version2_13 := "2.0.0-SNAPSHOT" diff --git a/project/Dependencies.scala b/project/Dependencies.scala index ae0d47987..535f2e3bb 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -15,7 +15,7 @@ object Dependencies { private val ioVersion = nightlyVersion.getOrElse("1.6.0") private val lmVersion = sys.props.get("sbt.build.lm.version").orElse(nightlyVersion).getOrElse("1.6.1") - val zincVersion = nightlyVersion.getOrElse("1.6.0") + val zincVersion = nightlyVersion.getOrElse("1.7.0-M2") private val sbtIO = "org.scala-sbt" %% "io" % ioVersion From 9565e33ea8efa1f81ff34cb85a3a46624ac797a2 Mon Sep 17 00:00:00 2001 From: "dmitrii.naumenko" Date: Mon, 23 May 2022 18:05:33 +0300 Subject: [PATCH 18/22] "oldshell" command should use `OldShell` on failure instead of `Shell` following up https://github.com/sbt/sbt/pull/3098 --- main-command/src/main/scala/sbt/BasicCommands.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main-command/src/main/scala/sbt/BasicCommands.scala b/main-command/src/main/scala/sbt/BasicCommands.scala index bb58bd301..06b7599f1 100644 --- a/main-command/src/main/scala/sbt/BasicCommands.scala +++ b/main-command/src/main/scala/sbt/BasicCommands.scala @@ -406,7 +406,7 @@ object BasicCommands { case Some(line) => val newState = s .copy( - onFailure = Some(Exec(Shell, None)), + onFailure = Some(Exec(OldShell, None)), remainingCommands = Exec(line, s.source) +: Exec(OldShell, None) +: s.remainingCommands ) .setInteractive(true) From 120014bd65b7b9476a50768987004d42e1a7eeaf Mon Sep 17 00:00:00 2001 From: Alonso Montero <31372753+AlonsoM45@users.noreply.github.com> Date: Thu, 26 May 2022 21:10:41 -0600 Subject: [PATCH 19/22] Use XDG_RUNTIME_DIR environment variable to choose the path used for the creation of sockets (#6887) The `XDG_RUNTIME_DIR` environment variable will be used to choose the base directory in which the sockets will be created by **sbt**. This can help when issues such as #6777 appear. There are other related issues, such as #6101, [Metals issue #2235](https://github.com/scalameta/metals/issues/2235), [Che issue #18394](https://github.com/eclipse/che/issues/18394). Those are closed issues, but there are some cases in which the solution does not work (such as the case in #6777). Furthermore, the solution given seems more like a workaround than an actual fix. **What causes this issue?** At least in my case, the **ServerAlreadyBootingException** is thrown after **sbt** tries to create a Unix domain socket and fails. In my specific environment, I was not able to create the sockets in some directories because they were mounted on an NFS. This prevented me from using any automated sbt command on any of those directories (Metals uses sbt under the hood, and it was not able to start because of the ServerAlreadyBootingException), which in turn resulted in me not being able to use Metals on a project that was created on any of those directories. **How is the issue solved in this PR?** If the `XDG_RUNTIME_DIR` environment variable is set, then sockets will be created in a folder with a hashed name, inside the directory `XDG_RUNTIME_DIR/.sbt/`. If this variable is not set, everything works exactly the same as always. Let's see an example: 1. My environment variable `XDG_RUNTIME_DIR` is set to /home/alonso/.runtime-dir` 2. I create my project in `/home/alonso/workspace/hello-world` 3. I run `sbt compile` 4. The socket is created in `/home/alonso/.runtime-dir/.sbt/sbt-socket102030405060/sbt-load.sock`. The number **102030405060** is a hash that is computed from the base directory of the project (it is actually the same hash that is produced in order to create sockets in Windows) and it will be different depending on the location of the project 5. The sbt command executed correctly, which would not have happened if the socket had been created in the `home/alonso/workspace` directory or any of its subdirectories (in this example, `/home/BlackDiamond/workspace` corresponds to a network file share) Co-authored-by: Alonso Montero --- .../java/sbt/internal/BootServerSocket.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/main-command/src/main/java/sbt/internal/BootServerSocket.java b/main-command/src/main/java/sbt/internal/BootServerSocket.java index beff97566..5cf91dc50 100644 --- a/main-command/src/main/java/sbt/internal/BootServerSocket.java +++ b/main-command/src/main/java/sbt/internal/BootServerSocket.java @@ -284,10 +284,11 @@ public class BootServerSocket implements AutoCloseable { public BootServerSocket(final AppConfiguration configuration) throws ServerAlreadyBootingException, IOException { final Path base = configuration.baseDirectory().toPath().toRealPath(); - final Path target = base.resolve("project").resolve("target"); if (!isWindows) { + final String actualSocketLocation = socketLocation(base); + final Path target = Paths.get(actualSocketLocation).getParent(); if (!Files.isDirectory(target)) Files.createDirectories(target); - socketFile = Paths.get(socketLocation(base)); + socketFile = Paths.get(actualSocketLocation); } else { socketFile = null; } @@ -301,13 +302,20 @@ public class BootServerSocket implements AutoCloseable { } } - public static String socketLocation(final Path base) throws UnsupportedEncodingException { + public static String socketLocation(final Path base) + throws UnsupportedEncodingException, IOException { final Path target = base.resolve("project").resolve("target"); + long hash = LongHashFunction.farmNa().hashBytes(target.toString().getBytes("UTF-8")); if (isWindows) { - long hash = LongHashFunction.farmNa().hashBytes(target.toString().getBytes("UTF-8")); return "sbt-load" + hash; } else { - return base.relativize(target.resolve("sbt-load.sock")).toString(); + final String alternativeSocketLocation = + System.getenv().getOrDefault("XDG_RUNTIME_DIR", "/tmp"); + final Path alternativeSocketLocationRoot = + Paths.get(alternativeSocketLocation).resolve(".sbt"); + final Path locationForSocket = alternativeSocketLocationRoot.resolve("sbt-socket" + hash); + final Path pathForSocket = locationForSocket.resolve("sbt-load.sock"); + return pathForSocket.toString(); } } From c04b2a4f732de848fec564b8c4ec694a156d11c8 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Mon, 13 Jun 2022 00:52:32 -0400 Subject: [PATCH 20/22] lm 1.7.0-M1 This removes OkHttp dependency. This also removes an experimental feature to customize HTTP for Ivy called CustomHttp we added in sbt 1.3.0. Since LM got switched to Coursier in 1.3.0, I don't think we advertized CustomHttp. --- build.sbt | 1 + main/src/main/scala/sbt/Defaults.scala | 10 +++----- main/src/main/scala/sbt/Opts.scala | 6 +++++ .../main/scala/sbt/internal/CustomHttp.scala | 25 ------------------- .../main/scala/sbt/internal/RemoteCache.scala | 2 +- project/Dependencies.scala | 2 +- 6 files changed, 13 insertions(+), 33 deletions(-) delete mode 100644 main/src/main/scala/sbt/internal/CustomHttp.scala diff --git a/build.sbt b/build.sbt index f59529599..c1fbace0a 100644 --- a/build.sbt +++ b/build.sbt @@ -1057,6 +1057,7 @@ lazy val mainProj = (project in file("main")) exclude[DirectMissingMethodProblem]("sbt.Defaults.earlyArtifactPathSetting"), exclude[MissingClassProblem]("sbt.internal.server.BuildServerReporter$"), exclude[IncompatibleTemplateDefProblem]("sbt.internal.server.BuildServerReporter"), + exclude[MissingClassProblem]("sbt.internal.CustomHttp*"), ) ) .configure( diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 82724a3b5..8f10c9dba 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -43,7 +43,7 @@ import sbt.internal.librarymanagement.mavenint.{ PomExtraDependencyAttributes, SbtPomExtraProperties } -import sbt.internal.librarymanagement.{ CustomHttp => _, _ } +import sbt.internal.librarymanagement._ import sbt.internal.nio.{ CheckBuildSources, Globs } import sbt.internal.server.{ BspCompileProgress, @@ -258,8 +258,6 @@ object Defaults extends BuildCommon { artifactClassifier :== None, checksums := Classpaths.bootChecksums(appConfiguration.value), conflictManager := ConflictManager.default, - CustomHttp.okhttpClientBuilder :== CustomHttp.defaultHttpClientBuilder, - CustomHttp.okhttpClient := CustomHttp.okhttpClientBuilder.value.build, pomExtra :== NodeSeq.Empty, pomPostProcess :== idFun, pomAllRepositories :== false, @@ -2705,7 +2703,7 @@ object Defaults extends BuildCommon { def dependencyResolutionTask: Def.Initialize[Task[DependencyResolution]] = Def.taskIf { if (useCoursier.value) CoursierDependencyResolution(csrConfiguration.value) - else IvyDependencyResolution(ivyConfiguration.value, CustomHttp.okhttpClient.value) + else IvyDependencyResolution(ivyConfiguration.value) } } @@ -3129,7 +3127,7 @@ object Classpaths { else None }, dependencyResolution := dependencyResolutionTask.value, - publisher := IvyPublisher(ivyConfiguration.value, CustomHttp.okhttpClient.value), + publisher := IvyPublisher(ivyConfiguration.value), ivyConfiguration := mkIvyConfiguration.value, ivyConfigurations := { val confs = thisProject.value.configurations @@ -3432,7 +3430,7 @@ object Classpaths { private[sbt] def ivySbt0: Initialize[Task[IvySbt]] = Def.task { Credentials.register(credentials.value, streams.value.log) - new IvySbt(ivyConfiguration.value, CustomHttp.okhttpClient.value) + new IvySbt(ivyConfiguration.value) } def moduleSettings0: Initialize[Task[ModuleSettings]] = Def.task { val deps = allDependencies.value.toVector diff --git a/main/src/main/scala/sbt/Opts.scala b/main/src/main/scala/sbt/Opts.scala index 720fd0f93..25afca7fd 100644 --- a/main/src/main/scala/sbt/Opts.scala +++ b/main/src/main/scala/sbt/Opts.scala @@ -41,8 +41,14 @@ object Opts { } object resolver { import sbt.io.syntax._ + @deprecated("Use sonatypeOssReleases instead", "1.7.0") val sonatypeReleases = Resolver.sonatypeRepo("releases") + val sonatypeOssReleases = Resolver.sonatypeOssRepos("releases") + + @deprecated("Use sonatypeOssSnapshots instead", "1.7.0") val sonatypeSnapshots = Resolver.sonatypeRepo("snapshots") + val sonatypeOssSnapshots = Resolver.sonatypeOssRepos("snapshots") + val sonatypeStaging = MavenRepository( "sonatype-staging", "https://oss.sonatype.org/service/local/staging/deploy/maven2" diff --git a/main/src/main/scala/sbt/internal/CustomHttp.scala b/main/src/main/scala/sbt/internal/CustomHttp.scala deleted file mode 100644 index bc9a12ae2..000000000 --- a/main/src/main/scala/sbt/internal/CustomHttp.scala +++ /dev/null @@ -1,25 +0,0 @@ -/* - * sbt - * Copyright 2011 - 2018, Lightbend, Inc. - * Copyright 2008 - 2010, Mark Harrah - * Licensed under Apache License 2.0 (see LICENSE) - */ - -package sbt.internal - -import sbt.internal.librarymanagement.{ CustomHttp => LMCustomHttp } -import okhttp3._ - -import sbt.BuildSyntax._ -import sbt.KeyRanks._ - -object CustomHttp { - val okhttpClientBuilder = - settingKey[OkHttpClient.Builder]("Builder for the HTTP client.").withRank(CSetting) - val okhttpClient = - settingKey[OkHttpClient]("HTTP client used for library management.").withRank(CSetting) - - def defaultHttpClientBuilder: OkHttpClient.Builder = { - LMCustomHttp.defaultHttpClientBuilder - } -} diff --git a/main/src/main/scala/sbt/internal/RemoteCache.scala b/main/src/main/scala/sbt/internal/RemoteCache.scala index 200117de7..58e7b4b9c 100644 --- a/main/src/main/scala/sbt/internal/RemoteCache.scala +++ b/main/src/main/scala/sbt/internal/RemoteCache.scala @@ -139,7 +139,7 @@ object RemoteCache { ivySbt := { Credentials.register(credentials.value, streams.value.log) val config0 = ivyConfiguration.value - new IvySbt(config0, sbt.internal.CustomHttp.okhttpClient.value) + new IvySbt(config0) }, ) ) ++ inTask(pullRemoteCache)( diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 535f2e3bb..b84554c9f 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -14,7 +14,7 @@ object Dependencies { // sbt modules private val ioVersion = nightlyVersion.getOrElse("1.6.0") private val lmVersion = - sys.props.get("sbt.build.lm.version").orElse(nightlyVersion).getOrElse("1.6.1") + sys.props.get("sbt.build.lm.version").orElse(nightlyVersion).getOrElse("1.7.0-M1") val zincVersion = nightlyVersion.getOrElse("1.7.0-M2") private val sbtIO = "org.scala-sbt" %% "io" % ioVersion From d5889d3ce36f21843f121ae817d2f5f3a710f4c7 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sun, 12 Jun 2022 23:25:36 -0400 Subject: [PATCH 21/22] Bump to Scala 2.12.16 --- .github/workflows/ci.yml | 4 ++-- build.sbt | 2 +- main/src/main/scala/sbt/PluginCross.scala | 2 +- main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala | 2 +- project/Dependencies.scala | 4 ++-- project/build.properties | 2 +- sbt-app/src/sbt-test/actions/cross-advanced/build.sbt | 2 +- sbt-app/src/sbt-test/actions/cross-advanced/test | 2 +- sbt-app/src/sbt-test/compiler-project/run-test/build.sbt | 2 +- .../sbt-test/dependency-graph/ignoreScalaLibrary/build.sbt | 2 +- sbt-app/src/sbt-test/dependency-graph/toFileSubTask/build.sbt | 2 +- sbt-app/src/sbt-test/project/sbt-plugin/build.sbt | 2 +- .../src/sbt-test/project/sbt-plugin/changes/oldSbtPlugin.sbt | 2 +- sbt-app/src/sbt-test/project/semanticdb/build.sbt | 2 +- sbt-app/src/sbt-test/project/unified/build.sbt | 2 +- sbt-app/src/sbt-test/source-dependencies/constants/test | 2 +- .../src/sbt-test/tests/scala-instance-classloader/build.sbt | 2 +- server-test/src/server-test/response/build.sbt | 2 +- 18 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad88219d2..06c63b5af 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,8 +49,8 @@ jobs: env: JAVA_OPTS: -Xms800M -Xmx2G -Xss6M -XX:ReservedCodeCacheSize=128M -server -Dsbt.io.virtual=false -Dfile.encoding=UTF-8 JVM_OPTS: -Xms800M -Xmx2G -Xss6M -XX:ReservedCodeCacheSize=128M -server -Dsbt.io.virtual=false -Dfile.encoding=UTF-8 - SCALA_212: 2.12.15 - SCALA_213: 2.13.6 + SCALA_212: 2.12.16 + SCALA_213: 2.13.8 SCALA_3: 3.1.0 UTIL_TESTS: "utilCache/test utilControl/test utilInterface/test utilLogging/test utilPosition/test utilRelation/test utilScripted/test utilTracking/test" SBT_LOCAL: false diff --git a/build.sbt b/build.sbt index c1fbace0a..3168ba1dd 100644 --- a/build.sbt +++ b/build.sbt @@ -46,7 +46,7 @@ ThisBuild / resolvers += Resolver.mavenLocal Global / semanticdbEnabled := !(Global / insideCI).value // Change main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala too, if you change this. -Global / semanticdbVersion := "4.5.4" +Global / semanticdbVersion := "4.5.9" val excludeLint = SettingKey[Set[Def.KeyedInitialize[_]]]("excludeLintKeys") Global / excludeLint := (Global / excludeLint).?.value.getOrElse(Set.empty) Global / excludeLint += componentID diff --git a/main/src/main/scala/sbt/PluginCross.scala b/main/src/main/scala/sbt/PluginCross.scala index 98d5d57e1..740709b98 100644 --- a/main/src/main/scala/sbt/PluginCross.scala +++ b/main/src/main/scala/sbt/PluginCross.scala @@ -99,7 +99,7 @@ private[sbt] object PluginCross { VersionNumber(sv) match { case VersionNumber(Seq(0, 12, _*), _, _) => "2.9.2" case VersionNumber(Seq(0, 13, _*), _, _) => "2.10.7" - case VersionNumber(Seq(1, 0, _*), _, _) => "2.12.15" + case VersionNumber(Seq(1, 0, _*), _, _) => "2.12.16" case _ => sys.error(s"Unsupported sbt binary version: $sv") } } diff --git a/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala b/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala index f8f2eebde..c1563c60d 100644 --- a/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala +++ b/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala @@ -26,7 +26,7 @@ object SemanticdbPlugin extends AutoPlugin { semanticdbEnabled := SysProp.semanticdb, semanticdbIncludeInJar := false, semanticdbOptions := List(), - semanticdbVersion := "4.5.4" + semanticdbVersion := "4.5.9" ) override lazy val projectSettings: Seq[Def.Setting[_]] = Seq( diff --git a/project/Dependencies.scala b/project/Dependencies.scala index b84554c9f..368646ed4 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -4,8 +4,8 @@ import sbt.contraband.ContrabandPlugin.autoImport._ object Dependencies { // WARNING: Please Scala update versions in PluginCross.scala too - val scala212 = "2.12.15" - val scala213 = "2.13.6" + val scala212 = "2.12.16" + val scala213 = "2.13.8" val checkPluginCross = settingKey[Unit]("Make sure scalaVersion match up") val baseScalaVersion = scala212 def nightlyVersion: Option[String] = diff --git a/project/build.properties b/project/build.properties index 77df8ac33..c8fcab543 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.5.4 \ No newline at end of file +sbt.version=1.6.2 diff --git a/sbt-app/src/sbt-test/actions/cross-advanced/build.sbt b/sbt-app/src/sbt-test/actions/cross-advanced/build.sbt index a77d7c0dd..91460f0e3 100644 --- a/sbt-app/src/sbt-test/actions/cross-advanced/build.sbt +++ b/sbt-app/src/sbt-test/actions/cross-advanced/build.sbt @@ -1,6 +1,6 @@ lazy val check = taskKey[Unit]("") lazy val compile2 = taskKey[Unit]("") -lazy val scala212 = "2.12.15" +lazy val scala212 = "2.12.16" lazy val root = (project in file(".")) .aggregate(foo, bar, client) diff --git a/sbt-app/src/sbt-test/actions/cross-advanced/test b/sbt-app/src/sbt-test/actions/cross-advanced/test index 5b3ab9f3b..5ee486310 100644 --- a/sbt-app/src/sbt-test/actions/cross-advanced/test +++ b/sbt-app/src/sbt-test/actions/cross-advanced/test @@ -17,7 +17,7 @@ ## test + with command or alias > clean ## for command cross building you do need crossScalaVerions on root -> set root/crossScalaVersions := Seq("2.12.15", "2.13.1") +> set root/crossScalaVersions := Seq("2.12.16", "2.13.1") > + build $ exists foo/target/scala-2.12 $ exists foo/target/scala-2.13 diff --git a/sbt-app/src/sbt-test/compiler-project/run-test/build.sbt b/sbt-app/src/sbt-test/compiler-project/run-test/build.sbt index 09ac3d406..f424c2d91 100644 --- a/sbt-app/src/sbt-test/compiler-project/run-test/build.sbt +++ b/sbt-app/src/sbt-test/compiler-project/run-test/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.15" +ThisBuild / scalaVersion := "2.12.16" libraryDependencies ++= Seq( "com.novocode" % "junit-interface" % "0.5" % Test, diff --git a/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/build.sbt b/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/build.sbt index 3e9fa7240..f67a2c7ee 100644 --- a/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/build.sbt +++ b/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.15" +ThisBuild / scalaVersion := "2.12.16" libraryDependencies ++= Seq( "org.slf4j" % "slf4j-api" % "1.7.2", diff --git a/sbt-app/src/sbt-test/dependency-graph/toFileSubTask/build.sbt b/sbt-app/src/sbt-test/dependency-graph/toFileSubTask/build.sbt index f830ec558..b8c0e1620 100644 --- a/sbt-app/src/sbt-test/dependency-graph/toFileSubTask/build.sbt +++ b/sbt-app/src/sbt-test/dependency-graph/toFileSubTask/build.sbt @@ -1,5 +1,5 @@ // ThisBuild / useCoursier := false -ThisBuild / scalaVersion := "2.12.15" +ThisBuild / scalaVersion := "2.12.16" ThisBuild / organization := "org.example" ThisBuild / version := "0.1" diff --git a/sbt-app/src/sbt-test/project/sbt-plugin/build.sbt b/sbt-app/src/sbt-test/project/sbt-plugin/build.sbt index bb26b40e0..1831f255c 100644 --- a/sbt-app/src/sbt-test/project/sbt-plugin/build.sbt +++ b/sbt-app/src/sbt-test/project/sbt-plugin/build.sbt @@ -1,6 +1,6 @@ lazy val root = project.in(file(".")) .enablePlugins(SbtPlugin) .settings( - scalaVersion := "2.12.15", + scalaVersion := "2.12.16", scalacOptions ++= Seq("-Xfatal-warnings", "-Xlint") ) diff --git a/sbt-app/src/sbt-test/project/sbt-plugin/changes/oldSbtPlugin.sbt b/sbt-app/src/sbt-test/project/sbt-plugin/changes/oldSbtPlugin.sbt index ce0acfbe0..03437f9da 100644 --- a/sbt-app/src/sbt-test/project/sbt-plugin/changes/oldSbtPlugin.sbt +++ b/sbt-app/src/sbt-test/project/sbt-plugin/changes/oldSbtPlugin.sbt @@ -1,6 +1,6 @@ lazy val root = project.in(file(".")) .settings( - scalaVersion := "2.12.15", + scalaVersion := "2.12.16", sbtPlugin := true, scalacOptions ++= Seq("-Xfatal-warnings", "-Xlint") ) diff --git a/sbt-app/src/sbt-test/project/semanticdb/build.sbt b/sbt-app/src/sbt-test/project/semanticdb/build.sbt index 3a76b7064..8c8a5b3d2 100644 --- a/sbt-app/src/sbt-test/project/semanticdb/build.sbt +++ b/sbt-app/src/sbt-test/project/semanticdb/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.15" +ThisBuild / scalaVersion := "2.12.16" ThisBuild / semanticdbEnabled := true ThisBuild / semanticdbIncludeInJar := true diff --git a/sbt-app/src/sbt-test/project/unified/build.sbt b/sbt-app/src/sbt-test/project/unified/build.sbt index 11d0eb84e..8210ad038 100644 --- a/sbt-app/src/sbt-test/project/unified/build.sbt +++ b/sbt-app/src/sbt-test/project/unified/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.15" +ThisBuild / scalaVersion := "2.12.16" import sbt.internal.CommandStrings.{ inspectBrief, inspectDetailed } import sbt.internal.Inspect diff --git a/sbt-app/src/sbt-test/source-dependencies/constants/test b/sbt-app/src/sbt-test/source-dependencies/constants/test index bc2cb09c9..b1e725f26 100644 --- a/sbt-app/src/sbt-test/source-dependencies/constants/test +++ b/sbt-app/src/sbt-test/source-dependencies/constants/test @@ -1,4 +1,4 @@ -> ++2.12.15! +> ++2.12.16! $ copy-file changes/B.scala B.scala diff --git a/sbt-app/src/sbt-test/tests/scala-instance-classloader/build.sbt b/sbt-app/src/sbt-test/tests/scala-instance-classloader/build.sbt index b099f2338..1f8431d46 100644 --- a/sbt-app/src/sbt-test/tests/scala-instance-classloader/build.sbt +++ b/sbt-app/src/sbt-test/tests/scala-instance-classloader/build.sbt @@ -3,7 +3,7 @@ import sbt.internal.inc.ScalaInstance lazy val OtherScala = config("other-scala").hide lazy val junitinterface = "com.novocode" % "junit-interface" % "0.11" lazy val akkaActor = "com.typesafe.akka" %% "akka-actor" % "2.5.17" -ThisBuild / scalaVersion := "2.12.15" +ThisBuild / scalaVersion := "2.12.16" lazy val root = (project in file(".")) .configs(OtherScala) diff --git a/server-test/src/server-test/response/build.sbt b/server-test/src/server-test/response/build.sbt index 1f18c7ab3..90f7f9f8d 100644 --- a/server-test/src/server-test/response/build.sbt +++ b/server-test/src/server-test/response/build.sbt @@ -1,6 +1,6 @@ import sbt.internal.server.{ ServerHandler, ServerIntent } -ThisBuild / scalaVersion := "2.12.15" +ThisBuild / scalaVersion := "2.12.16" Global / serverLog / logLevel := Level.Debug // custom handler From d9e43ecfdfac9ab2523803d77c7e07bd80a1d392 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sun, 12 Jun 2022 23:11:28 -0400 Subject: [PATCH 22/22] Revert "Add support for scala-output-version flag in Scala 3" This reverts commit 1e89d713111fc48c11a1a102b4d70e4df0374078. --- main/src/main/scala/sbt/Defaults.scala | 80 ++----------- main/src/main/scala/sbt/Keys.scala | 6 +- .../scala/sbt/internal/ClassLoaders.scala | 4 +- .../build.sbt | 30 ----- .../scala-output-version-redundant-flags/test | 3 - .../scala-output-version/Bar.scala | 5 - .../scala-output-version/build.sbt | 112 ------------------ .../foo/src/main/scala-2.13/Foo.scala | 11 -- .../foo/src/main/scala-3/Foo.scala | 17 --- .../foo/src/test/scala-2.13/FooTest.scala | 7 -- .../foo/src/test/scala-3/FooTest.scala | 19 --- .../scala-output-version/test | 35 ------ 12 files changed, 12 insertions(+), 317 deletions(-) delete mode 100644 sbt-app/src/sbt-test/dependency-management/scala-output-version-redundant-flags/build.sbt delete mode 100644 sbt-app/src/sbt-test/dependency-management/scala-output-version-redundant-flags/test delete mode 100644 sbt-app/src/sbt-test/dependency-management/scala-output-version/Bar.scala delete mode 100644 sbt-app/src/sbt-test/dependency-management/scala-output-version/build.sbt delete mode 100644 sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/main/scala-2.13/Foo.scala delete mode 100644 sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/main/scala-3/Foo.scala delete mode 100644 sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/test/scala-2.13/FooTest.scala delete mode 100644 sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/test/scala-3/FooTest.scala delete mode 100644 sbt-app/src/sbt-test/dependency-management/scala-output-version/test diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 8f10c9dba..6e4746a53 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -677,15 +677,6 @@ object Defaults extends BuildCommon { else topLoader }, scalaInstance := scalaInstanceTask.value, - runtimeScalaInstance := Def.taskDyn { - scalaOutputVersion.?.value - .map { version => - scalaInstanceTask0(version = version, isRuntimeInstance = true) - } - .getOrElse { - scalaInstance - } - }.value, crossVersion := (if (crossPaths.value) CrossVersion.binary else CrossVersion.disabled), pluginCrossBuild / sbtBinaryVersion := binarySbtVersion( (pluginCrossBuild / sbtVersion).value @@ -984,49 +975,6 @@ object Defaults extends BuildCommon { old ++ Seq("-Wconf:cat=unused-nowarn:s", "-Xsource:3") else old }, - scalacOptions := { - val old = scalacOptions.value - - // 3 possible sources of Scala output version - val fromCompiler = CrossVersion - .partialVersion(scalaVersion.value) - .map { case (major, minor) => s"${major}.${minor}" } - .getOrElse( - sys.error(s"Wrong value of `scalaVersion`: ${scalaVersion.value}") - ) - - val maybeFromSetting = scalaOutputVersion.?.value.map { version => - CrossVersion - .partialVersion(version) - .map { case (major, minor) => s"${major}.${minor}" } - .getOrElse( - sys.error(s"Wrong value of `scalaOutputVersion`: ${version}") - ) - } - - val maybeFromFlags = old.zipWithIndex.flatMap { - case (opt, idx) => - if (opt.startsWith("-scala-output-version:")) - Some(opt.stripPrefix("-scala-output-version:")) - else if (opt == "-scala-output-version" && idx + 1 < old.length) - Some(old(idx + 1)) - else None - }.lastOption - - // Add -scala-output-version flag when minor Scala versions are different - // unless the flag is already set properly - maybeFromSetting match { - case Some(fromSetting) if fromSetting != fromCompiler => - maybeFromFlags match { - case Some(fromFlags) if fromFlags == fromSetting => - old - case _ => - old ++ Seq("-scala-output-version", fromSetting) - } - case _ => - old - } - }, persistJarClasspath :== true, classpathEntryDefinesClassVF := { (if (persistJarClasspath.value) classpathDefinesClassCache.value @@ -1137,19 +1085,13 @@ object Defaults extends BuildCommon { } def scalaInstanceTask: Initialize[Task[ScalaInstance]] = Def.taskDyn { - scalaInstanceTask0(scalaVersion.value, false) - } - - private def scalaInstanceTask0( - version: String, - isRuntimeInstance: Boolean - ): Initialize[Task[ScalaInstance]] = Def.taskDyn { // if this logic changes, ensure that `unmanagedScalaInstanceOnly` and `update` are changed // appropriately to avoid cycles scalaHome.value match { case Some(h) => scalaInstanceFromHome(h) case None => val scalaProvider = appConfiguration.value.provider.scalaProvider + val version = scalaVersion.value if (version == scalaProvider.version) // use the same class loader as the Scala classes used by sbt Def.task { val allJars = scalaProvider.jars @@ -1167,7 +1109,7 @@ object Defaults extends BuildCommon { case _ => ScalaInstance(version, scalaProvider) } } else - scalaInstanceFromUpdate0(isRuntimeInstance) + scalaInstanceFromUpdate } } @@ -1188,29 +1130,22 @@ object Defaults extends BuildCommon { pre + post } - def scalaInstanceFromUpdate: Initialize[Task[ScalaInstance]] = scalaInstanceFromUpdate0(false) - - private def scalaInstanceFromUpdate0( - isRuntimeInstance: Boolean - ): Initialize[Task[ScalaInstance]] = Def.task { + def scalaInstanceFromUpdate: Initialize[Task[ScalaInstance]] = Def.task { val sv = scalaVersion.value val fullReport = update.value val toolReport = fullReport .configuration(Configurations.ScalaTool) .getOrElse(sys.error(noToolConfiguration(managedScalaInstance.value))) - lazy val runtimeReport = fullReport.configuration(Configurations.Runtime).get - val libraryReport = if (isRuntimeInstance) runtimeReport else toolReport - def libraryFile(id: String): File = { + def file(id: String): File = { val files = for { - m <- libraryReport.modules if m.module.name.startsWith(id) + m <- toolReport.modules if m.module.name.startsWith(id) (art, file) <- m.artifacts if art.`type` == Artifact.DefaultType } yield file files.headOption getOrElse sys.error(s"Missing $id jar file") } - val libraryJars = ScalaArtifacts.libraryIds(sv).map(libraryFile) val allCompilerJars = toolReport.modules.flatMap(_.artifacts.map(_._2)) val allDocJars = fullReport @@ -1218,6 +1153,7 @@ object Defaults extends BuildCommon { .toSeq .flatMap(_.modules) .flatMap(_.artifacts.map(_._2)) + val libraryJars = ScalaArtifacts.libraryIds(sv).map(file) makeScalaInstance( sv, @@ -2096,7 +2032,7 @@ object Defaults extends BuildCommon { def runnerInit: Initialize[Task[ScalaRun]] = Def.task { val tmp = taskTemporaryDirectory.value val resolvedScope = resolvedScoped.value.scope - val si = runtimeScalaInstance.value + val si = scalaInstance.value val s = streams.value val opts = forkOptions.value val options = javaOptions.value @@ -3323,7 +3259,7 @@ object Classpaths { autoScalaLibrary.value && scalaHome.value.isEmpty && managedScalaInstance.value, sbtPlugin.value, scalaOrganization.value, - scalaOutputVersion.?.value.getOrElse(scalaVersion.value) + scalaVersion.value ), // Override the default to handle mixing in the sbtPlugin + scala dependencies. allDependencies := { diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 68332cd13..392812271 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -186,11 +186,9 @@ object Keys { val compileOptions = taskKey[CompileOptions]("Collects basic options to configure compilers").withRank(DTask) val compileInputs = taskKey[Inputs]("Collects all inputs needed for compilation.").withRank(DTask) val scalaHome = settingKey[Option[File]]("If Some, defines the local Scala installation to use for compilation, running, and testing.").withRank(ASetting) - val scalaInstance = taskKey[ScalaInstance]("Defines the Scala instance to use for compilation").withRank(DTask) - val runtimeScalaInstance = taskKey[ScalaInstance]("Defines the Scala instance used at runtime (also for tests).").withRank(DTask) + val scalaInstance = taskKey[ScalaInstance]("Defines the Scala instance to use for compilation, running, and testing.").withRank(DTask) val scalaOrganization = settingKey[String]("Organization/group ID of the Scala used in the project. Default value is 'org.scala-lang'. This is an advanced setting used for clones of the Scala Language. It should be disregarded in standard use cases.").withRank(CSetting) - val scalaVersion = settingKey[String]("The version of Scala compiler used for building this project.").withRank(APlusSetting) - val scalaOutputVersion = settingKey[String]("The version of Scala standard library used for running this project and declared as its transitive dependency.").withRank(APlusSetting) + val scalaVersion = settingKey[String]("The version of Scala used for building.").withRank(APlusSetting) val scalaBinaryVersion = settingKey[String]("The Scala version substring describing binary compatibility.").withRank(BPlusSetting) val crossScalaVersions = settingKey[Seq[String]]("The versions of Scala used when cross-building.").withRank(BPlusSetting) val crossVersion = settingKey[CrossVersion]("Configures handling of the Scala version when cross-building.").withRank(CSetting) diff --git a/main/src/main/scala/sbt/internal/ClassLoaders.scala b/main/src/main/scala/sbt/internal/ClassLoaders.scala index 0275ae9dd..1c5df8aa0 100644 --- a/main/src/main/scala/sbt/internal/ClassLoaders.scala +++ b/main/src/main/scala/sbt/internal/ClassLoaders.scala @@ -35,7 +35,7 @@ private[sbt] object ClassLoaders { * Get the class loader for a test task. The configuration could be IntegrationTest or Test. */ private[sbt] def testTask: Def.Initialize[Task[ClassLoader]] = Def.task { - val si = runtimeScalaInstance.value + val si = scalaInstance.value val cp = fullClasspath.value.map(_.data) val dependencyStamps = modifiedTimes((dependencyClasspathFiles / outputFileStamps).value).toMap def getLm(f: File): Long = dependencyStamps.getOrElse(f, IO.getModifiedTimeOrZero(f)) @@ -64,7 +64,7 @@ private[sbt] object ClassLoaders { private[sbt] def runner: Def.Initialize[Task[ScalaRun]] = Def.taskDyn { val resolvedScope = resolvedScoped.value.scope - val instance = runtimeScalaInstance.value + val instance = scalaInstance.value val s = streams.value val opts = forkOptions.value val options = javaOptions.value diff --git a/sbt-app/src/sbt-test/dependency-management/scala-output-version-redundant-flags/build.sbt b/sbt-app/src/sbt-test/dependency-management/scala-output-version-redundant-flags/build.sbt deleted file mode 100644 index e1bc21ae3..000000000 --- a/sbt-app/src/sbt-test/dependency-management/scala-output-version-redundant-flags/build.sbt +++ /dev/null @@ -1,30 +0,0 @@ -val checkOptions = taskKey[Unit]("") - -lazy val p1 = project - .settings( - scalaVersion := "3.0.2", - checkOptions := { - assert((Compile / scalacOptions).value == Seq()) - assert((Test / scalacOptions).value == Seq()) - } - ) - -lazy val p2 = project - .settings( - scalaVersion := "3.0.2", - scalaOutputVersion := "3.0.2", - checkOptions := { - assert((Compile / scalacOptions).value == Seq()) - assert((Test / scalacOptions).value == Seq()) - } - ) - -lazy val p3 = project - .settings( - scalaVersion := "3.1.2-RC2", - scalaOutputVersion := "3.0.2", - checkOptions := { - assert((Compile / scalacOptions).value == Seq("-scala-output-version", "3.0")) - assert((Test / scalacOptions).value == Seq("-scala-output-version", "3.0")) - } - ) diff --git a/sbt-app/src/sbt-test/dependency-management/scala-output-version-redundant-flags/test b/sbt-app/src/sbt-test/dependency-management/scala-output-version-redundant-flags/test deleted file mode 100644 index 500721a31..000000000 --- a/sbt-app/src/sbt-test/dependency-management/scala-output-version-redundant-flags/test +++ /dev/null @@ -1,3 +0,0 @@ -> p1 / checkOptions -> p2 / checkOptions -> p3 / checkOptions \ No newline at end of file diff --git a/sbt-app/src/sbt-test/dependency-management/scala-output-version/Bar.scala b/sbt-app/src/sbt-test/dependency-management/scala-output-version/Bar.scala deleted file mode 100644 index 772478e95..000000000 --- a/sbt-app/src/sbt-test/dependency-management/scala-output-version/Bar.scala +++ /dev/null @@ -1,5 +0,0 @@ -object Bar { - def main(args: Array[String]) = { - assert(foo.main.Foo.numbers == Seq(1, 2, 3)) - } -} diff --git a/sbt-app/src/sbt-test/dependency-management/scala-output-version/build.sbt b/sbt-app/src/sbt-test/dependency-management/scala-output-version/build.sbt deleted file mode 100644 index 78ad544f4..000000000 --- a/sbt-app/src/sbt-test/dependency-management/scala-output-version/build.sbt +++ /dev/null @@ -1,112 +0,0 @@ -// cases 1, 2, 3: check for scala version in bar -// case a: check locally published Ivy dependency -// case b: check locally published Maven dependency -// case c: check unpublished sibling module dependency - -val org = "org.example" -val fooName = "sbt-test-scala-output-version-foo" -val revision = "0.0.1-SNAPSHOT" - -ThisBuild / organization := org -ThisBuild / version := revision - -lazy val foo = project.in(file("foo")) - .settings( - name := fooName, - scalaVersion := "3.1.2-RC2", - crossScalaVersions := List("2.13.8", "3.1.2-RC2"), - scalaOutputVersion := "3.0.2", - scalaOutputVersion := { - CrossVersion.partialVersion(scalaVersion.value) match { - case Some((3, _)) => "3.0.2" - case _ => scalaVersion.value - } - }, - libraryDependencies ++= Seq( - "org.scalameta" %% "munit" % "0.7.29" % Test - ), - TaskKey[Unit]("checkIvy") := { - val ivyFile = makeIvyXml.value - val ivyContent = IO.read(ivyFile) - val expectedContent = """""" - val hasCorrectStdlib = ivyContent.contains(expectedContent) - if (!hasCorrectStdlib) sys.error(s"The produced Ivy file is incorrect:\n\n${ivyContent}") - }, - TaskKey[Unit]("checkPom") := { - val pomFile = makePom.value - val pomContent = IO.read(pomFile) - val flatPom = pomContent.filterNot(_.isWhitespace) - val expectedContent = "org.scala-langscala3-library_33.0.2" - val hasCorrectStdlib = flatPom.contains(expectedContent) - if (!hasCorrectStdlib) sys.error(s"The produced POM file is incorrect:\n\n${pomContent}") - } - ) - -val scala3_1 = Seq(scalaVersion := "3.1.1") -val scala3_0 = Seq(scalaVersion := "3.0.2") -val scala2_13 = Seq(scalaVersion := "2.13.8") -val ivyFooDep = Seq( - libraryDependencies ++= Seq( - org %% fooName % revision - ), - resolvers := Seq(Resolver.defaultLocal) -) -val mavenFooDep = Seq( - libraryDependencies ++= Seq( - org %% fooName % revision - ), - resolvers := Seq(Resolver.mavenLocal) -) - -lazy val bar1a = project.in(file("bar1a")) - .settings( - scala3_1, - ivyFooDep - ) - -lazy val bar1b = project.in(file("bar1b")) - .settings( - scala3_1, - mavenFooDep - ) - -lazy val bar1c = project.in(file("bar1c")) - .settings( - scala3_1, - ).dependsOn(foo) - - -lazy val bar2a = project.in(file("bar2a")) - .settings( - scala3_0, - ivyFooDep - ) - -lazy val bar2b = project.in(file("bar2b")) - .settings( - scala3_0, - mavenFooDep - ) - -lazy val bar2c = project.in(file("bar2c")) - .settings( - scala3_0, - ).dependsOn(foo) - - -lazy val bar3a = project.in(file("bar3a")) - .settings( - scala2_13, - ivyFooDep - ) - -lazy val bar3b = project.in(file("bar3b")) - .settings( - scala2_13, - mavenFooDep - ) - -lazy val bar3c = project.in(file("bar3c")) - .settings( - scala2_13, - ).dependsOn(foo) diff --git a/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/main/scala-2.13/Foo.scala b/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/main/scala-2.13/Foo.scala deleted file mode 100644 index 6347cd083..000000000 --- a/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/main/scala-2.13/Foo.scala +++ /dev/null @@ -1,11 +0,0 @@ -package foo.main - -object Foo { - val numbers = Seq(1, 2, 3) -} - -object Run { - def main(args: Array[String]) = { - assert(Foo.numbers.length == 3) - } -} diff --git a/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/main/scala-3/Foo.scala b/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/main/scala-3/Foo.scala deleted file mode 100644 index 11ec280a3..000000000 --- a/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/main/scala-3/Foo.scala +++ /dev/null @@ -1,17 +0,0 @@ -package foo.main - -class MyException extends Exception("MyException") - -@annotation.experimental -object Exceptional: - import language.experimental.saferExceptions - def foo(): Unit throws MyException = // this requires at least 3.1.x to compile - throw new MyException - -object Foo: - val numbers = Seq(1, 2, 3) - -@main def run() = - val canEqualMethods = classOf[CanEqual.type].getMethods.toList - assert( canEqualMethods.exists(_.getName == "canEqualSeq")) // since 3.0.x - assert(!canEqualMethods.exists(_.getName == "canEqualSeqs")) // since 3.1.x diff --git a/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/test/scala-2.13/FooTest.scala b/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/test/scala-2.13/FooTest.scala deleted file mode 100644 index 4102861ae..000000000 --- a/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/test/scala-2.13/FooTest.scala +++ /dev/null @@ -1,7 +0,0 @@ -package foo.test - -class FooTest extends munit.FunSuite { - test("foo") { - assertEquals(foo.main.Foo.numbers, Seq(1, 2, 3)) - } -} diff --git a/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/test/scala-3/FooTest.scala b/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/test/scala-3/FooTest.scala deleted file mode 100644 index cc0ee76b6..000000000 --- a/sbt-app/src/sbt-test/dependency-management/scala-output-version/foo/src/test/scala-3/FooTest.scala +++ /dev/null @@ -1,19 +0,0 @@ -package foo.test - -class MyException extends Exception("MyException") - -@annotation.experimental -object Exceptional: - import language.experimental.saferExceptions - def foo(): Unit throws MyException = // this requires at least 3.1.x to compile - throw new MyException - - -class FooTest extends munit.FunSuite: - test("foo") { - assertEquals(foo.main.Foo.numbers, Seq(1, 2, 3)) - - val canEqualMethods = classOf[CanEqual.type].getMethods.toList - assert( canEqualMethods.exists(_.getName == "canEqualSeq")) // since 3.0.x - assert(!canEqualMethods.exists(_.getName == "canEqualSeqs")) // since 3.1.x - } diff --git a/sbt-app/src/sbt-test/dependency-management/scala-output-version/test b/sbt-app/src/sbt-test/dependency-management/scala-output-version/test deleted file mode 100644 index ee66638fb..000000000 --- a/sbt-app/src/sbt-test/dependency-management/scala-output-version/test +++ /dev/null @@ -1,35 +0,0 @@ -$ copy-file Bar.scala bar1a/src/main/scala/Bar.scala -$ copy-file Bar.scala bar1b/src/main/scala/Bar.scala -$ copy-file Bar.scala bar1c/src/main/scala/Bar.scala -$ copy-file Bar.scala bar2a/src/main/scala/Bar.scala -$ copy-file Bar.scala bar2b/src/main/scala/Bar.scala -$ copy-file Bar.scala bar2c/src/main/scala/Bar.scala -$ copy-file Bar.scala bar3a/src/main/scala/Bar.scala -$ copy-file Bar.scala bar3b/src/main/scala/Bar.scala -$ copy-file Bar.scala bar3c/src/main/scala/Bar.scala - -> ++3.1.2-RC2 -> foo / compile -> foo / run -> foo / test -> foo / publishLocal -> foo / checkIvy -> foo / publishM2 -> foo / checkPom - -> ++2.13.8 -> foo / compile -> foo / run -> foo / test -> foo / publishLocal -> foo / publishM2 - -> bar1a / run -> bar1b / run -> bar1c / run -> bar2a / run -> bar2b / run -> bar2c / run -> bar3a / run -> bar3b / run -> bar3c / run