diff --git a/build.sbt b/build.sbt index e8c0a5d3b..a33ac73ae 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.0-SNAPSHOT" + val v = "1.6.3-SNAPSHOT" nightlyVersion.getOrElse(v) } ThisBuild / version2_13 := "2.0.0-SNAPSHOT" diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala index 884aecc9a..15a1f2dcb 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala @@ -202,7 +202,7 @@ trait Parsers { * Parses a potentially quoted String value. The value may be verbatim quoted ([[StringVerbatim]]), * quoted with interpreted escapes ([[StringEscapable]]), or unquoted ([[NotQuoted]]). */ - lazy val StringBasic = StringVerbatim | StringEscapable | NotQuoted + lazy val StringBasic = StringVerbatim | StringEscapable | NotQuoted | NotQuotedThenQuoted /** * Parses a verbatim quoted String value, discarding the quotes in the result. This kind of quoted text starts with triple quotes `"""` @@ -270,6 +270,11 @@ trait Parsers { /** Parses an unquoted, non-empty String value that cannot start with a double quote and cannot contain whitespace.*/ lazy val NotQuoted = (NotDQuoteSpaceClass ~ OptNotSpace) map { case (c, s) => c.toString + s } + /** Parses a non-empty String value that cannot start with a double quote, but includes double quotes.*/ + lazy val NotQuotedThenQuoted = (NotQuoted ~ StringEscapable) map { + case (s1, s2) => s"""$s1\"$s2\"""" + } + /** * Applies `rep` zero or more times, separated by `sep`. * The result is the (possibly empty) sequence of results from the multiple `rep` applications. The `sep` results are discarded. diff --git a/internal/util-complete/src/test/scala/ParserTest.scala b/internal/util-complete/src/test/scala/ParserTest.scala index 49f99056f..4694f974a 100644 --- a/internal/util-complete/src/test/scala/ParserTest.scala +++ b/internal/util-complete/src/test/scala/ParserTest.scala @@ -119,6 +119,8 @@ object ParserTest extends Properties("Completing Parser") { property("repeatDep requires at least one token") = !matches(repeat, "") property("repeatDep accepts one token") = matches(repeat, colors.toSeq.head) property("repeatDep accepts two tokens") = matches(repeat, colors.toSeq.take(2).mkString(" ")) + property("parses string that doesn't start with quotes, but includes quotes within it") = + matches(StringBasic, "-Dsilicon:z3ConfigArgs=\"model=true model_validate=true\"") } object ParserExample { val ws = charClass(_.isWhitespace, "whitespace").+ diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 1a248b1df..2270ee573 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -419,7 +419,7 @@ object Keys { val bspBuildTargetScalacOptions = inputKey[Unit]("").withRank(DTask) val bspBuildTargetScalacOptionsItem = taskKey[ScalacOptionsItem]("").withRank(DTask) val bspScalaTestClasses = inputKey[Unit]("Corresponds to buildTarget/scalaTestClasses request").withRank(DTask) - val bspScalaTestClassesItem = taskKey[ScalaTestClassesItem]("").withRank(DTask) + val bspScalaTestClassesItem = taskKey[Seq[ScalaTestClassesItem]]("").withRank(DTask) val bspScalaMainClasses = inputKey[Unit]("Corresponds to buildTarget/scalaMainClasses request").withRank(DTask) val bspScalaMainClassesItem = taskKey[ScalaMainClassesItem]("").withRank(DTask) val bspReporter = taskKey[BuildServerReporter]("").withRank(DTask) diff --git a/main/src/main/scala/sbt/internal/Banner.scala b/main/src/main/scala/sbt/internal/Banner.scala index b43f811ad..0b5a25e71 100644 --- a/main/src/main/scala/sbt/internal/Banner.scala +++ b/main/src/main/scala/sbt/internal/Banner.scala @@ -16,7 +16,7 @@ private[sbt] object Banner { | - Improved JDK 17 support | - Improved Build Server Protocol (BSP) support | - Tab completion of global keys - |See https://eed3si9n.com/sbt-1.6.0-beta for full release notes. + |See https://eed3si9n.com/sbt-1.6.0 for full release notes. |Hide the banner for this release by running `skipBanner`. |""".stripMargin.linesIterator.mkString("\n")) case v if v.startsWith("1.4.0") => diff --git a/main/src/main/scala/sbt/internal/SysProp.scala b/main/src/main/scala/sbt/internal/SysProp.scala index fbf0942b2..737b0b368 100644 --- a/main/src/main/scala/sbt/internal/SysProp.scala +++ b/main/src/main/scala/sbt/internal/SysProp.scala @@ -218,6 +218,6 @@ object SysProp { baseCache.getAbsoluteFile / "v1" } - val sbtCredentialsEnv: Option[Credentials] = + lazy val sbtCredentialsEnv: Option[Credentials] = sys.env.get("SBT_CREDENTIALS").map(raw => new FileCredentials(new File(raw))) } diff --git a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala index b5006aabb..0c6655e2d 100644 --- a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala @@ -39,6 +39,7 @@ import scala.collection.mutable import scala.util.control.NonFatal import scala.util.{ Failure, Success, Try } import scala.annotation.nowarn +import sbt.testing.Framework object BuildServerProtocol { import sbt.internal.bsp.codec.JsonProtocol._ @@ -242,7 +243,7 @@ object BuildServerProtocol { val filter = ScopeFilter.in(workspace.scopes.values.toList) Def.task { val items = bspScalaTestClassesItem.result.all(filter).value - val successfulItems = anyOrThrow(items) + val successfulItems = anyOrThrow(items).flatten.toVector val result = ScalaTestClassesResult(successfulItems.toVector, None) s.respondEvent(result) } @@ -840,15 +841,25 @@ object BuildServerProtocol { } } - private def scalaTestClassesTask: Initialize[Task[ScalaTestClassesItem]] = Def.task { - val testClasses = Keys.definedTests.?.value - .getOrElse(Seq.empty) - .map(_.name) - .toVector - ScalaTestClassesItem( - bspTargetIdentifier.value, - testClasses - ) + private def scalaTestClassesTask: Initialize[Task[Seq[ScalaTestClassesItem]]] = Def.task { + Keys.definedTests.?.value match { + case None => Vector.empty + case Some(definitions) => + val frameworks: Seq[Framework] = Keys.loadedTestFrameworks.?.value + .map(_.values.toSeq) + .getOrElse(Seq.empty) + + val grouped = TestFramework.testMap(frameworks, definitions) + + grouped.map { + case (framework, definitions) => + ScalaTestClassesItem( + bspTargetIdentifier.value, + definitions.map(_.name).toVector, + framework.name() + ) + }.toSeq + } } private def scalaMainClassesTask: Initialize[Task[ScalaMainClassesItem]] = Def.task { diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 06bf9bbaf..ae0d47987 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -12,10 +12,10 @@ object Dependencies { sys.env.get("BUILD_VERSION") orElse sys.props.get("sbt.build.version") // sbt modules - private val ioVersion = nightlyVersion.getOrElse("1.6.0-M2") + private val ioVersion = nightlyVersion.getOrElse("1.6.0") private val lmVersion = - sys.props.get("sbt.build.lm.version").orElse(nightlyVersion).getOrElse("1.6.0-M2") - val zincVersion = nightlyVersion.getOrElse("1.6.0-M2") + sys.props.get("sbt.build.lm.version").orElse(nightlyVersion).getOrElse("1.6.1") + val zincVersion = nightlyVersion.getOrElse("1.6.0") private val sbtIO = "org.scala-sbt" %% "io" % ioVersion @@ -69,13 +69,13 @@ object Dependencies { def addSbtLmIvy = addSbtModule(sbtLmPath, "lmIvy", libraryManagementIvy) def addSbtLmIvyTest = addSbtModule(sbtLmPath, "lmIvy", libraryManagementIvy, Some(Test)) - def addSbtCompilerInterface = addSbtModule(sbtZincPath, "compilerInterfaceJVM", compilerInterface) - def addSbtCompilerClasspath = addSbtModule(sbtZincPath, "zincClasspathJVM2_12", compilerClasspath) - def addSbtCompilerApiInfo = addSbtModule(sbtZincPath, "zincApiInfoJVM2_12", compilerApiInfo) - def addSbtCompilerBridge = addSbtModule(sbtZincPath, "compilerBridgeJVM2_12", compilerBridge) - def addSbtZinc = addSbtModule(sbtZincPath, "zincJVM2_12", zinc) - def addSbtZincCompile = addSbtModule(sbtZincPath, "zincCompileJVM2_12", zincCompile) - def addSbtZincCompileCore = addSbtModule(sbtZincPath, "zincCompileCoreJVM2_12", zincCompileCore) + def addSbtCompilerInterface = addSbtModule(sbtZincPath, "compilerInterface", compilerInterface) + def addSbtCompilerClasspath = addSbtModule(sbtZincPath, "zincClasspath", compilerClasspath) + def addSbtCompilerApiInfo = addSbtModule(sbtZincPath, "zincApiInfo", compilerApiInfo) + def addSbtCompilerBridge = addSbtModule(sbtZincPath, "compilerBridge2_12", compilerBridge) + def addSbtZinc = addSbtModule(sbtZincPath, "zinc", zinc) + def addSbtZincCompile = addSbtModule(sbtZincPath, "zincCompile", zincCompile) + def addSbtZincCompileCore = addSbtModule(sbtZincPath, "zincCompileCore", zincCompileCore) val lmCoursierShaded = "io.get-coursier" %% "lm-coursier-shaded" % "2.0.10" @@ -124,7 +124,7 @@ object Dependencies { val scalaPar = "org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.0" // specify all of log4j modules to prevent misalignment - def log4jModule = (n: String) => "org.apache.logging.log4j" % n % "2.16.0" + def log4jModule = (n: String) => "org.apache.logging.log4j" % n % "2.17.1" val log4jApi = log4jModule("log4j-api") val log4jCore = log4jModule("log4j-core") val log4jSlf4jImpl = log4jModule("log4j-slf4j-impl") diff --git a/project/plugins.sbt b/project/plugins.sbt index 7f7e8fa9a..00c24984e 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -6,9 +6,9 @@ addSbtPlugin("com.dwijnand" % "sbt-dynver" % "4.0.0") addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.3.0") addSbtPlugin("org.scala-sbt" % "sbt-contraband" % "0.5.1") -addSbtPlugin("de.heikoseeberger" % "sbt-header" % "3.0.2") +addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.6.5") addSbtPlugin("com.lightbend" % "sbt-whitesource" % "0.1.14") -addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9") +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.2.0") addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.8.1") addSbtPlugin("com.swoval" % "sbt-java-format" % "0.3.1") addDependencyTreePlugin diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/ScalaTestClassesItem.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/ScalaTestClassesItem.scala index 210e8cc30..e274fda16 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/ScalaTestClassesItem.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/ScalaTestClassesItem.scala @@ -7,25 +7,27 @@ package sbt.internal.bsp /** * @param target The build target that contains the test classes. * @param classes The fully qualified names of the test classes in this target + * @param framework The name of the test framework used in test classes. */ final class ScalaTestClassesItem private ( val target: sbt.internal.bsp.BuildTargetIdentifier, - val classes: Vector[String]) extends Serializable { + val classes: Vector[String], + val framework: Option[String]) extends Serializable { override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { - case x: ScalaTestClassesItem => (this.target == x.target) && (this.classes == x.classes) + case x: ScalaTestClassesItem => (this.target == x.target) && (this.classes == x.classes) && (this.framework == x.framework) case _ => false }) override def hashCode: Int = { - 37 * (37 * (37 * (17 + "sbt.internal.bsp.ScalaTestClassesItem".##) + target.##) + classes.##) + 37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.ScalaTestClassesItem".##) + target.##) + classes.##) + framework.##) } override def toString: String = { - "ScalaTestClassesItem(" + target + ", " + classes + ")" + "ScalaTestClassesItem(" + target + ", " + classes + ", " + framework + ")" } - private[this] def copy(target: sbt.internal.bsp.BuildTargetIdentifier = target, classes: Vector[String] = classes): ScalaTestClassesItem = { - new ScalaTestClassesItem(target, classes) + private[this] def copy(target: sbt.internal.bsp.BuildTargetIdentifier = target, classes: Vector[String] = classes, framework: Option[String] = framework): ScalaTestClassesItem = { + new ScalaTestClassesItem(target, classes, framework) } def withTarget(target: sbt.internal.bsp.BuildTargetIdentifier): ScalaTestClassesItem = { copy(target = target) @@ -33,8 +35,15 @@ final class ScalaTestClassesItem private ( def withClasses(classes: Vector[String]): ScalaTestClassesItem = { copy(classes = classes) } + def withFramework(framework: Option[String]): ScalaTestClassesItem = { + copy(framework = framework) + } + def withFramework(framework: String): ScalaTestClassesItem = { + copy(framework = Option(framework)) + } } object ScalaTestClassesItem { - def apply(target: sbt.internal.bsp.BuildTargetIdentifier, classes: Vector[String]): ScalaTestClassesItem = new ScalaTestClassesItem(target, classes) + def apply(target: sbt.internal.bsp.BuildTargetIdentifier, classes: Vector[String], framework: Option[String]): ScalaTestClassesItem = new ScalaTestClassesItem(target, classes, framework) + def apply(target: sbt.internal.bsp.BuildTargetIdentifier, classes: Vector[String], framework: String): ScalaTestClassesItem = new ScalaTestClassesItem(target, classes, Option(framework)) } diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaTestClassesItemFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaTestClassesItemFormats.scala index a079301b3..1353a0b41 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaTestClassesItemFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaTestClassesItemFormats.scala @@ -13,8 +13,9 @@ implicit lazy val ScalaTestClassesItemFormat: JsonFormat[sbt.internal.bsp.ScalaT unbuilder.beginObject(__js) val target = unbuilder.readField[sbt.internal.bsp.BuildTargetIdentifier]("target") val classes = unbuilder.readField[Vector[String]]("classes") + val framework = unbuilder.readField[Option[String]]("framework") unbuilder.endObject() - sbt.internal.bsp.ScalaTestClassesItem(target, classes) + sbt.internal.bsp.ScalaTestClassesItem(target, classes, framework) case None => deserializationError("Expected JsObject but found None") } @@ -23,6 +24,7 @@ implicit lazy val ScalaTestClassesItemFormat: JsonFormat[sbt.internal.bsp.ScalaT builder.beginObject() builder.addField("target", obj.target) builder.addField("classes", obj.classes) + builder.addField("framework", obj.framework) builder.endObject() } } diff --git a/protocol/src/main/contraband/bsp.contra b/protocol/src/main/contraband/bsp.contra index 01b3a450f..62438cff0 100644 --- a/protocol/src/main/contraband/bsp.contra +++ b/protocol/src/main/contraband/bsp.contra @@ -636,6 +636,9 @@ type ScalaTestClassesItem { ## The fully qualified names of the test classes in this target classes: [String] + + ## The name of the test framework used in test classes. + framework: String } ## Scala Main Class Request diff --git a/sbt b/sbt index 9d04b2deb..97afb370d 100755 --- a/sbt +++ b/sbt @@ -1,7 +1,7 @@ #!/usr/bin/env bash set +e -declare builtin_sbt_version="1.6.0-RC1" +declare builtin_sbt_version="1.6.2" declare -a residual_args declare -a java_args declare -a scalac_args @@ -583,7 +583,6 @@ Usage: `basename "$0"` [options] -Dkey=val pass -Dkey=val directly to the java runtime -J-X pass option -X directly to the java runtime (-J is stripped) - -S-X add -X to sbt's scalacOptions (-S is stripped) In the case of duplicated or conflicting options, the order above shows precedence: JAVA_OPTS lowest, command line options highest. diff --git a/sbt-app/src/main/scala/sbt/Import.scala b/sbt-app/src/main/scala/sbt/Import.scala index b71e855df..548e571a4 100644 --- a/sbt-app/src/main/scala/sbt/Import.scala +++ b/sbt-app/src/main/scala/sbt/Import.scala @@ -297,6 +297,7 @@ trait Import { type IvyScala = sbt.librarymanagement.ScalaModuleInfo val JCenterRepository = sbt.librarymanagement.Resolver.JCenterRepository val JavaNet2Repository = sbt.librarymanagement.Resolver.JavaNet2Repository + val License = sbt.librarymanagement.License type LogicalClock = sbt.librarymanagement.LogicalClock val LogicalClock = sbt.librarymanagement.LogicalClock type MakePomConfiguration = sbt.librarymanagement.MakePomConfiguration diff --git a/sbt-app/src/sbt-test/dependency-management/artifact/build.sbt b/sbt-app/src/sbt-test/dependency-management/artifact/build.sbt index d0d5afc46..e4ffeae52 100644 --- a/sbt-app/src/sbt-test/dependency-management/artifact/build.sbt +++ b/sbt-app/src/sbt-test/dependency-management/artifact/build.sbt @@ -11,6 +11,7 @@ ThisBuild / version := "0.1.0-SNAPSHOT" ThisBuild / organization := "com.example" ThisBuild / organizationName := "example" ThisBuild / csrCacheDirectory := (ThisBuild / baseDirectory).value / "coursier-cache" +ThisBuild / licenses := List(License.Apache2) lazy val Dev = config("dev").extend(Compile) .describedAs("Dependencies required for development environments") diff --git a/server-test/src/test/scala/testpkg/BuildServerTest.scala b/server-test/src/test/scala/testpkg/BuildServerTest.scala index 5aa231752..9cbe43980 100644 --- a/server-test/src/test/scala/testpkg/BuildServerTest.scala +++ b/server-test/src/test/scala/testpkg/BuildServerTest.scala @@ -308,7 +308,8 @@ object BuildServerTest extends AbstractServerTest { assert(svr.waitForString(10.seconds) { s => (s contains """"id":"72"""") && (s contains """"tests.FailingTest"""") && - (s contains """"tests.PassingTest"""") + (s contains """"tests.PassingTest"""") && + (s contains """"framework":"ScalaTest"""") }) } diff --git a/testing/src/main/scala/sbt/TestFramework.scala b/testing/src/main/scala/sbt/TestFramework.scala index d7e057ac5..cb89dd5f4 100644 --- a/testing/src/main/scala/sbt/TestFramework.scala +++ b/testing/src/main/scala/sbt/TestFramework.scala @@ -49,12 +49,14 @@ final class TestFramework(val implClassNames: String*) extends Serializable { ): Option[Framework] = { def logError(e: Throwable): Option[Framework] = { log.error( - s"Error loading test framework ($e). This usually means that you are" - + " using a layered class loader that cannot reach the sbt.testing.Framework class." - + " The most likely cause is that your project has a runtime dependency on your" - + " test framework, e.g. scalatest. To fix this, you can try to set\n" - + "Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.ScalaLibrary\nor\n" - + "Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Flat" + s"""Error loading test framework ($e). + |This often means that you are using a layered class loader that cannot reach the sbt.testing.Framework class. + |The most likely cause is that your project has a runtime dependency on your + |test framework, e.g. ScalaTest. To fix this, you can try to set + | + | Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.ScalaLibrary + |or + | Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Flat""".stripMargin ) None } @@ -66,8 +68,12 @@ final class TestFramework(val implClassNames: String*) extends Serializable { case oldFramework: OldFramework => new FrameworkWrapper(oldFramework) }) } catch { - case e: NoClassDefFoundError => logError(e) - case e: MatchError => logError(e) + case e: NoClassDefFoundError => + logError(e) + throw e + case e: MatchError => + logError(e) + throw e case _: ClassNotFoundException => log.debug("Framework implementation '" + head + "' not present.") createFramework(loader, log, tail) @@ -225,21 +231,26 @@ object TestFramework { ): Vector[(String, TestFunction)] = for (d <- inputs; act <- mapped.get(d.name)) yield (d.name, act) - private[this] def testMap( + def testMap( frameworks: Seq[Framework], tests: Seq[TestDefinition] ): Map[Framework, Set[TestDefinition]] = { import scala.collection.mutable.{ HashMap, HashSet, Set } val map = new HashMap[Framework, Set[TestDefinition]] + def assignTest(test: TestDefinition): Unit = { def isTestForFramework(framework: Framework) = getFingerprints(framework).exists { t => matches(t, test.fingerprint) } - for (framework <- frameworks.find(isTestForFramework)) + + frameworks.find(isTestForFramework).foreach { framework => map.getOrElseUpdate(framework, new HashSet[TestDefinition]) += test + } } + if (frameworks.nonEmpty) for (test <- tests) assignTest(test) + map.toMap.mapValues(_.toSet).toMap }