diff --git a/.gitattributes b/.gitattributes index 11d44bd7f..5b6b22eaf 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,7 @@ +sbt text eol=lf +*.sh text eol=lf +launcher-package/src/windows/sbt text eol=lf + # Exclude contraband generated files from diff (by default - you can see it if you want) **/contraband-scala/**/* -diff merge=ours **/contraband-scala/**/* linguist-generated=true diff --git a/.github/ISSUE_TEMPLATE/--feature-request.md b/.github/ISSUE_TEMPLATE/--feature-request.md index 5ef8d3776..3d76f73b9 100644 --- a/.github/ISSUE_TEMPLATE/--feature-request.md +++ b/.github/ISSUE_TEMPLATE/--feature-request.md @@ -7,4 +7,4 @@ assignees: '' --- -Please use https://discuss.lightbend.com/c/tooling including a specific user story instead of posting them to the issue tracker. +Please use https://github.com/sbt/sbt/discussions including a specific user story instead of posting them to the issue tracker. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 980202e19..4a9f6e365 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,38 +10,48 @@ jobs: matrix: include: - os: ubuntu-latest - java: "17.0-custom=tgz+https://download.java.net/java/GA/jdk17/0d483333a00540d886896bac774ff48b/35/GPL/openjdk-17_linux-x64_bin.tar.gz" + java: 17 + distribution: temurin jobtype: 1 - os: ubuntu-latest - java: "17.0-custom=tgz+https://download.java.net/java/GA/jdk17/0d483333a00540d886896bac774ff48b/35/GPL/openjdk-17_linux-x64_bin.tar.gz" + java: 17 + distribution: temurin jobtype: 2 - os: ubuntu-latest - java: "17.0-custom=tgz+https://download.java.net/java/GA/jdk17/0d483333a00540d886896bac774ff48b/35/GPL/openjdk-17_linux-x64_bin.tar.gz" + java: 17 + distribution: temurin jobtype: 3 - os: ubuntu-latest - java: "17.0-custom=tgz+https://download.java.net/java/GA/jdk17/0d483333a00540d886896bac774ff48b/35/GPL/openjdk-17_linux-x64_bin.tar.gz" + java: 17 + distribution: temurin jobtype: 4 - os: ubuntu-latest - java: "17-custom=tgz+https://download.java.net/java/GA/jdk17/0d483333a00540d886896bac774ff48b/35/GPL/openjdk-17_linux-x64_bin.tar.gz" + java: 17 + distribution: temurin jobtype: 5 - os: ubuntu-latest - java: "adopt@1.8" + java: 8 + distribution: adopt jobtype: 6 - os: ubuntu-latest - java: "adopt@1.8" + java: 8 + distribution: adopt jobtype: 7 - os: macos-latest - java: "adopt@1.8" + java: 8 + distribution: adopt jobtype: 8 - os: windows-latest - java: "adopt@1.8" + java: 8 + distribution: adopt jobtype: 9 runs-on: ${{ matrix.os }} 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 TEST_SBT_VER: 1.5.0 @@ -50,119 +60,132 @@ jobs: SPARK_LOCAL_IP: "127.0.0.1" steps: - name: Checkout sbt/sbt - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Checkout sbt/io - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: sbt/io ref: develop path: io - name: Checkout sbt/librarymanagement - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: sbt/librarymanagement ref: develop path: librarymanagement - name: Checkout sbt/zinc - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: sbt/zinc ref: develop path: zinc - - name: Setup - uses: olafurpg/setup-scala@v13 + - name: Setup JDK + uses: actions/setup-java@v3 with: + distribution: "${{ matrix.distribution }}" java-version: "${{ matrix.java }}" - name: Set up Python 3.7 - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.7 - name: Coursier cache uses: coursier/cache-action@v6 - name: Cache sbt - uses: actions/cache@v2.1.6 + uses: actions/cache@v3 with: path: ~/.sbt key: ${{ runner.os }}-sbt-cache-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} - - name: Build and test + - name: Build and test (1) + if: ${{ matrix.jobtype == 1 }} + shell: bash + run: | + # ./sbt -v --client mimaReportBinaryIssues + ./sbt -v --client javafmtCheck + ./sbt -v --client "Test/javafmtCheck" + ./sbt -v --client scalafmtCheckAll + ./sbt -v --client scalafmtSbtCheck + ./sbt -v --client serverTestProj/scalafmtCheckAll + ./sbt -v --client headerCheck + ./sbt -v --client "Test/headerCheck" + ./sbt -v --client whitesourceOnPush + ./sbt -v --client "Test/compile" + ./sbt -v --client publishLocal + ./sbt -v --client test + ./sbt -v --client "serverTestProj/test" + ./sbt -v --client doc + ./sbt -v --client "all $UTIL_TESTS" + ./sbt -v --client ++$SCALA_213 + ./sbt -v --client "all $UTIL_TESTS" + - name: Build and test (2) + if: ${{ matrix.jobtype == 2 }} + shell: bash + run: | + ./sbt -v "scripted actions/* apiinfo/* compiler-project/* ivy-deps-management/* reporter/* tests/* watch/* classloader-cache/* package/*" + - name: Build and test (3) + if: ${{ matrix.jobtype == 3 }} + shell: bash + run: | + ./sbt -v "dependencyTreeProj/publishLocal; scripted dependency-graph/* dependency-management/* plugins/* project-load/* java/* run/* nio/*" + - name: Build and test (4) + if: ${{ matrix.jobtype == 4 }} + shell: bash + run: | + ./sbt -v "repoOverrideTest:scripted dependency-management/*; scripted source-dependencies/* project/*" + - name: Build and test (5) + if: ${{ matrix.jobtype == 5 }} + shell: bash + run: | + ./sbt -v "++$SCALA_213!; test; ++$SCALA_3!; all utilControl/test utilRelation/test utilPosition/test" + - name: Build and test (6) + if: ${{ matrix.jobtype == 6 }} + shell: bash + run: | + # build from fresh IO, LM, and Zinc + BUILD_VERSION="1.5.0-SNAPSHOT" + cd io + sbt -v -Dsbt.build.version=${BUILD_VERSION} +publishLocal + cd ../ + sbt -Dsbtlm.path=$HOME/work/sbt/sbt/librarymanagement -Dsbtzinc.path=$HOME/work/sbt/sbt/zinc -Dsbt.build.version=$BUILD_VERSION -Dsbt.build.fatal=false "+lowerUtils/publishLocal; {librarymanagement}/publishLocal; {zinc}/publishLocal; upperModules/publishLocal" + rm -r $(find $HOME/.sbt/boot -name "*-SNAPSHOT") || true + sbt -v -Dsbt.version=$BUILD_VERSION "++$SCALA_213; all $UTIL_TESTS; ++$SCALA_212; all $UTIL_TESTS; scripted actions/* source-dependencies/*1of3 dependency-management/*1of4 java/*" + - name: Build and test (7) + if: ${{ matrix.jobtype == 7 }} + shell: bash + run: | + # test launcher script + echo build using JDK 8 test using JDK 8 and JDK 11 + cd launcher-package + sbt -Dsbt.build.version=$TEST_SBT_VER rpm:packageBin debian:packageBin + sbt -Dsbt.build.version=$TEST_SBT_VER universal:packageBin universal:stage integrationTest/test + cd citest && ./test.sh + $HOME/bin/jabba install $JDK11 && exec $HOME/bin/jabba which --home $JDK11 + java -Xmx32m -version + ./test.sh + - name: Build and test (8) + if: ${{ matrix.jobtype == 8 }} + shell: bash + run: | + # test launcher script + echo build using JDK 8, test using JDK 8, on macOS + cd launcher-package + bin/coursier resolve + sbt -Dsbt.build.version=$TEST_SBT_VER universal:packageBin universal:stage integrationTest/test + cd citest && ./test.sh + - name: Build and test (9) + if: ${{ matrix.jobtype == 9 }} + shell: bash + run: | + # test launcher script + echo build using JDK 8, test using JDK 8, on Windows + cd launcher-package + bin/coursier.bat resolve + sbt -Dsbt.build.version=$TEST_SBT_VER universal:packageBin universal:stage integrationTest/test + cd citest + ./test.bat + test3/test3.bat + - name: Cleanup shell: bash run: | - rm -rf "$HOME/.sbt/scripted/" || true - case ${{ matrix.jobtype }} in - 1) - ./sbt -v --client mimaReportBinaryIssues - ./sbt -v --client javafmtCheck - ./sbt -v --client "Test/javafmtCheck" - ./sbt -v --client scalafmtCheckAll - ./sbt -v --client scalafmtSbtCheck - ./sbt -v --client serverTestProj/scalafmtCheckAll - ./sbt -v --client headerCheck - ./sbt -v --client "Test/headerCheck" - ./sbt -v --client whitesourceOnPush - ./sbt -v --client "Test/compile" - ./sbt -v --client publishLocal - ./sbt -v --client test - ./sbt -v --client "serverTestProj/test" - ./sbt -v --client doc - ./sbt -v --client "all $UTIL_TESTS" - ./sbt -v --client ++$SCALA_213 - ./sbt -v --client "all $UTIL_TESTS" - ;; - 2) - ./sbt -v "scripted actions/* apiinfo/* compiler-project/* ivy-deps-management/* reporter/* tests/* watch/* classloader-cache/* package/*" - ;; - 3) - ./sbt -v "dependencyTreeProj/publishLocal; scripted dependency-graph/* dependency-management/* plugins/* project-load/* java/* run/* nio/*" - ;; - 4) - ./sbt -v "repoOverrideTest:scripted dependency-management/*; scripted source-dependencies/* project/*" - ;; - 5) - ./sbt -v "++$SCALA_213!; test;" - ;; - 6) - # build from fresh IO, LM, and Zinc - BUILD_VERSION="1.5.0-SNAPSHOT" - cd io - sbt -v -Dsbt.build.version=${BUILD_VERSION} +publishLocal - cd ../ - sbt -Dsbtlm.path=$HOME/work/sbt/sbt/librarymanagement -Dsbtzinc.path=$HOME/work/sbt/sbt/zinc -Dsbt.build.version=$BUILD_VERSION -Dsbt.build.fatal=false "+lowerUtils/publishLocal; {librarymanagement}/publishLocal; {zinc}/publishLocal; upperModules/publishLocal" - rm -r $(find $HOME/.sbt/boot -name "*-SNAPSHOT") || true - sbt -v -Dsbt.version=$BUILD_VERSION "++$SCALA_213; all $UTIL_TESTS; ++$SCALA_212; all $UTIL_TESTS; scripted actions/* source-dependencies/*1of3 dependency-management/*1of4 java/*" - ;; - 7) - # test launcher script - echo build using JDK 8 test using JDK 8 and JDK 11 - cd launcher-package - sbt -Dsbt.build.version=$TEST_SBT_VER rpm:packageBin debian:packageBin - sbt -Dsbt.build.version=$TEST_SBT_VER universal:packageBin universal:stage integrationTest/test - cd citest && ./test.sh - $HOME/bin/jabba install $JDK11 && exec $HOME/bin/jabba which --home $JDK11 - java -Xmx32m -version - ./test.sh - ;; - 8) - # test launcher script - echo build using JDK 8, test using JDK 8, on macOS - cd launcher-package - bin/coursier resolve - sbt -Dsbt.build.version=$TEST_SBT_VER universal:packageBin universal:stage integrationTest/test - cd citest && ./test.sh - ;; - 9) - # test launcher script - echo build using JDK 8, test using JDK 8, on Windows - cd launcher-package - bin/coursier.bat resolve - sbt -Dsbt.build.version=$TEST_SBT_VER universal:packageBin universal:stage integrationTest/test - cd citest - ./test.bat - test3/test3.bat - ;; - *) - echo unknown jobtype - exit 1 - esac rm -rf "$HOME/.sbt/scripted/" || true rm -rf "$HOME/.ivy2/local" || true rm -r $(find $HOME/.sbt/boot -name "*-SNAPSHOT") || true diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 0593b63e9..4d98a29d8 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -16,21 +16,21 @@ jobs: JAVA_OPTS: -Xms800M -Xmx800M -Xss6M -XX:ReservedCodeCacheSize=128M -server -Dsbt.io.virtual=false -Dfile.encoding=UTF-8 steps: - name: Checkout sbt/sbt - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Checkout sbt/io - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: sbt/io ref: develop path: io - name: Checkout sbt/librarymanagement - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: sbt/librarymanagement ref: develop path: librarymanagement - name: Checkout sbt/zinc - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: sbt/zinc ref: develop diff --git a/.gitignore b/.gitignore index 322179c4a..4a82989bc 100644 --- a/.gitignore +++ b/.gitignore @@ -11,5 +11,6 @@ npm-debug.log .bloop .metals .bsp/ +.vscode/ metals.sbt launcher-package/citest/freshly-baked diff --git a/.scalafmt.conf b/.scalafmt.conf index 213dea496..06a114f59 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -21,3 +21,8 @@ align.openParenDefnSite = false danglingParentheses = true trailingCommas = preserve + +# TODO update scalafmt and enable Scala 3 +project.excludeFilters = [ + "internal/util-position/src/main/scala-3/sbt/internal/util/SourcePositionMacro.scala" +] diff --git a/DEVELOPING.md b/DEVELOPING.md index 8b1db7b09..40cbfda3a 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -151,14 +151,14 @@ suite with `sbt testOnly` #### Integration tests -Scripted integration tests reside in `sbt/src/sbt-test` and are +Scripted integration tests reside in `sbt-app/src/sbt-test` and are written using the same testing infrastructure sbt plugin authors can use to test their own plugins with sbt. You can read more about this style of tests [here](https://www.scala-sbt.org/1.0/docs/Testing-sbt-plugins). You can run the integration tests with the `sbt scripted` sbt command. To run a single test, such as the test in -`sbt/src/sbt-test/project/global-plugin`, simply run: +`sbt-app/src/sbt-test/project/global-plugin`, simply run: sbt "scripted project/global-plugin" diff --git a/README.md b/README.md index 4224db130..990d6c676 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build Status](https://travis-ci.com/sbt/sbt.svg?branch=develop)](https://travis-ci.com/github/sbt/sbt) +[![CI](https://github.com/sbt/sbt/actions/workflows/ci.yml/badge.svg)](https://github.com/sbt/sbt/actions/workflows/ci.yml) [![Latest version](https://img.shields.io/github/tag/sbt/sbt.svg)](https://index.scala-lang.org/sbt/sbt) [![Gitter Chat](https://badges.gitter.im/sbt/sbt.svg)](https://gitter.im/sbt/sbt) diff --git a/build.sbt b/build.sbt index 774a4d591..3168ba1dd 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.7.0-SNAPSHOT" nightlyVersion.getOrElse(v) } ThisBuild / version2_13 := "2.0.0-SNAPSHOT" @@ -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.9" val excludeLint = SettingKey[Set[Def.KeyedInitialize[_]]]("excludeLintKeys") Global / excludeLint := (Global / excludeLint).?.value.getOrElse(Set.empty) Global / excludeLint += componentID @@ -68,10 +68,6 @@ def commonBaseSettings: Seq[Setting[_]] = Def.settings( componentID := None, resolvers += Resolver.typesafeIvyRepo("releases").withName("typesafe-sbt-build-ivy-releases"), resolvers += Resolver.sonatypeRepo("snapshots"), - resolvers += Resolver.url( - "bintray-scala-hedgehog", - url("https://dl.bintray.com/hedgehogqa/scala-hedgehog") - )(Resolver.ivyStylePatterns), testFrameworks += TestFramework("hedgehog.sbt.Framework"), testFrameworks += TestFramework("verify.runner.Framework"), Global / concurrentRestrictions += Util.testExclusiveRestriction, @@ -106,8 +102,15 @@ def commonBaseSettings: Seq[Setting[_]] = Def.settings( run / fork := true, ) def commonSettings: Seq[Setting[_]] = - commonBaseSettings :+ - addCompilerPlugin(kindProjector) + commonBaseSettings :+ { + libraryDependencies ++= { + if (scalaBinaryVersion.value == "3") { + Nil + } else { + Seq(compilerPlugin(kindProjector)) + } + } + } def utilCommonSettings: Seq[Setting[_]] = baseSettings :+ (crossScalaVersions := (scala212 :: scala213 :: Nil)) @@ -467,7 +470,7 @@ lazy val utilScripted = (project in file("internal") / "util-scripted") .settings( utilCommonSettings, name := "Util Scripted", - libraryDependencies += scalaParsers, + libraryDependencies += scalaParsers.value, utilMimaSettings, ) .configure(addSbtIO) @@ -481,7 +484,7 @@ lazy val testingProj = (project in file("testing")) baseSettings, name := "Testing", libraryDependencies ++= Seq( - scalaXml, + scalaXml.value, testInterface, launcherInterface, sjsonNewScalaJson.value @@ -807,7 +810,13 @@ lazy val coreMacrosProj = (project in file("core-macros")) .settings( baseSettings :+ (crossScalaVersions := (scala212 :: scala213 :: Nil)), name := "Core Macros", - libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value, + libraryDependencies += { + if (scalaBinaryVersion.value == "3") { + "org.scala-lang" % "scala-compiler" % scala213 + } else { + "org.scala-lang" % "scala-compiler" % scalaVersion.value + } + }, SettingKey[Boolean]("exportPipelining") := false, mimaSettings, ) @@ -917,7 +926,7 @@ lazy val mainProj = (project in file("main")) } }, libraryDependencies ++= - (Seq(scalaXml, launcherInterface, caffeine, lmCoursierShaded) ++ log4jModules), + (Seq(scalaXml.value, launcherInterface, caffeine, lmCoursierShaded) ++ log4jModules), libraryDependencies ++= (scalaVersion.value match { case v if v.startsWith("2.12.") => List() case _ => List(scalaPar) @@ -1048,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( @@ -1108,7 +1118,7 @@ lazy val serverTestProj = (project in file("server-test")) val rawClasspath = (Compile / fullClasspathAsJars).value.map(_.data).mkString(java.io.File.pathSeparator) val cp = - if (scala.util.Properties.isWin) rawClasspath.replaceAllLiterally("\\", "\\\\") + if (scala.util.Properties.isWin) rawClasspath.replace("\\", "\\\\") else rawClasspath val content = { s"""| diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/HList.scala b/internal/util-collection/src/main/scala/sbt/internal/util/HList.scala index f0922a079..dc9d163f9 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/HList.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/HList.scala @@ -30,7 +30,7 @@ final case class HCons[H, T <: HList](head: H, tail: T) extends HList { type Wrap[M[_]] = M[H] :+: T#Wrap[M] def :+:[G](g: G): G :+: H :+: T = HCons(g, this) - override def toString = head + " :+: " + tail.toString + override def toString = head.toString + " :+: " + tail.toString } object HList { diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala index f4110acc8..4541980cd 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Settings.scala @@ -43,7 +43,7 @@ private final class Settings0[ScopeType]( (data get scope).flatMap(_ get key) def set[T](scope: ScopeType, key: AttributeKey[T], value: T): Settings[ScopeType] = { - val map = data getOrElse (scope, AttributeMap.empty) + val map = data.getOrElse(scope, AttributeMap.empty) val newData = data.updated(scope, map.put(key, value)) new Settings0(newData, delegates) } @@ -476,7 +476,7 @@ trait Init[ScopeType] { if (posDefined.size == settings.size) "defined at:" else "some of the defining occurrences:" - header + (posDefined.distinct mkString ("\n\t", "\n\t", "\n")) + header + (posDefined.distinct.mkString("\n\t", "\n\t", "\n")) } else "" } @@ -711,10 +711,10 @@ trait Init[ScopeType] { def mapReferenced(g: MapScoped): Setting[T] = make(key, init mapReferenced g, pos) def validateReferenced(g: ValidateRef): Either[Seq[Undefined], Setting[T]] = - (init validateReferenced g).right.map(newI => make(key, newI, pos)) + (init validateReferenced g).map(newI => make(key, newI, pos)) private[sbt] def validateKeyReferenced(g: ValidateKeyRef): Either[Seq[Undefined], Setting[T]] = - (init validateKeyReferenced g).right.map(newI => make(key, newI, pos)) + (init validateKeyReferenced g).map(newI => make(key, newI, pos)) def mapKey(g: MapScoped): Setting[T] = make(g(key), init, pos) def mapInit(f: (ScopedKey[T], T) => T): Setting[T] = make(key, init(t => f(key, t)), pos) @@ -879,9 +879,8 @@ trait Init[ScopeType] { def evaluate(ss: Settings[ScopeType]): T = f(in evaluate ss) evaluate ss def mapReferenced(g: MapScoped) = new Bind[S, T](s => f(s) mapReferenced g, in mapReferenced g) - def validateKeyReferenced(g: ValidateKeyRef) = (in validateKeyReferenced g).right.map { - validIn => - new Bind[S, T](s => handleUndefined(f(s) validateKeyReferenced g), validIn) + def validateKeyReferenced(g: ValidateKeyRef) = (in validateKeyReferenced g).map { validIn => + new Bind[S, T](s => handleUndefined(f(s) validateKeyReferenced g), validIn) } def mapConstant(g: MapConstant) = new Bind[S, T](s => f(s) mapConstant g, in mapConstant g) @@ -898,7 +897,7 @@ trait Init[ScopeType] { def validateKeyReferenced(g: ValidateKeyRef) = a match { case None => Right(this) - case Some(i) => Right(new Optional(i.validateKeyReferenced(g).right.toOption, f)) + case Some(i) => Right(new Optional(i.validateKeyReferenced(g).toOption, f)) } def mapConstant(g: MapConstant): Initialize[T] = new Optional(a map mapConstantT(g).fn, f) diff --git a/internal/util-collection/src/main/scala/sbt/internal/util/Util.scala b/internal/util-collection/src/main/scala/sbt/internal/util/Util.scala index 0155306c8..ca03ed381 100644 --- a/internal/util-collection/src/main/scala/sbt/internal/util/Util.scala +++ b/internal/util-collection/src/main/scala/sbt/internal/util/Util.scala @@ -43,7 +43,7 @@ object Util { def camelToHyphen(s: String): String = Camel.replaceAllIn(s, m => m.group(1) + "-" + m.group(2).toLowerCase(Locale.ENGLISH)) - def quoteIfKeyword(s: String): String = if (ScalaKeywords.values(s)) '`' + s + '`' else s + def quoteIfKeyword(s: String): String = if (ScalaKeywords.values(s)) s"`${s}`" else s def ignoreResult[T](f: => T): Unit = macro Macro.ignore @@ -54,7 +54,7 @@ object Util { System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows") lazy val isCygwin: Boolean = { - val os = Option(System.getenv("OSTYPE")) + val os = sys.env.get("OSTYPE") os match { case Some(x) => x.toLowerCase(Locale.ENGLISH).contains("cygwin") case _ => false @@ -64,7 +64,7 @@ object Util { lazy val isNonCygwinWindows: Boolean = isWindows && !isCygwin lazy val isCygwinWindows: Boolean = isWindows && isCygwin - lazy val isEmacs: Boolean = Option(System.getenv("INSIDE_EMACS")).isDefined + lazy val isEmacs: Boolean = sys.env.contains("INSIDE_EMACS") def nil[A]: List[A] = List.empty[A] def nilSeq[A]: Seq[A] = Seq.empty[A] diff --git a/internal/util-collection/src/test/scala/UnitSpec.scala b/internal/util-collection/src/test/scala/UnitSpec.scala index ed582c3bb..912b39d16 100644 --- a/internal/util-collection/src/test/scala/UnitSpec.scala +++ b/internal/util-collection/src/test/scala/UnitSpec.scala @@ -7,6 +7,7 @@ package sbt.internal.util -import org.scalatest._ +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers -abstract class UnitSpec extends FlatSpec with Matchers +abstract class UnitSpec extends AnyFlatSpec with Matchers diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala index d52903d87..fbc86b725 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala @@ -178,13 +178,7 @@ abstract class JLine extends LineReader { protected[this] lazy val in: InputStream = Terminal.wrappedSystemIn override def readLine(prompt: String, mask: Option[Char] = None): Option[String] = - try { - unsynchronizedReadLine(prompt, mask) - } catch { - case _: InterruptedException => - // println("readLine: InterruptedException") - Option("") - } + unsynchronizedReadLine(prompt, mask) private[this] def unsynchronizedReadLine(prompt: String, mask: Option[Char]): Option[String] = readLineWithHistory(prompt, mask) map { x => diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala index 464ab1916..8d66d2137 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala @@ -909,7 +909,7 @@ private final class StringLiteral(str: String, start: Int) extends ValidParser[S if (str.charAt(start) == c) stringLiteral(str, start + 1) else new Invalid(resultEmpty) def completions(level: Int) = Completions.single(Completion.suggestion(str.substring(start))) - override def toString = '"' + str + '"' + override def toString = "\"" + str + "\"" } private final class CharacterClass(f: Char => Boolean, label: String) extends ValidParser[Char] { 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 651ddd8d0..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 `"""` @@ -238,12 +238,12 @@ trait Parsers { val notDelim = charClass(c => c != open && c != close).*.string def impl(): Parser[String] = { (open ~ (notDelim ~ close).?).flatMap { - case (l, Some((content, r))) => Parser.success(l + content + r) + case (l, Some((content, r))) => Parser.success(s"$l$content$r") case (l, None) => ((notDelim ~ impl()).map { case (leftPrefix, nestedBraces) => leftPrefix + nestedBraces }.+ ~ notDelim ~ close).map { - case ((nested, suffix), r) => l + nested.mkString + suffix + r + case ((nested, suffix), r) => s"$l${nested.mkString}$suffix$r" } } } @@ -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/internal/util-complete/src/test/scala/UnitSpec.scala b/internal/util-complete/src/test/scala/UnitSpec.scala index ed582c3bb..912b39d16 100644 --- a/internal/util-complete/src/test/scala/UnitSpec.scala +++ b/internal/util-complete/src/test/scala/UnitSpec.scala @@ -7,6 +7,7 @@ package sbt.internal.util -import org.scalatest._ +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers -abstract class UnitSpec extends FlatSpec with Matchers +abstract class UnitSpec extends AnyFlatSpec with Matchers diff --git a/internal/util-complete/src/test/scala/sbt/internal/util/complete/SizeParserSpec.scala b/internal/util-complete/src/test/scala/sbt/internal/util/complete/SizeParserSpec.scala index 521f2d10a..6d68fdae9 100644 --- a/internal/util-complete/src/test/scala/sbt/internal/util/complete/SizeParserSpec.scala +++ b/internal/util-complete/src/test/scala/sbt/internal/util/complete/SizeParserSpec.scala @@ -7,9 +7,9 @@ package sbt.internal.util.complete -import org.scalatest.FlatSpec +import org.scalatest.flatspec.AnyFlatSpec -class SizeParserSpec extends FlatSpec { +class SizeParserSpec extends AnyFlatSpec { "SizeParser" should "handle raw bytes" in { assert(Parser.parse(str = "123456", SizeParser.value) == Right(123456L)) } 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(); + } } diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/GlobalLogging.scala b/internal/util-logging/src/main/scala/sbt/internal/util/GlobalLogging.scala index 9be015da7..00c0a67c0 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/GlobalLogging.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/GlobalLogging.scala @@ -83,7 +83,7 @@ object GlobalLogging { ): GlobalLogging = { val loggerName = generateName val log = LoggerContext.globalContext.logger(loggerName, None, None) - val appender = ConsoleAppender(ConsoleAppender.generateName, console) + val appender = ConsoleAppender(ConsoleAppender.generateName(), console) LoggerContext.globalContext.addAppender(loggerName, appender -> Level.Info) GlobalLogging(log, console, appender, GlobalLogBacking(newBackingFile), newAppender) } diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/MainLogging.scala b/internal/util-logging/src/main/scala/sbt/internal/util/MainAppender.scala similarity index 95% rename from internal/util-logging/src/main/scala/sbt/internal/util/MainLogging.scala rename to internal/util-logging/src/main/scala/sbt/internal/util/MainAppender.scala index db5d2e25e..22ad78a9c 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/MainLogging.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/MainAppender.scala @@ -67,15 +67,15 @@ object MainAppender { ) def defaultScreen(console: ConsoleOut): Appender = - ConsoleAppender(ConsoleAppender.generateName, console) + ConsoleAppender(ConsoleAppender.generateName(), console) def defaultScreen( console: ConsoleOut, suppressedMessage: SuppressedTraceContext => Option[String] ): Appender = { ConsoleAppender( - ConsoleAppender.generateName, - Terminal.get, + ConsoleAppender.generateName(), + console, suppressedMessage = suppressedMessage ) } @@ -99,7 +99,7 @@ object MainAppender { def defaultBacked(loggerName: String, useFormat: Boolean): PrintWriter => Appender = to => { ConsoleAppender( - ConsoleAppender.generateName, + ConsoleAppender.generateName(), ConsoleOut.printWriterOut(to), useFormat = useFormat ) diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/ManagedLogger.scala b/internal/util-logging/src/main/scala/sbt/internal/util/ManagedLogger.scala index 21e2bbfa6..3f5f38489 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/ManagedLogger.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/ManagedLogger.scala @@ -50,13 +50,6 @@ class ManagedLogger( } } - @deprecated("Use macro-powered StringTypeTag.fast instead", "1.4.0") - def registerStringCodec[A]( - s: ShowLines[A], - tt: scala.reflect.runtime.universe.TypeTag[A] - ): Unit = { - LogExchange.registerStringCodec[A](s, tt) - } def registerStringCodec[A: ShowLines: StringTypeTag]: Unit = { LogExchange.registerStringCodec[A] } diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/ProgressState.scala b/internal/util-logging/src/main/scala/sbt/internal/util/ProgressState.scala index 5ea305920..817363f8d 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/ProgressState.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/ProgressState.scala @@ -99,8 +99,8 @@ private[sbt] final class ProgressState( addBytes(terminal, bytes) val toWrite = new ArrayBuffer[Byte] terminal.prompt match { - case a: Prompt.AskUser if a.render.nonEmpty && canClearPrompt => toWrite ++= cleanPrompt - case _ => + case a: Prompt.AskUser if a.render().nonEmpty && canClearPrompt => toWrite ++= cleanPrompt + case _ => } val endsWithNewLine = bytes.endsWith(lineSeparatorBytes) if (endsWithNewLine || bytes.containsSlice(lineSeparatorBytes)) { diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala b/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala index 2c2a6434c..9fa61c02e 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala @@ -308,7 +308,7 @@ object Terminal { } private[sbt] lazy val isAnsiSupported: Boolean = logFormatEnabled.getOrElse(useColorDefault) - private[this] val isDumb = "dumb" == System.getenv("TERM") + private[this] val isDumb = Some("dumb") == sys.env.get("TERM") private[this] def isDumbTerminal = isDumb || System.getProperty("jline.terminal", "") == "none" private[this] val hasConsole = Option(java.lang.System.console).isDefined private[this] def useColorDefault: Boolean = { @@ -736,22 +736,20 @@ object Terminal { val supershell: Boolean ) private[sbt] val TERMINAL_PROPS = "SBT_TERMINAL_PROPS" - private val props = System.getenv(TERMINAL_PROPS) match { - case null => None - case p => - p.split(",") match { - case Array(width, height, ansi, color, supershell) => - Try( - new Props( - width.toInt, - height.toInt, - ansi.toBoolean, - color.toBoolean, - supershell.toBoolean - ) - ).toOption - case _ => None - } + private val props = sys.env.get(TERMINAL_PROPS) flatMap { p => + p.split(",") match { + case Array(width, height, ansi, color, supershell) => + Try( + new Props( + width.toInt, + height.toInt, + ansi.toBoolean, + color.toBoolean, + supershell.toBoolean + ) + ).toOption + case _ => None + } } private[sbt] def startedByRemoteClient = props.isDefined @@ -905,7 +903,7 @@ object Terminal { new AtomicReference[((Int, Int), Deadline)](((1, 1), Deadline.now - 1.day)) private[this] def setSize() = size.set((Try(getSizeImpl).getOrElse((1, 1)), Deadline.now)) private[this] def getSize = size.get match { - case (s, d) if (d + sizeRefreshPeriod).isOverdue => + case (s, d) if (d + sizeRefreshPeriod).isOverdue() => setSize() size.get._1 case (s, _) => s diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/WindowsInputStream.scala b/internal/util-logging/src/main/scala/sbt/internal/util/WindowsInputStream.scala index 486759cbb..830dbc2d2 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/WindowsInputStream.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/WindowsInputStream.scala @@ -56,7 +56,7 @@ private[util] class WindowsInputStream(term: org.jline.terminal.Terminal, in: In private val SHIFT_PRESSED = 0x0010; private def getCapability(cap: Capability): String = term.getStringCapability(cap) match { case null => null - case c => c.replaceAllLiterally("\\E", "\u001B") + case c => c.replace("\\E", "\u001B") } /* * This function is a hybrid of jline 2 WindowsTerminal.readConsoleInput diff --git a/internal/util-logging/src/main/scala/sbt/util/LogExchange.scala b/internal/util-logging/src/main/scala/sbt/util/LogExchange.scala index 99ee76192..229c12fde 100644 --- a/internal/util-logging/src/main/scala/sbt/util/LogExchange.scala +++ b/internal/util-logging/src/main/scala/sbt/util/LogExchange.scala @@ -7,15 +7,14 @@ package sbt.util -import java.util.concurrent.ConcurrentHashMap -import org.apache.logging.log4j.{ LogManager => XLogManager, Level => XLevel } -import org.apache.logging.log4j.core.{ Appender => XAppender, LoggerContext => XLoggerContext } -import org.apache.logging.log4j.core.config.{ AppenderRef, LoggerConfig } +import org.apache.logging.log4j.core.config.LoggerConfig import org.apache.logging.log4j.core.layout.PatternLayout +import org.apache.logging.log4j.core.{ LoggerContext => XLoggerContext } +import org.apache.logging.log4j.{ LogManager => XLogManager } import sbt.internal.util._ + +import java.util.concurrent.ConcurrentHashMap import scala.collection.concurrent -import sjsonnew.JsonFormat -import org.apache.logging.log4j.core.appender.AsyncAppender // http://logging.apache.org/log4j/2.x/manual/customconfig.html // https://logging.apache.org/log4j/2.x/log4j-core/apidocs/index.html @@ -29,54 +28,18 @@ sealed abstract class LogExchange { Util.ignoreResult(configs.putIfAbsent(name, config)) private[util] def removeConfig(name: String): Option[LoggerConfig] = Option(configs.remove(name)) - @deprecated("Use LoggerContext to create loggers", "1.4.0") def logger(name: String): ManagedLogger = logger(name, None, None) - @deprecated("Use LoggerContext to create loggers", "1.4.0") def logger(name: String, channelName: Option[String], execId: Option[String]): ManagedLogger = LoggerContext.globalContext.logger(name, channelName, execId) - @deprecated("Use LoggerContext to unbind appenders", "1.4.0") def unbindLoggerAppenders(loggerName: String): Unit = { LoggerContext.globalContext.clearAppenders(loggerName) } - @deprecated("Use LoggerContext to bind appenders", "1.4.0") - def bindLoggerAppenders( - loggerName: String, - appenders: List[(XAppender, Level.Value)] - ): Unit = { - appenders.foreach { - case (a, l) => - LoggerContext.globalContext - .addAppender(loggerName, new ConsoleAppenderFromLog4J(loggerName, a) -> l) - } - } - @deprecated("Use LoggerContext to bind appenders", "1.4.0") def bindLoggerAppenders( loggerName: String, appenders: Seq[(Appender, Level.Value)] - ): Unit = bindLoggerAppenders(loggerName, appenders.map { case (a, l) => a.toLog4J -> l }.toList) - @deprecated("unused", "1.4.0") - def loggerConfig(loggerName: String): LoggerConfig = configs.get(loggerName) - - @deprecated("unused", "1.4.0") - lazy val asyncStdout = buildAsyncStdout - @deprecated("unused", "1.4.0") - private[sbt] def buildAsyncStdout: AsyncAppender = { - val ctx = XLogManager.getContext(false) match { case x: XLoggerContext => x } - val config = ctx.getConfiguration - val appender = ConsoleAppender("Stdout").toLog4J - // CustomConsoleAppender.createAppender("Stdout", layout, null, null) - appender.start - config.addAppender(appender) - val asyncAppender: AsyncAppender = AsyncAppender - .newBuilder() - .setName("AsyncStdout") - .setAppenderRefs(Array(AppenderRef.createAppenderRef("Stdout", XLevel.DEBUG, null))) - .setBlocking(false) - .setConfiguration(config) - .build - asyncAppender.start - config.addAppender(asyncAppender) - asyncAppender + ): Unit = { + appenders.map(LoggerContext.globalContext.addAppender(loggerName, _)) + () } // Construct these StringTypeTags manually, because they're used at the very startup of sbt @@ -87,9 +50,9 @@ sealed abstract class LogExchange { lazy val stringTypeTagSuccessEvent = StringTypeTag[SuccessEvent]("sbt.internal.util.SuccessEvent") private[sbt] def initStringCodecs(): Unit = { + import sbt.internal.util.codec.SuccessEventShowLines._ import sbt.internal.util.codec.ThrowableShowLines._ import sbt.internal.util.codec.TraceEventShowLines._ - import sbt.internal.util.codec.SuccessEventShowLines._ registerStringCodecByStringTypeTag(stringTypeTagThrowable) registerStringCodecByStringTypeTag(stringTypeTagTraceEvent) @@ -111,15 +74,6 @@ sealed abstract class LogExchange { lo } - @deprecated("It is now necessary to provide a json format instance", "1.4.0") - def jsonCodec[A](tag: String): Option[JsonFormat[A]] = None - @deprecated("Always returns false", "1.4.0") - def hasJsonCodec(tag: String): Boolean = false - @deprecated("This is a no-op", "1.4.0") - def getOrElseUpdateJsonCodec[A](tag: String, v: JsonFormat[A]): JsonFormat[A] = v - @deprecated("The log manager no longer caches jsonCodecs", "1.4.0") - def jsonCodecs(): concurrent.Map[String, JsonFormat[_]] = concurrent.TrieMap.empty - def stringCodec[A](tag: String): Option[ShowLines[A]] = stringCodecs.get(tag) map { _.asInstanceOf[ShowLines[A]] } def hasStringCodec(tag: String): Boolean = @@ -127,13 +81,6 @@ sealed abstract class LogExchange { def getOrElseUpdateStringCodec[A](tag: String, v: ShowLines[A]): ShowLines[A] = stringCodecs.getOrElseUpdate(tag, v).asInstanceOf[ShowLines[A]] - @deprecated("Prefer macro based registerStringCodec", "1.4.0") - def registerStringCodec[A]( - st: ShowLines[A], - tt: scala.reflect.runtime.universe.TypeTag[A] - ): Unit = { - registerStringCodecByStringTypeTag(StringTypeTag.apply[A](tt))(st) - } private[sbt] def registerStringCodec[A: ShowLines: StringTypeTag]: Unit = { registerStringCodecByStringTypeTag(implicitly[StringTypeTag[A]]) } @@ -144,8 +91,8 @@ sealed abstract class LogExchange { } private[sbt] def init(): XLoggerContext = { - import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory import org.apache.logging.log4j.core.config.Configurator + import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory val builder = ConfigurationBuilderFactory.newConfigurationBuilder builder.setConfigurationName("sbt.util.logging") val ctx = Configurator.initialize(builder.build()) diff --git a/internal/util-logging/src/main/scala/sbt/util/LoggerContext.scala b/internal/util-logging/src/main/scala/sbt/util/LoggerContext.scala index ee1069061..e040f8bbf 100644 --- a/internal/util-logging/src/main/scala/sbt/util/LoggerContext.scala +++ b/internal/util-logging/src/main/scala/sbt/util/LoggerContext.scala @@ -7,15 +7,11 @@ package sbt.util +import sbt.internal.util._ + import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicBoolean -import org.apache.logging.log4j.{ Level => XLevel } -import org.apache.logging.log4j.core.{ Appender => XAppender, LoggerContext => XLoggerContext } -import org.apache.logging.log4j.core.config.{ AppenderRef, LoggerConfig } -import sbt.internal.util._ import scala.collection.JavaConverters._ -import org.apache.logging.log4j.core.config.AbstractConfiguration -import org.apache.logging.log4j.message.ObjectMessage /** * Provides a context for generating loggers during task evaluation. The logger context @@ -35,83 +31,8 @@ sealed trait LoggerContext extends AutoCloseable { def remove(name: String): Unit } object LoggerContext { - private[this] val useLog4J = System.getProperty("sbt.log.uselog4j", "false") == "true" - private[this] lazy val global = new LoggerContext.LoggerContextImpl - private[this] lazy val globalLog4J = new LoggerContext.Log4JLoggerContext(LogExchange.context) - private[sbt] lazy val globalContext = if (useLog4J) globalLog4J else global - private[util] class Log4JLoggerContext(val xlc: XLoggerContext) extends LoggerContext { - private val config = xlc.getConfiguration match { - case a: AbstractConfiguration => a - case _ => throw new IllegalStateException("") - } - val loggers = new java.util.Vector[String] - private[this] val closed = new AtomicBoolean(false) - override def logger( - name: String, - channelName: Option[String], - execId: Option[String] - ): ManagedLogger = { - if (closed.get) { - throw new IllegalStateException("Tried to create logger for closed LoggerContext") - } - val loggerConfig = LoggerConfig.createLogger( - false, - XLevel.DEBUG, - name, - // disable the calculation of caller location as it is very expensive - // https://issues.apache.org/jira/browse/LOG4J2-153 - "false", - Array[AppenderRef](), - null, - config, - null - ) - config.addLogger(name, loggerConfig) - val logger = xlc.getLogger(name) - LogExchange.addConfig(name, loggerConfig) - loggers.add(name) - val xlogger = new MiniLogger { - def log(level: Level.Value, message: => String): Unit = - logger.log( - ConsoleAppender.toXLevel(level), - new ObjectMessage(StringEvent(level.toString, message, channelName, execId)) - ) - def log[T](level: Level.Value, message: ObjectEvent[T]): Unit = - logger.log(ConsoleAppender.toXLevel(level), new ObjectMessage(message)) - } - new ManagedLogger(name, channelName, execId, xlogger, Some(Terminal.get), this) - } - override def clearAppenders(loggerName: String): Unit = { - val lc = config.getLoggerConfig(loggerName) - lc.getAppenders.asScala foreach { - case (name, a) => - a.stop() - lc.removeAppender(name) - } - } - override def addAppender( - loggerName: String, - appender: (Appender, Level.Value) - ): Unit = { - val lc = config.getLoggerConfig(loggerName) - appender match { - case (x: XAppender, lv) => lc.addAppender(x, ConsoleAppender.toXLevel(lv), null) - case (x, lv) => lc.addAppender(x.toLog4J, ConsoleAppender.toXLevel(lv), null) - } - } - override def appenders(loggerName: String): Seq[Appender] = { - val lc = config.getLoggerConfig(loggerName) - lc.getAppenders.asScala.collect { case (name, ca: ConsoleAppender) => ca }.toVector - } - override def remove(name: String): Unit = { - val lc = config.getLoggerConfig(name) - config.removeLogger(name) - } - def close(): Unit = if (closed.compareAndSet(false, true)) { - loggers.forEach(l => remove(l)) - loggers.clear() - } - } + private[sbt] lazy val globalContext: LoggerContext = new LoggerContext.LoggerContextImpl + private[util] class LoggerContextImpl extends LoggerContext { private class Log extends MiniLogger { private val consoleAppenders: java.util.Vector[(Appender, Level.Value)] = @@ -186,6 +107,5 @@ object LoggerContext { loggers.clear() } } - private[sbt] def apply(useLog4J: Boolean) = - if (useLog4J) new Log4JLoggerContext(LogExchange.context) else new LoggerContextImpl + private[sbt] def apply() = new LoggerContextImpl } diff --git a/internal/util-logging/src/test/scala/Escapes.scala b/internal/util-logging/src/test/scala/Escapes.scala index 82d144d99..af4e1c705 100644 --- a/internal/util-logging/src/test/scala/Escapes.scala +++ b/internal/util-logging/src/test/scala/Escapes.scala @@ -96,7 +96,7 @@ object Escapes extends Properties("Escapes") { ) } assert(isEscapeTerminator(terminator)) - def makeString: String = ESC + content + terminator + def makeString: String = s"$ESC$content$terminator" override def toString = if (content.isEmpty) s"ESC (${terminator.toInt})" diff --git a/internal/util-logging/src/test/scala/LogExchangeSpec.scala b/internal/util-logging/src/test/scala/LogExchangeSpec.scala index 3a26b44ab..7e3e44131 100644 --- a/internal/util-logging/src/test/scala/LogExchangeSpec.scala +++ b/internal/util-logging/src/test/scala/LogExchangeSpec.scala @@ -9,9 +9,10 @@ package sbt.util import sbt.internal.util._ -import org.scalatest._ +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers -class LogExchangeSpec extends FlatSpec with Matchers { +class LogExchangeSpec extends AnyFlatSpec with Matchers { import LogExchange._ checkTypeTag("stringTypeTagThrowable", stringTypeTagThrowable, StringTypeTag.fast[Throwable]) diff --git a/internal/util-logging/src/test/scala/ManagedLoggerSpec.scala b/internal/util-logging/src/test/scala/ManagedLoggerSpec.scala index d8b07e310..d19d61658 100644 --- a/internal/util-logging/src/test/scala/ManagedLoggerSpec.scala +++ b/internal/util-logging/src/test/scala/ManagedLoggerSpec.scala @@ -7,16 +7,18 @@ package sbt.internal.util -import org.scalatest._ +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers import sbt.util._ import java.io.{ File, PrintWriter } import sbt.io.Using import scala.annotation.nowarn -class ManagedLoggerSpec extends FlatSpec with Matchers { - val context = LoggerContext(useLog4J = true) +class ManagedLoggerSpec extends AnyFlatSpec with Matchers { + val context = LoggerContext() @nowarn - val asyncStdout = new ConsoleAppenderFromLog4J("asyncStdout", LogExchange.asyncStdout) + //TODO create a new appender for testing purposes - 3/12/21 + val asyncStdout = ConsoleAppender("asyncStdout") def newLogger(name: String): ManagedLogger = context.logger(name, None, None) "ManagedLogger" should "log to console" in { val log = newLogger("foo") diff --git a/internal/util-logging/src/test/scala/sbt/internal/util/CleanStringSpec.scala b/internal/util-logging/src/test/scala/sbt/internal/util/CleanStringSpec.scala index f734923a6..ee1abdd66 100644 --- a/internal/util-logging/src/test/scala/sbt/internal/util/CleanStringSpec.scala +++ b/internal/util-logging/src/test/scala/sbt/internal/util/CleanStringSpec.scala @@ -7,9 +7,9 @@ package sbt.internal.util -import org.scalatest.FlatSpec +import org.scalatest.flatspec.AnyFlatSpec -class CleanStringSpec extends FlatSpec { +class CleanStringSpec extends AnyFlatSpec { "EscHelpers" should "not modify normal strings" in { val cleanString = s"1234" assert(EscHelpers.stripColorsAndMoves(cleanString) == cleanString) diff --git a/internal/util-logging/src/test/scala/sbt/internal/util/ProgressStateSpec.scala b/internal/util-logging/src/test/scala/sbt/internal/util/ProgressStateSpec.scala index 4c3ae123a..909c64968 100644 --- a/internal/util-logging/src/test/scala/sbt/internal/util/ProgressStateSpec.scala +++ b/internal/util-logging/src/test/scala/sbt/internal/util/ProgressStateSpec.scala @@ -9,12 +9,13 @@ package sbt.internal.util import java.io.{ File, PrintStream } -import org.scalatest.{ BeforeAndAfterAll, FlatSpec } +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.BeforeAndAfterAll import sbt.internal.util.Terminal.SimpleTerminal import scala.io.Source -class ProgressStateSpec extends FlatSpec with BeforeAndAfterAll { +class ProgressStateSpec extends AnyFlatSpec with BeforeAndAfterAll { private lazy val fileIn = new File("/tmp/tmp.txt") private lazy val fileOut = Source.fromFile("/tmp/tmp.txt") diff --git a/internal/util-logging/src/test/scala/sbt/internal/util/UTF8DecoderSpec.scala b/internal/util-logging/src/test/scala/sbt/internal/util/UTF8DecoderSpec.scala index 957a95b4e..06c2e711b 100644 --- a/internal/util-logging/src/test/scala/sbt/internal/util/UTF8DecoderSpec.scala +++ b/internal/util-logging/src/test/scala/sbt/internal/util/UTF8DecoderSpec.scala @@ -9,10 +9,10 @@ package sbt.internal.util import java.io.InputStream import java.nio.charset.Charset -import org.scalatest.FlatSpec +import org.scalatest.flatspec.AnyFlatSpec import java.util.concurrent.LinkedBlockingQueue -class UTF8DecoderSpec extends FlatSpec { +class UTF8DecoderSpec extends AnyFlatSpec { val decoder = Charset.forName("UTF-8").newDecoder "ascii characters" should "not be modified" in { val inputStream = new InputStream { diff --git a/internal/util-position/src/main/scala-2/sbt/internal/util/SourcePositionMacro.scala b/internal/util-position/src/main/scala-2/sbt/internal/util/SourcePositionMacro.scala new file mode 100644 index 000000000..a4a38e744 --- /dev/null +++ b/internal/util-position/src/main/scala-2/sbt/internal/util/SourcePositionMacro.scala @@ -0,0 +1,53 @@ +/* + * sbt + * Copyright 2011 - 2018, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt.internal.util + +import scala.language.experimental.macros +import scala.annotation.tailrec +import scala.reflect.macros.blackbox +import scala.reflect.internal.util.UndefinedPosition + +abstract class SourcePositionImpl { + + /** Creates a SourcePosition by using the enclosing position of the invocation of this method. + * @return SourcePosition + */ + def fromEnclosing(): SourcePosition = macro SourcePositionMacro.fromEnclosingImpl +} + +final class SourcePositionMacro(val c: blackbox.Context) { + import c.universe.{ NoPosition => _, _ } + + def fromEnclosingImpl(): Expr[SourcePosition] = { + val pos = c.enclosingPosition + if (!pos.isInstanceOf[UndefinedPosition] && pos.line >= 0 && pos.source != null) { + val f = pos.source.file + val name = constant[String](ownerSource(f.path, f.name)) + val line = constant[Int](pos.line) + reify { LinePosition(name.splice, line.splice) } + } else + reify { NoPosition } + } + + private[this] def ownerSource(path: String, name: String): String = { + @tailrec def inEmptyPackage(s: Symbol): Boolean = + s != NoSymbol && ( + s.owner == c.mirror.EmptyPackage + || s.owner == c.mirror.EmptyPackageClass + || inEmptyPackage(s.owner) + ) + + c.internal.enclosingOwner match { + case ec if !ec.isStatic => name + case ec if inEmptyPackage(ec) => path + case ec => s"(${ec.fullName}) $name" + } + } + + private[this] def constant[T: WeakTypeTag](t: T): Expr[T] = c.Expr[T](Literal(Constant(t))) +} diff --git a/internal/util-position/src/main/scala-3/sbt/internal/util/SourcePositionMacro.scala b/internal/util-position/src/main/scala-3/sbt/internal/util/SourcePositionMacro.scala new file mode 100644 index 000000000..f6e45f37d --- /dev/null +++ b/internal/util-position/src/main/scala-3/sbt/internal/util/SourcePositionMacro.scala @@ -0,0 +1,34 @@ +/* + * sbt + * Copyright 2011 - 2018, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt.internal.util + +import scala.quoted.{ Expr, Quotes, quotes } + +abstract class SourcePositionImpl { + + /** Creates a SourcePosition by using the enclosing position of the invocation of this method. + * + * @return SourcePosition + */ + inline def fromEnclosing(): SourcePosition = + ${ SourcePositionImpl.fromEnclosingImpl } +} + +object SourcePositionImpl { + + def fromEnclosingImpl(using Quotes): Expr[SourcePosition] = { + val x = quotes.reflect.Position.ofMacroExpansion + + '{ + LinePosition( + path = ${Expr(x.sourceFile.name)}, + startLine = ${Expr(x.startLine + 1)} + ) + } + } +} diff --git a/internal/util-position/src/main/scala/sbt/internal/util/Positions.scala b/internal/util-position/src/main/scala/sbt/internal/util/Positions.scala index c3895a4bd..78deb8e48 100644 --- a/internal/util-position/src/main/scala/sbt/internal/util/Positions.scala +++ b/internal/util-position/src/main/scala/sbt/internal/util/Positions.scala @@ -7,8 +7,6 @@ package sbt.internal.util -import scala.language.experimental.macros - sealed trait SourcePosition sealed trait FilePosition extends SourcePosition { @@ -28,47 +26,4 @@ final case class RangePosition(path: String, range: LineRange) extends FilePosit def startLine = range.start } -object SourcePosition { - - /** Creates a SourcePosition by using the enclosing position of the invocation of this method. - * @return SourcePosition - */ - def fromEnclosing(): SourcePosition = macro SourcePositionMacro.fromEnclosingImpl - -} - -import scala.annotation.tailrec -import scala.reflect.macros.blackbox -import scala.reflect.internal.util.UndefinedPosition - -final class SourcePositionMacro(val c: blackbox.Context) { - import c.universe.{ NoPosition => _, _ } - - def fromEnclosingImpl(): Expr[SourcePosition] = { - val pos = c.enclosingPosition - if (!pos.isInstanceOf[UndefinedPosition] && pos.line >= 0 && pos.source != null) { - val f = pos.source.file - val name = constant[String](ownerSource(f.path, f.name)) - val line = constant[Int](pos.line) - reify { LinePosition(name.splice, line.splice) } - } else - reify { NoPosition } - } - - private[this] def ownerSource(path: String, name: String): String = { - @tailrec def inEmptyPackage(s: Symbol): Boolean = - s != NoSymbol && ( - s.owner == c.mirror.EmptyPackage - || s.owner == c.mirror.EmptyPackageClass - || inEmptyPackage(s.owner) - ) - - c.internal.enclosingOwner match { - case ec if !ec.isStatic => name - case ec if inEmptyPackage(ec) => path - case ec => s"(${ec.fullName}) $name" - } - } - - private[this] def constant[T: WeakTypeTag](t: T): Expr[T] = c.Expr[T](Literal(Constant(t))) -} +object SourcePosition extends SourcePositionImpl diff --git a/internal/util-position/src/test/scala/sbt/internal/util/SourcePositionSpec.scala b/internal/util-position/src/test/scala/sbt/internal/util/SourcePositionSpec.scala index 93ed9fb96..60206dcc6 100644 --- a/internal/util-position/src/test/scala/sbt/internal/util/SourcePositionSpec.scala +++ b/internal/util-position/src/test/scala/sbt/internal/util/SourcePositionSpec.scala @@ -7,9 +7,9 @@ package sbt.internal.util -import org.scalatest._ +import org.scalatest.flatspec.AnyFlatSpec -class SourcePositionSpec extends FlatSpec { +class SourcePositionSpec extends AnyFlatSpec { "SourcePosition()" should "return a sane SourcePosition" in { val filename = "SourcePositionSpec.scala" val lineNumber = 16 diff --git a/internal/util-relation/src/main/scala/sbt/internal/util/Relation.scala b/internal/util-relation/src/main/scala/sbt/internal/util/Relation.scala index dc6c60b97..ce644ac13 100644 --- a/internal/util-relation/src/main/scala/sbt/internal/util/Relation.scala +++ b/internal/util-relation/src/main/scala/sbt/internal/util/Relation.scala @@ -214,5 +214,5 @@ private final class MRelation[A, B](fwd: Map[A, Set[B]], rev: Map[B, Set[A]]) override def hashCode = fwd.filterNot(_._2.isEmpty).hashCode() override def toString = - all.map { case (a, b) => a + " -> " + b }.mkString("Relation [", ", ", "]") + all.map { case (a, b) => s"$a -> $b" }.mkString("Relation [", ", ", "]") } diff --git a/internal/util-scripted/src/main/scala/sbt/internal/scripted/FileCommands.scala b/internal/util-scripted/src/main/scala/sbt/internal/scripted/FileCommands.scala index d9e7a1d42..fcc6707f5 100644 --- a/internal/util-scripted/src/main/scala/sbt/internal/scripted/FileCommands.scala +++ b/internal/util-scripted/src/main/scala/sbt/internal/scripted/FileCommands.scala @@ -13,7 +13,6 @@ import java.io.File import sbt.io.{ IO, Path } import sbt.io.syntax._ import Path._ -import sbt.io.IO class FileCommands(baseDirectory: File) extends BasicStatementHandler { lazy val commands = commandMap @@ -25,7 +24,7 @@ class FileCommands(baseDirectory: File) extends BasicStatementHandler { "mkdir" nonEmpty makeDirectories _, "absent" nonEmpty absent _, // "sync" twoArg("Two directory paths", sync _), - "newer" twoArg ("Two paths", newer _), + "newer".twoArg("Two paths", newer _), "pause" noArg { println("Pausing in " + baseDirectory) /*readLine("Press enter to continue. ") */ @@ -33,11 +32,11 @@ class FileCommands(baseDirectory: File) extends BasicStatementHandler { System.console.readLine println() }, - "sleep" oneArg ("Time in milliseconds", time => Thread.sleep(time.toLong)), + "sleep".oneArg("Time in milliseconds", time => Thread.sleep(time.toLong)), "exec" nonEmpty (execute _), "copy" copy (to => rebase(baseDirectory, to)), - "copy-file" twoArg ("Two paths", copyFile _), - "must-mirror" twoArg ("Two paths", diffFiles _), + "copy-file".twoArg("Two paths", copyFile _), + "must-mirror".twoArg("Two paths", diffFiles _), "copy-flat" copy flat ) diff --git a/internal/util-scripted/src/main/scala/sbt/internal/scripted/ScriptedTests.scala b/internal/util-scripted/src/main/scala/sbt/internal/scripted/ScriptedTests.scala index ffdd4e685..93672518d 100644 --- a/internal/util-scripted/src/main/scala/sbt/internal/scripted/ScriptedTests.scala +++ b/internal/util-scripted/src/main/scala/sbt/internal/scripted/ScriptedTests.scala @@ -25,8 +25,7 @@ object ScriptedRunnerImpl { tests: Array[String], handlersProvider: HandlersProvider ): Unit = { - val context = - LoggerContext(useLog4J = System.getProperty("sbt.log.uselog4j", "false") == "true") + val context = LoggerContext() val runner = new ScriptedTests(resourceBaseDirectory, bufferLog, handlersProvider) val logger = newLogger(context) val allTests = get(tests, resourceBaseDirectory, logger) flatMap { @@ -103,7 +102,7 @@ final class ScriptedTests( log: ManagedLogger, context: LoggerContext, ): Seq[() => Option[String]] = { - for (groupDir <- (resourceBaseDirectory * group).get; nme <- (groupDir * name).get) yield { + for (groupDir <- (resourceBaseDirectory * group).get(); nme <- (groupDir * name).get()) yield { val g = groupDir.getName val n = nme.getName val str = s"$g / $n" diff --git a/launcher-package/build.sbt b/launcher-package/build.sbt index dd9e2558c..b621c7695 100755 --- a/launcher-package/build.sbt +++ b/launcher-package/build.sbt @@ -191,7 +191,7 @@ val root = (project in file(".")). }, // Used to have "openjdk-8-jdk" but that doesn't work on Ubuntu 14.04 https://github.com/sbt/sbt/issues/3105 // before that we had java6-runtime-headless" and that was pulling in JDK9 on Ubuntu 16.04 https://github.com/sbt/sbt/issues/2931 - debianPackageDependencies in Debian ++= Seq("bash (>= 3.2)"), + debianPackageDependencies in Debian ++= Seq("bash (>= 3.2)", "curl | wget"), debianPackageRecommends in Debian += "git", linuxPackageMappings in Debian += { val bd = sourceDirectory.value @@ -273,7 +273,7 @@ val root = (project in file(".")). case (k, BinSbt) => import java.nio.file.{Files, FileSystems} val x = IO.read(k) - IO.write(t / "sbt", x.replaceAllLiterally( + IO.write(t / "sbt", x.replace( "declare init_sbt_version=_to_be_replaced", s"declare init_sbt_version=$sbtVersionToRelease")) diff --git a/launcher-package/integration-test/src/test/scala/ScriptTest.scala b/launcher-package/integration-test/src/test/scala/ScriptTest.scala index 570f0b0dd..e1fe01b5f 100644 --- a/launcher-package/integration-test/src/test/scala/ScriptTest.scala +++ b/launcher-package/integration-test/src/test/scala/ScriptTest.scala @@ -4,7 +4,8 @@ import minitest._ import java.io.File object SbtScriptTest extends SimpleTestSuite with PowerAssertions { - lazy val isWindows: Boolean = sys.props("os.name").toLowerCase(java.util.Locale.ENGLISH).contains("windows") + lazy val isWindows: Boolean = + sys.props("os.name").toLowerCase(java.util.Locale.ENGLISH).contains("windows") lazy val sbtScript = if (isWindows) new File("target/universal/stage/bin/sbt.bat") else new File("target/universal/stage/bin/sbt") @@ -12,12 +13,13 @@ object SbtScriptTest extends SimpleTestSuite with PowerAssertions { private val javaBinDir = new File("integration-test", "bin").getAbsolutePath private def makeTest( - name: String, - javaOpts: String = "", - sbtOpts: String = "", + name: String, + javaOpts: String = "", + sbtOpts: String = "", )(args: String*)(f: List[String] => Any) = { test(name) { - val out = sbtProcessWithOpts(args: _*)(javaOpts = javaOpts, sbtOpts = sbtOpts).!!.linesIterator.toList + val out = + sbtProcessWithOpts(args: _*)(javaOpts = javaOpts, sbtOpts = sbtOpts).!!.linesIterator.toList f(out) () } @@ -26,12 +28,14 @@ object SbtScriptTest extends SimpleTestSuite with PowerAssertions { def sbtProcess(args: String*) = sbtProcessWithOpts(args: _*)("", "") def sbtProcessWithOpts(args: String*)(javaOpts: String, sbtOpts: String) = { val path = sys.env("PATH") - sbt.internal.Process(Seq(sbtScript.getAbsolutePath) ++ args, new File("citest"), + sbt.internal.Process( + Seq(sbtScript.getAbsolutePath) ++ args, + new File("citest"), "JAVA_OPTS" -> javaOpts, "SBT_OPTS" -> sbtOpts, if (isWindows) "JAVACMD" -> new File(javaBinDir, "java.cmd").getAbsolutePath() - else + else "PATH" -> (javaBinDir + File.pathSeparator + path) ) } @@ -48,9 +52,14 @@ object SbtScriptTest extends SimpleTestSuite with PowerAssertions { assert(out.contains[String]("-Dsbt.color=false")) } - makeTest("sbt --no-colors in SBT_OPTS", sbtOpts = "--no-colors")("compile", "-v") { out: List[String] => - if (isWindows) cancel("Test not supported on windows") - assert(out.contains[String]("-Dsbt.log.noformat=true")) + makeTest("sbt --no-colors in SBT_OPTS", sbtOpts = "--no-colors")("compile", "-v") { + out: List[String] => + if (isWindows) cancel("Test not supported on windows") + assert(out.contains[String]("-Dsbt.log.noformat=true")) + } + + makeTest("sbt --no-server")("compile", "--no-server", "-v") { out: List[String] => + assert(out.contains[String]("-Dsbt.server.autostart=false")) } makeTest("sbt --debug-inc")("compile", "--debug-inc", "-v") { out: List[String] => @@ -77,33 +86,43 @@ object SbtScriptTest extends SimpleTestSuite with PowerAssertions { assert(out.contains[String]("-Xmx503m")) } - makeTest("sbt with -mem 503, -Xmx in JAVA_OPTS", javaOpts = "-Xmx1024m")("-mem", "503", "-v") { out: List[String] => - assert(out.contains[String]("-Xmx503m")) - assert(!out.contains[String]("-Xmx1024m")) + makeTest("sbt with -mem 503, -Xmx in JAVA_OPTS", javaOpts = "-Xmx1024m")("-mem", "503", "-v") { + out: List[String] => + assert(out.contains[String]("-Xmx503m")) + assert(!out.contains[String]("-Xmx1024m")) } - makeTest("sbt with -mem 503, -Xmx in SBT_OPTS", sbtOpts = "-Xmx1024m")("-mem", "503", "-v") { out: List[String] => - assert(out.contains[String]("-Xmx503m")) - assert(!out.contains[String]("-Xmx1024m")) + makeTest("sbt with -mem 503, -Xmx in SBT_OPTS", sbtOpts = "-Xmx1024m")("-mem", "503", "-v") { + out: List[String] => + assert(out.contains[String]("-Xmx503m")) + assert(!out.contains[String]("-Xmx1024m")) } - makeTest("sbt with -mem 503, -Xss in JAVA_OPTS", javaOpts = "-Xss6m")("-mem", "503", "-v") { out: List[String] => - assert(out.contains[String]("-Xmx503m")) - assert(!out.contains[String]("-Xss6m")) + makeTest("sbt with -mem 503, -Xss in JAVA_OPTS", javaOpts = "-Xss6m")("-mem", "503", "-v") { + out: List[String] => + assert(out.contains[String]("-Xmx503m")) + assert(!out.contains[String]("-Xss6m")) } - makeTest("sbt with -mem 503, -Xss in SBT_OPTS", sbtOpts = "-Xss6m")("-mem", "503", "-v") { out: List[String] => - assert(out.contains[String]("-Xmx503m")) - assert(!out.contains[String]("-Xss6m")) + makeTest("sbt with -mem 503, -Xss in SBT_OPTS", sbtOpts = "-Xss6m")("-mem", "503", "-v") { + out: List[String] => + assert(out.contains[String]("-Xmx503m")) + assert(!out.contains[String]("-Xss6m")) } - makeTest("sbt with -Xms2048M -Xmx2048M -Xss6M in JAVA_OPTS", javaOpts = "-Xms2048M -Xmx2048M -Xss6M")("-v") { out: List[String] => + makeTest( + "sbt with -Xms2048M -Xmx2048M -Xss6M in JAVA_OPTS", + javaOpts = "-Xms2048M -Xmx2048M -Xss6M" + )("-v") { out: List[String] => assert(out.contains[String]("-Xms2048M")) assert(out.contains[String]("-Xmx2048M")) assert(out.contains[String]("-Xss6M")) } - makeTest("sbt with -Xms2048M -Xmx2048M -Xss6M in SBT_OPTS", sbtOpts = "-Xms2048M -Xmx2048M -Xss6M")( "-v") { out: List[String] => + makeTest( + "sbt with -Xms2048M -Xmx2048M -Xss6M in SBT_OPTS", + sbtOpts = "-Xms2048M -Xmx2048M -Xss6M" + )("-v") { out: List[String] => assert(out.contains[String]("-Xms2048M")) assert(out.contains[String]("-Xmx2048M")) assert(out.contains[String]("-Xss6M")) @@ -125,13 +144,18 @@ object SbtScriptTest extends SimpleTestSuite with PowerAssertions { assert(out.contains[String]("-XX:PermSize=128M")) } - makeTest("sbt with -XX:+UseG1GC -XX:+PrintGC in JAVA_OPTS", javaOpts = "-XX:+UseG1GC -XX:+PrintGC")("-v") { out: List[String] => + makeTest( + "sbt with -XX:+UseG1GC -XX:+PrintGC in JAVA_OPTS", + javaOpts = "-XX:+UseG1GC -XX:+PrintGC" + )("-v") { out: List[String] => assert(out.contains[String]("-XX:+UseG1GC")) assert(out.contains[String]("-XX:+PrintGC")) assert(!out.contains[String]("-XX:+UseG1GC=-XX:+PrintGC")) } - makeTest("sbt with -XX:-UseG1GC -XX:-PrintGC in SBT_OPTS", sbtOpts = "-XX:+UseG1GC -XX:+PrintGC")( "-v") { out: List[String] => + makeTest("sbt with -XX:-UseG1GC -XX:-PrintGC in SBT_OPTS", sbtOpts = "-XX:+UseG1GC -XX:+PrintGC")( + "-v" + ) { out: List[String] => assert(out.contains[String]("-XX:+UseG1GC")) assert(out.contains[String]("-XX:+PrintGC")) assert(!out.contains[String]("-XX:+UseG1GC=-XX:+PrintGC")) @@ -140,7 +164,8 @@ object SbtScriptTest extends SimpleTestSuite with PowerAssertions { test("sbt with -debug in SBT_OPTS appears in sbt commands") { if (isWindows) cancel("Test not supported on windows") - val out: List[String] = sbtProcessWithOpts("compile", "-v")(javaOpts = "", sbtOpts = "-debug").!!.linesIterator.toList + val out: List[String] = + sbtProcessWithOpts("compile", "-v")(javaOpts = "", sbtOpts = "-debug").!!.linesIterator.toList // Debug argument must appear in the 'commands' section (after the sbt-launch.jar argument) to work val sbtLaunchMatcher = """^.+sbt-launch.jar["]{0,1}$""".r val locationOfSbtLaunchJarArg = out.zipWithIndex.collectFirst { @@ -155,7 +180,9 @@ object SbtScriptTest extends SimpleTestSuite with PowerAssertions { } makeTest("sbt --jvm-debug ")("--jvm-debug", "12345", "-v") { out: List[String] => - assert(out.contains[String]("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=12345")) + assert( + out.contains[String]("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=12345") + ) } makeTest("sbt --no-share adds three system properties")("--no-share") { out: List[String] => @@ -171,7 +198,7 @@ object SbtScriptTest extends SimpleTestSuite with PowerAssertions { test("sbt --script-version should print sbtVersion") { val out = sbtProcess("--script-version").!!.trim - val expectedVersion = "^"+SbtRunnerTest.versionRegEx+"$" + val expectedVersion = "^" + SbtRunnerTest.versionRegEx + "$" assert(out.matches(expectedVersion)) () } diff --git a/launcher-package/src/universal/bin/sbt.bat b/launcher-package/src/universal/bin/sbt.bat index 1a59fd9e1..f44af2219 100755 --- a/launcher-package/src/universal/bin/sbt.bat +++ b/launcher-package/src/universal/bin/sbt.bat @@ -25,6 +25,7 @@ set default_java_opts=-Dfile.encoding=UTF-8 set sbt_jar= set build_props_sbt_version= set run_native_client= +set shutdownall= set sbt_args_print_version= set sbt_args_print_sbt_version= @@ -49,6 +50,7 @@ set sbt_args_sbt_dir= set sbt_args_sbt_version= set sbt_args_mem= set sbt_args_client= +set sbt_args_no_server= rem users can set SBT_OPTS via .sbtopts if exist .sbtopts for /F %%A in (.sbtopts) do ( @@ -204,6 +206,15 @@ if defined _no_colors_arg ( goto args_loop ) +if "%~0" == "-no-server" set _no_server_arg=true +if "%~0" == "--no-server" set _no_server_arg=true + +if defined _no_server_arg ( + set _no_server_arg= + set sbt_args_no_server=1 + goto args_loop +) + if "%~0" == "-no-global" set _no_global_arg=true if "%~0" == "--no-global" set _no_global_arg=true @@ -399,6 +410,11 @@ if defined _timings_arg ( goto args_loop ) +if "%~0" == "shutdownall" ( + set shutdownall=1 + goto args_loop +) + if "%~0" == "--script-version" ( set sbt_args_print_sbt_script_version=1 goto args_loop @@ -508,7 +524,7 @@ goto args_loop rem Confirm a user's intent if the current directory does not look like an sbt rem top-level directory and the "new" command was not given. -if not defined sbt_args_sbt_create if not defined sbt_args_print_version if not defined sbt_args_print_sbt_version if not defined sbt_args_print_sbt_script_version if not exist build.sbt ( +if not defined sbt_args_sbt_create if not defined sbt_args_print_version if not defined sbt_args_print_sbt_version if not defined sbt_args_print_sbt_script_version if not defined shutdownall if not exist build.sbt ( if not exist project\ ( if not defined sbt_new ( echo [warn] Neither build.sbt nor a 'project' directory in the current directory: "%CD%" @@ -535,6 +551,16 @@ call :process rem avoid bootstrapping/java version check for script version +if !shutdownall! equ 1 ( + set count=0 + for /f "tokens=1" %%i in ('jps -lv ^| findstr "xsbt.boot.Boot"') do ( + taskkill /F /PID %%i + set /a count=!count!+1 + ) + echo shutdown !count! sbt processes + goto :eof +) + if !sbt_args_print_sbt_script_version! equ 1 ( echo !init_sbt_version! goto :eof @@ -630,6 +656,10 @@ if defined sbt_args_traces ( set _SBT_OPTS=-Dsbt.traces=true !_SBT_OPTS! ) +if defined sbt_args_no_server ( + set _SBT_OPTS=-Dsbt.io.virtual=false -Dsbt.server.autostart=false !_SBT_OPTS! +) + rem TODO: _SBT_OPTS needs to be processed as args and diffed against SBT_ARGS if !sbt_args_print_sbt_version! equ 1 ( diff --git a/launcher-package/src/windows/sbt b/launcher-package/src/windows/sbt index ce24abbdd..c8d63bbd4 100644 --- a/launcher-package/src/windows/sbt +++ b/launcher-package/src/windows/sbt @@ -36,3 +36,4 @@ else # Use Jansi to intercept ANSI sequences "$JAVA_CMD" -Dsbt.log.format=true $JAVA_OPTS $SBT_OPTS -cp "$WDIR/sbt-launch.jar" xsbt.boot.Boot "$@" fi + diff --git a/main-actions/src/main/scala/sbt/ForkTests.scala b/main-actions/src/main/scala/sbt/ForkTests.scala index ddc01a850..ba707d5a2 100755 --- a/main-actions/src/main/scala/sbt/ForkTests.scala +++ b/main-actions/src/main/scala/sbt/ForkTests.scala @@ -18,7 +18,7 @@ import sbt.util.Logger import sbt.ConcurrentRestrictions.Tag import sbt.protocol.testing._ import sbt.internal.util.Util.{ AnyOps, none } -import sbt.internal.util.{ RunningProcesses, Terminal => UTerminal } +import sbt.internal.util.{ Terminal => UTerminal } private[sbt] object ForkTests { def apply( @@ -158,13 +158,7 @@ private[sbt] object ForkTests { classOf[ForkMain].getCanonicalName, server.getLocalPort.toString ) - val p = Fork.java.fork(fork, options) - RunningProcesses.add(p) - val ec = try p.exitValue() - finally { - if (p.isAlive) p.destroy() - RunningProcesses.remove(p) - } + val ec = Fork.java(fork, options) val result = if (ec != 0) TestOutput( @@ -223,7 +217,7 @@ private final class React( listeners.foreach(_ testEvent event) val suiteResult = SuiteResult(tEvents) results += group -> suiteResult - listeners.foreach(_ endGroup (group, suiteResult.result)) + listeners.foreach(_.endGroup(group, suiteResult.result)) react() } } diff --git a/main-actions/src/main/scala/sbt/Package.scala b/main-actions/src/main/scala/sbt/Package.scala index e083cc494..2b4946700 100644 --- a/main-actions/src/main/scala/sbt/Package.scala +++ b/main-actions/src/main/scala/sbt/Package.scala @@ -88,7 +88,7 @@ object Package { for ((key, value) <- mergeManifest.getEntries.asScala) { entryMap.get(key) match { case Some(attributes) => mergeAttributes(attributes, value); () - case None => entryMap put (key, value); () + case None => entryMap.put(key, value); () } } } diff --git a/main-actions/src/main/scala/sbt/RawCompileLike.scala b/main-actions/src/main/scala/sbt/RawCompileLike.scala index 7ac1a12cc..4a81f027b 100644 --- a/main-actions/src/main/scala/sbt/RawCompileLike.scala +++ b/main-actions/src/main/scala/sbt/RawCompileLike.scala @@ -65,7 +65,7 @@ object RawCompileLike { log.debug("Uptodate: " + outputDirectory.getAbsolutePath) } } - cachedComp(inputs)(exists(outputDirectory.allPaths.get.toSet)) + cachedComp(inputs)(exists(outputDirectory.allPaths.get().toSet)) } def prepare(description: String, doCompile: Gen): Gen = diff --git a/main-actions/src/main/scala/sbt/compiler/Eval.scala b/main-actions/src/main/scala/sbt/compiler/Eval.scala index df0386262..6995637f4 100644 --- a/main-actions/src/main/scala/sbt/compiler/Eval.scala +++ b/main-actions/src/main/scala/sbt/compiler/Eval.scala @@ -265,7 +265,7 @@ final class Eval( if (phase == null || phase == phase.next || evalReporter.hasErrors) () else { - enteringPhase(phase) { phase.run } + enteringPhase(phase) { phase.run() } compile(phase.next) } } diff --git a/main-actions/src/test/scala/sbt/CacheIvyTest.scala b/main-actions/src/test/scala/sbt/CacheIvyTest.scala index 17cb778e1..a8f76ec98 100644 --- a/main-actions/src/test/scala/sbt/CacheIvyTest.scala +++ b/main-actions/src/test/scala/sbt/CacheIvyTest.scala @@ -85,7 +85,7 @@ class CacheIvyTest extends Properties("CacheIvy") { for { o <- Gen.identifier n <- Gen.identifier - r <- for { n <- Gen.numChar; ns <- Gen.numStr } yield n + ns + r <- for { n <- Gen.numChar; ns <- Gen.numStr } yield s"$n$ns" cs <- arbitrary[Option[String]] branch <- arbitrary[Option[String]] isChanging <- arbitrary[Boolean] 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(); } } diff --git a/main-command/src/main/scala/sbt/BasicCommands.scala b/main-command/src/main/scala/sbt/BasicCommands.scala index 9a3e49936..06b7599f1 100644 --- a/main-command/src/main/scala/sbt/BasicCommands.scala +++ b/main-command/src/main/scala/sbt/BasicCommands.scala @@ -153,7 +153,7 @@ object BasicCommands { private[this] def completionsParser: Parser[String] = { val notQuoted = (NotQuoted ~ any.*) map { case (nq, s) => nq + s } val quotedOrUnquotedSingleArgument = Space ~> (StringVerbatim | StringEscapable | notQuoted) - token(quotedOrUnquotedSingleArgument ?? "" examples ("", " ")) + token((quotedOrUnquotedSingleArgument ?? "").examples("", " ")) } def runCompletions(state: State)(input: String): State = { @@ -199,7 +199,7 @@ object BasicCommands { val it = s.iterator var fail = false while (it.hasNext && !fail) { - it.next match { + it.next() match { case "" => fail = it.hasNext; () case next => result += next; () } @@ -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) diff --git a/main-command/src/main/scala/sbt/CommandUtil.scala b/main-command/src/main/scala/sbt/CommandUtil.scala index f5f15733c..dc2f52476 100644 --- a/main-command/src/main/scala/sbt/CommandUtil.scala +++ b/main-command/src/main/scala/sbt/CommandUtil.scala @@ -88,10 +88,10 @@ object CommandUtil { } def layoutDetails(details: Map[String, String]): String = - details.map { case (k, v) => k + "\n\n " + v } mkString ("\n", "\n\n", "\n") + details.map { case (k, v) => k + "\n\n " + v }.mkString("\n", "\n\n", "\n") final val HelpPatternFlags = Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE private[sbt] def isSbtBuild(baseDir: File) = - (baseDir / "project").exists() || (baseDir * "*.sbt").get.nonEmpty + (baseDir / "project").exists() || (baseDir * "*.sbt").get().nonEmpty } diff --git a/main-command/src/main/scala/sbt/internal/LegacyWatched.scala b/main-command/src/main/scala/sbt/internal/LegacyWatched.scala index dc9860efd..78a7a25b7 100644 --- a/main-command/src/main/scala/sbt/internal/LegacyWatched.scala +++ b/main-command/src/main/scala/sbt/internal/LegacyWatched.scala @@ -44,7 +44,7 @@ private[sbt] object LegacyWatched { (ClearOnFailure :: next :: FailureWall :: repeat :: s) .put(ContinuousEventMonitor, monitor: EventMonitor) case Some(eventMonitor) => - Watched.printIfDefined(watched watchingMessage eventMonitor.state) + Watched.printIfDefined(watched watchingMessage eventMonitor.state()) @tailrec def impl(): State = { val triggered = try eventMonitor.awaitEvent() catch { @@ -56,7 +56,7 @@ private[sbt] object LegacyWatched { false } if (triggered) { - Watched.printIfDefined(watched triggeredMessage eventMonitor.state) + Watched.printIfDefined(watched triggeredMessage eventMonitor.state()) ClearOnFailure :: next :: FailureWall :: repeat :: s } else if (shouldTerminate) { while (System.in.available() > 0) System.in.read() 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 6f30db31c..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 @@ -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") && - System.getenv("SBTC_AUTO_COMPLETE") == null + !sys.env.contains("SBTN_AUTO_COMPLETE") && !sys.env.contains("SBTC_AUTO_COMPLETE") private def mkSocket(file: File): (Socket, Option[String]) = ClientSocket.socket(file, useJNI) @@ -380,13 +380,13 @@ class NetworkClient( } if (!startServer) { val deadline = 5.seconds.fromNow - while (socket.isEmpty && !deadline.isOverdue) { + while (socket.isEmpty && !deadline.isOverdue()) { socket = Try(ClientSocket.localSocket(bootSocketName, useJNI)).toOption if (socket.isEmpty) Thread.sleep(20) } } - val hook = new Thread(() => Option(sbtProcess.get).foreach(_.destroyForcibly())) - Runtime.getRuntime.addShutdownHook(hook) + val shutdown = new Thread(() => Option(sbtProcess.get).foreach(_.destroyForcibly())) + Runtime.getRuntime.addShutdownHook(shutdown) var gotInputBack = false val readThreadAlive = new AtomicBoolean(true) /* @@ -406,10 +406,13 @@ class NetworkClient( socket.foreach { s => try { s.getInputStream.read match { - case -1 | 0 => readThreadAlive.set(false) - case 2 => gotInputBack = true - case 5 => term.enterRawMode(); startInputThread() - case 3 if gotInputBack => readThreadAlive.set(false) + case -1 | 0 => readThreadAlive.set(false) + case 2 => // STX: start of text + gotInputBack = true + case 5 => // ENQ: enquiry + term.enterRawMode(); startInputThread() + case 3 if gotInputBack => // ETX: end of text + readThreadAlive.set(false) case i if gotInputBack => stdinBytes.offer(i) case i => printStream.write(i) } @@ -441,8 +444,13 @@ class NetworkClient( while (!gotInputBack && !stdinBytes.isEmpty && socket.isDefined) { val out = s.getOutputStream val b = stdinBytes.poll - out.write(b) - out.flush() + if (b == -1) { + // server waits for user input but stinBytes has ended + shutdown.run() + } else { + out.write(b) + out.flush() + } } } process.foreach { p => @@ -483,7 +491,7 @@ class NetworkClient( try blockUntilStart() catch { case t: Throwable => t.printStackTrace() } finally { sbtProcess.set(null) - Util.ignoreResult(Runtime.getRuntime.removeShutdownHook(hook)) + Util.ignoreResult(Runtime.getRuntime.removeShutdownHook(shutdown)) } if (!portfile.exists()) throw new ServerFailedException if (attached.get && !stdinBytes.isEmpty) Option(inputThread.get).foreach(_.drain()) @@ -828,12 +836,12 @@ class NetworkClient( case -1 => (query, query, None, None) // shouldn't happen case i => val rawPrefix = query.substring(0, i) - val prefix = rawPrefix.replaceAllLiterally("\"", "").replaceAllLiterally("\\;", ";") - val rawSuffix = query.substring(i).replaceAllLiterally("\\;", ";") + val prefix = rawPrefix.replace("\"", "").replace("\\;", ";") + val rawSuffix = query.substring(i).replace("\\;", ";") val suffix = if (rawSuffix.length > 1) rawSuffix.substring(1) else "" (rawPrefix, prefix, Some(rawSuffix), Some(suffix)) } - } else (query, query.replaceAllLiterally("\\;", ";"), None, None) + } else (query, query.replace("\\;", ";"), None, None) val tailSpace = query.endsWith(" ") || query.endsWith("\"") val sanitizedQuery = suffix.foldLeft(prefix) { _ + _ } def getCompletions(query: String, sendCommand: Boolean): Seq[String] = { @@ -877,7 +885,7 @@ class NetworkClient( } getCompletions(sanitizedQuery, true) collect { case c if inQuote => c - case c if tailSpace && c.contains(" ") => c.replaceAllLiterally(prefix, "") + case c if tailSpace && c.contains(" ") => c.replace(prefix, "") case c if !tailSpace => c.split(" ").last } } @@ -1098,10 +1106,10 @@ object NetworkClient { launchJar = a .split("--sbt-launch-jar=") .lastOption - .map(_.replaceAllLiterally("%20", " ")) + .map(_.replace("%20", " ")) case "--sbt-launch-jar" if i + 1 < sanitized.length => i += 1 - launchJar = Option(sanitized(i).replaceAllLiterally("%20", " ")) + launchJar = Option(sanitized(i).replace("%20", " ")) case "-bsp" | "--bsp" => bsp = true case a if !a.startsWith("-") => commandArgs += a case a @ SysProp(key, value) => @@ -1123,7 +1131,7 @@ object NetworkClient { sbtArguments.toSeq, commandArgs.toSeq, completionArguments.toSeq, - sbtScript.getOrElse(defaultSbtScript).replaceAllLiterally("%20", " "), + sbtScript.getOrElse(defaultSbtScript).replace("%20", " "), bsp, launchJar ) diff --git a/main-command/src/main/scala/sbt/internal/client/ServerConnection.scala b/main-command/src/main/scala/sbt/internal/client/ServerConnection.scala index 23e8837af..777593caa 100644 --- a/main-command/src/main/scala/sbt/internal/client/ServerConnection.scala +++ b/main-command/src/main/scala/sbt/internal/client/ServerConnection.scala @@ -79,7 +79,7 @@ abstract class ServerConnection(connection: Socket) { if (a.nonEmpty) { out.write(a) } - writeEndLine + writeEndLine() } catch { case e: IOException => shutdown() @@ -100,7 +100,7 @@ abstract class ServerConnection(connection: Socket) { out.close() connection.close() } catch { case e: IOException => e.printStackTrace() } - onShutdown + onShutdown() } } diff --git a/main-command/src/main/scala/sbt/internal/ui/UITask.scala b/main-command/src/main/scala/sbt/internal/ui/UITask.scala index 1ed500047..5c27fc873 100644 --- a/main-command/src/main/scala/sbt/internal/ui/UITask.scala +++ b/main-command/src/main/scala/sbt/internal/ui/UITask.scala @@ -75,7 +75,7 @@ private[sbt] object UITask { this.synchronized(this.wait()) Right("") // should be unreachable // JLine returns null on ctrl+d when there is no other input. This interprets - // ctrl+d with no imput as an exit + // ctrl+d with no input as an exit case None => Left(TerminateAction) case Some(s: String) => s.trim() match { diff --git a/main-command/src/main/scala/sbt/internal/util/JoinThread.scala b/main-command/src/main/scala/sbt/internal/util/JoinThread.scala index 4c3d109f4..c955e9993 100644 --- a/main-command/src/main/scala/sbt/internal/util/JoinThread.scala +++ b/main-command/src/main/scala/sbt/internal/util/JoinThread.scala @@ -20,7 +20,7 @@ object JoinThread { t.interrupt() t.join(10) } catch { case e: InterruptedException => } - if (t.isAlive && !deadline.isOverdue) impl() + if (t.isAlive && !deadline.isOverdue()) impl() } impl() if (t.isAlive) { diff --git a/main-command/src/test/scala/sbt/MultiParserSpec.scala b/main-command/src/test/scala/sbt/MultiParserSpec.scala index 88aea432d..2e8b54584 100644 --- a/main-command/src/test/scala/sbt/MultiParserSpec.scala +++ b/main-command/src/test/scala/sbt/MultiParserSpec.scala @@ -8,7 +8,7 @@ package sbt import scala.concurrent.duration._ -import org.scalatest.FlatSpec +import org.scalatest.flatspec.AnyFlatSpec import sbt.internal.util.complete.Parser object MultiParserSpec { @@ -22,7 +22,7 @@ object MultiParserSpec { } } import sbt.MultiParserSpec._ -class MultiParserSpec extends FlatSpec { +class MultiParserSpec extends AnyFlatSpec { "parsing" should "parse single commands" in { assert(";foo".parse == Seq("foo")) assert("; foo".parse == Seq("foo")) diff --git a/main-command/src/test/scala/sbt/internal/ClassLoaderCacheTest.scala b/main-command/src/test/scala/sbt/internal/ClassLoaderCacheTest.scala index eec114f40..089bbd034 100644 --- a/main-command/src/test/scala/sbt/internal/ClassLoaderCacheTest.scala +++ b/main-command/src/test/scala/sbt/internal/ClassLoaderCacheTest.scala @@ -10,7 +10,8 @@ package sbt.internal import java.io.File import java.nio.file.Files -import org.scalatest.{ FlatSpec, Matchers } +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers import sbt.internal.classpath.ClassLoaderCache import sbt.io.IO @@ -19,7 +20,7 @@ object ClassLoaderCacheTest { def get(classpath: Seq[File]): ClassLoader = c(classpath.toList) } } -class ClassLoaderCacheTest extends FlatSpec with Matchers { +class ClassLoaderCacheTest extends AnyFlatSpec with Matchers { import ClassLoaderCacheTest._ private def withCache[R](f: ClassLoaderCache => R): R = { val cache = new ClassLoaderCache(ClassLoader.getSystemClassLoader) diff --git a/main-settings/src/main/scala/sbt/Def.scala b/main-settings/src/main/scala/sbt/Def.scala index df78134ea..13ccec44e 100644 --- a/main-settings/src/main/scala/sbt/Def.scala +++ b/main-settings/src/main/scala/sbt/Def.scala @@ -252,7 +252,7 @@ object Def extends Init[Scope] with TaskMacroExtra with InitializeImplicits { )(l: Def.Initialize[Task[A => C]])(r: Def.Initialize[Task[B => C]]): Def.Initialize[Task[C]] = { val lhs = { val innerLhs: Def.Initialize[Task[Either[A, Either[B, C]]]] = - x.map((fab: Either[A, B]) => fab.right.map(Left(_))) + x.map((fab: Either[A, B]) => fab.map(Left(_))) val innerRhs: Def.Initialize[Task[A => Either[B, C]]] = l.map((fn: A => C) => fn.andThen(Right(_))) selectITask(innerLhs, innerRhs) diff --git a/main-settings/src/main/scala/sbt/Remove.scala b/main-settings/src/main/scala/sbt/Remove.scala index e09555f5b..6ab53e42b 100644 --- a/main-settings/src/main/scala/sbt/Remove.scala +++ b/main-settings/src/main/scala/sbt/Remove.scala @@ -34,4 +34,14 @@ object Remove { def removeValue(a: Seq[T], b: Option[T]): Seq[T] = b.fold(a)(a filterNot _.==) def removeValues(a: Seq[T], b: Option[T]): Seq[T] = b.fold(a)(a filterNot _.==) } + implicit def removeSet[T, V <: T]: Sequence[Set[T], Set[V], V] = + new Sequence[Set[T], Set[V], V] { + def removeValue(a: Set[T], b: V): Set[T] = a - b + def removeValues(a: Set[T], b: Set[V]): Set[T] = a diff (b.toSeq: Seq[T]).toSet + } + implicit def removeMap[A, B, X <: A]: Sequence[Map[A, B], Seq[X], X] = + new Sequence[Map[A, B], Seq[X], X] { + def removeValue(a: Map[A, B], b: X): Map[A, B] = a - b + def removeValues(a: Map[A, B], b: Seq[X]): Map[A, B] = a -- b + } } diff --git a/main-settings/src/test/scala/sbt/ScopeDisplaySpec.scala b/main-settings/src/test/scala/sbt/ScopeDisplaySpec.scala index c1af590b4..e38fecdff 100644 --- a/main-settings/src/test/scala/sbt/ScopeDisplaySpec.scala +++ b/main-settings/src/test/scala/sbt/ScopeDisplaySpec.scala @@ -7,12 +7,12 @@ package sbt -import org.scalatest.FlatSpec +import org.scalatest.flatspec.AnyFlatSpec import sbt.internal.util.{ AttributeKey, AttributeMap } import sbt.io.syntax.file import scala.annotation.nowarn -class ScopeDisplaySpec extends FlatSpec { +class ScopeDisplaySpec extends AnyFlatSpec { val project = ProjectRef(file("foo/bar"), "bar") val mangledName = "bar_slash_blah_blah_blah" diff --git a/main-settings/src/test/scala/sbt/std/TaskConfigSpec.scala b/main-settings/src/test/scala/sbt/std/TaskConfigSpec.scala index e43b655a6..210284215 100644 --- a/main-settings/src/test/scala/sbt/std/TaskConfigSpec.scala +++ b/main-settings/src/test/scala/sbt/std/TaskConfigSpec.scala @@ -7,12 +7,12 @@ package sbt.std -import org.scalatest.{ TestData, fixture } +import org.scalatest.{ TestData, fixture, funsuite } import sbt.std.TestUtil._ import scala.tools.reflect.{ FrontEnd, ToolBoxError } -class TaskConfigSpec extends fixture.FunSuite with fixture.TestDataFixture { +class TaskConfigSpec extends funsuite.FixtureAnyFunSuite with fixture.TestDataFixture { private def expectError( errorSnippet: String, compileOptions: String = "", diff --git a/main-settings/src/test/scala/sbt/std/UsageTest.scala b/main-settings/src/test/scala/sbt/std/UsageTest.scala index 154bec498..ec1f2149e 100644 --- a/main-settings/src/test/scala/sbt/std/UsageTest.scala +++ b/main-settings/src/test/scala/sbt/std/UsageTest.scala @@ -69,7 +69,7 @@ object Assign { val is = Seq( mk := 3, name := "asdf", - tk := (math.random * 1000).toInt, + tk := (math.random() * 1000).toInt, isk := dummys.value.parsed // should not compile: cannot use a task to define the parser // ik := { if( tsk.parsed.value == "blue") tk.value else mk.value } ) diff --git a/main-settings/src/test/scala/sbt/std/neg/TaskNegSpec.scala b/main-settings/src/test/scala/sbt/std/neg/TaskNegSpec.scala index 0e08b5e96..e2d214657 100644 --- a/main-settings/src/test/scala/sbt/std/neg/TaskNegSpec.scala +++ b/main-settings/src/test/scala/sbt/std/neg/TaskNegSpec.scala @@ -8,11 +8,11 @@ package sbt.std.neg import scala.tools.reflect.ToolBoxError -import org.scalatest.{ TestData, fixture } +import org.scalatest.{ TestData, fixture, funsuite } import sbt.std.{ TaskLinterDSLFeedback, TestUtil } import sbt.std.TestUtil._ -class TaskNegSpec extends fixture.FunSuite with fixture.TestDataFixture { +class TaskNegSpec extends funsuite.FixtureAnyFunSuite with fixture.TestDataFixture { def expectError( errorSnippet: String, compileOptions: String = "", diff --git a/main/src/main/scala/sbt/BackgroundJobService.scala b/main/src/main/scala/sbt/BackgroundJobService.scala index 1cbff3f2d..8faad7728 100644 --- a/main/src/main/scala/sbt/BackgroundJobService.scala +++ b/main/src/main/scala/sbt/BackgroundJobService.scala @@ -44,7 +44,7 @@ abstract class BackgroundJobService extends Closeable { start(logger, file)._2.apply() } - /** Same as shutown. */ + /** Same as shutdown. */ def close(): Unit /** Shuts down all background jobs. */ diff --git a/main/src/main/scala/sbt/BuildPaths.scala b/main/src/main/scala/sbt/BuildPaths.scala index 064f50107..87bfb7173 100644 --- a/main/src/main/scala/sbt/BuildPaths.scala +++ b/main/src/main/scala/sbt/BuildPaths.scala @@ -116,7 +116,8 @@ object BuildPaths { private[this] def defaultGlobalZinc(globalBase: File) = globalBase / "zinc" def configurationSources(base: File): Seq[File] = - (base * (GlobFilter("*.sbt") - ".sbt")).get + (base * (GlobFilter("*.sbt") - ".sbt")) + .get() .sortBy(_.getName.toLowerCase(Locale.ENGLISH)) def pluginDirectory(definitionBase: File) = definitionBase / PluginsDirectoryName diff --git a/main/src/main/scala/sbt/Cross.scala b/main/src/main/scala/sbt/Cross.scala index 8bb001d04..9966c5eb9 100644 --- a/main/src/main/scala/sbt/Cross.scala +++ b/main/src/main/scala/sbt/Cross.scala @@ -8,7 +8,7 @@ package sbt import java.io.File - +import java.util.regex.Pattern import sbt.Def.{ ScopedKey, Setting } import sbt.Keys._ import sbt.SlashSyntax0._ @@ -284,8 +284,8 @@ object Cross { } def logSwitchInfo( - included: Seq[(ProjectRef, Seq[ScalaVersion])], - excluded: Seq[(ProjectRef, Seq[ScalaVersion])] + included: Seq[(ResolvedReference, ScalaVersion, Seq[ScalaVersion])], + excluded: Seq[(ResolvedReference, Seq[ScalaVersion])] ) = { instance.foreach { @@ -304,56 +304,96 @@ object Cross { def detailedLog(msg: => String) = if (switch.verbose) state.log.info(msg) else state.log.debug(msg) - def logProject: (ProjectRef, Seq[ScalaVersion]) => Unit = (proj, scalaVersions) => { - val current = if (proj == currentRef) "*" else " " - detailedLog(s" $current ${proj.project} ${scalaVersions.mkString("(", ", ", ")")}") + def logProject: (ResolvedReference, Seq[ScalaVersion]) => Unit = (ref, scalaVersions) => { + val current = if (ref == currentRef) "*" else " " + ref match { + case proj: ProjectRef => + detailedLog(s" $current ${proj.project} ${scalaVersions.mkString("(", ", ", ")")}") + case _ => // don't log BuildRefs + } } detailedLog("Switching Scala version on:") - included.foreach(logProject.tupled) + included.foreach { case (project, _, versions) => logProject(project, versions) } detailedLog("Excluding projects:") excluded.foreach(logProject.tupled) } - val projects: Seq[(ResolvedReference, Seq[ScalaVersion])] = { + val projects: Seq[(ResolvedReference, Option[ScalaVersion], Seq[ScalaVersion])] = { val projectScalaVersions = structure.allProjectRefs.map(proj => proj -> crossVersions(extracted, proj)) if (switch.version.force) { - logSwitchInfo(projectScalaVersions, Nil) - projectScalaVersions ++ structure.units.keys + projectScalaVersions.map { + case (ref, options) => (ref, Some(version), options) + } ++ structure.units.keys .map(BuildRef.apply) - .map(proj => proj -> crossVersions(extracted, proj)) + .map(proj => (proj, Some(version), crossVersions(extracted, proj))) + } else if (version.contains('*')) { + projectScalaVersions.map { + case (project, scalaVersions) => + globFilter(version, scalaVersions) match { + case Nil => (project, None, scalaVersions) + case Seq(version) => (project, Some(version), scalaVersions) + case multiple => + sys.error( + s"Multiple crossScalaVersions matched query '$version': ${multiple.mkString(", ")}" + ) + } + } } else { val binaryVersion = CrossVersion.binaryScalaVersion(version) - - val (included, excluded) = projectScalaVersions.partition { - case (_, scalaVersions) => - scalaVersions.exists(v => CrossVersion.binaryScalaVersion(v) == binaryVersion) + projectScalaVersions.map { + case (project, scalaVersions) => + if (scalaVersions.exists(v => CrossVersion.binaryScalaVersion(v) == binaryVersion)) + (project, Some(version), scalaVersions) + else + (project, None, scalaVersions) } - if (included.isEmpty) { - sys.error( - s"""Switch failed: no subprojects list "$version" (or compatible version) in crossScalaVersions setting. - |If you want to force it regardless, call ++ $version!""".stripMargin - ) - } - logSwitchInfo(included, excluded) - included } } - (setScalaVersionForProjects(version, instance, projects, state, extracted), projects.map(_._1)) + val included = projects.collect { + case (project, Some(version), scalaVersions) => (project, version, scalaVersions) + } + val excluded = projects.collect { + case (project, None, scalaVersions) => (project, scalaVersions) + } + + if (included.isEmpty) { + sys.error( + s"""Switch failed: no subprojects list "$version" (or compatible version) in crossScalaVersions setting. + |If you want to force it regardless, call ++ $version!""".stripMargin + ) + } + + logSwitchInfo(included, excluded) + + (setScalaVersionsForProjects(instance, included, state, extracted), included.map(_._1)) } - private def setScalaVersionForProjects( - version: String, + def globFilter(pattern: String, candidates: Seq[String]): Seq[String] = { + def createGlobRegex(remainingPattern: String): String = + remainingPattern.indexOf("*") match { + case -1 => Pattern.quote(remainingPattern) + case n => + val chunk = Pattern.quote(remainingPattern.substring(0, n)) + ".*" + if (remainingPattern.length > n) + chunk + createGlobRegex(remainingPattern.substring(n + 1)) + else chunk + } + val compiledPattern = Pattern.compile(createGlobRegex(pattern)) + candidates.filter(compiledPattern.matcher(_).matches()) + } + + private def setScalaVersionsForProjects( instance: Option[(File, ScalaInstance)], - projects: Seq[(ResolvedReference, Seq[String])], + projects: Seq[(ResolvedReference, String, Seq[String])], state: State, extracted: Extracted ): State = { import extracted._ val newSettings = projects.flatMap { - case (project, scalaVersions) => + case (project, version, scalaVersions) => val scope = Scope(Select(project), Zero, Zero, Zero) instance match { diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 58217442b..f90f91de3 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, @@ -148,6 +148,7 @@ object Defaults extends BuildCommon { val m = (for (a <- cp; an <- a.metadata get Keys.analysis) yield (a.data, an)).toMap m.get _ } + private[sbt] def globalDefaults(ss: Seq[Setting[_]]): Seq[Setting[_]] = Def.defaultSettings(inScope(GlobalScope)(ss)) @@ -229,7 +230,7 @@ object Defaults extends BuildCommon { private[sbt] lazy val globalIvyCore: Seq[Setting[_]] = Seq( internalConfigurationMap :== Configurations.internalMap _, - credentials :== Nil, + credentials :== SysProp.sbtCredentialsEnv.toList, exportJars :== false, trackInternalDependencies :== TrackLevel.TrackAlways, exportToInternal :== TrackLevel.TrackAlways, @@ -257,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, @@ -1394,11 +1393,10 @@ object Defaults extends BuildCommon { val x = { import analysis.{ apis, relations => rel } rel.internalClassDeps(c).map(intlStamp(_, analysis, s + c)) ++ - rel.externalDeps(c).map(stamp) + - (apis.internal.get(c) match { - case Some(x) => x.compilationTimestamp - case _ => Long.MinValue - }) + rel.externalDeps(c).map(stamp) ++ + rel.productClassName.reverse(c).flatMap { pc => + apis.internal.get(pc).map(_.compilationTimestamp) + } + Long.MinValue }.max if (x != Long.MinValue) { stamps(c) = x @@ -2341,7 +2339,7 @@ object Defaults extends BuildCommon { s: TaskStreams, ci: Inputs, promise: PromiseWrap[Boolean], - reporter: BuildServerReporter + reporter: BuildServerReporter, ): CompileResult = { lazy val x = s.text(ExportStream) def onArgs(cs: Compilers) = { @@ -2634,7 +2632,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) } } @@ -3058,7 +3056,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 @@ -3197,8 +3195,8 @@ object Classpaths { update / unresolvedWarningConfiguration := UnresolvedWarningConfiguration( dependencyPositions.value ), - updateFull := (updateTask tag (Tags.Update, Tags.Network)).value, - update := (updateWithoutDetails("update") tag (Tags.Update, Tags.Network)).value, + updateFull := (updateTask.tag(Tags.Update, Tags.Network)).value, + update := (updateWithoutDetails("update").tag(Tags.Update, Tags.Network)).value, update := { val report = update.value val log = streams.value.log @@ -3209,7 +3207,7 @@ object Classpaths { evicted / evictionWarningOptions := EvictionWarningOptions.full, evicted := { import ShowLines._ - val report = (updateTask tag (Tags.Update, Tags.Network)).value + val report = (updateTask.tag(Tags.Update, Tags.Network)).value val log = streams.value.log val ew = EvictionWarning(ivyModule.value, (evicted / evictionWarningOptions).value, report) @@ -3361,7 +3359,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 @@ -3701,7 +3699,7 @@ object Classpaths { try { val extracted = (Project extract st) val sk = (projRef / Zero / Zero / libraryDependencies).scopedKey - val empty = extracted.structure.data set (sk.scope, sk.key, Nil) + val empty = extracted.structure.data.set(sk.scope, sk.key, Nil) val settings = extracted.structure.settings filter { s: Setting[_] => (s.key.key == libraryDependencies.key) && (s.key.scope.project == Select(projRef)) @@ -4318,7 +4316,7 @@ trait BuildExtra extends BuildCommon with DefExtra { /** Constructs a setting that declares a new artifact `a` that is generated by `taskDef`. */ def addArtifact(a: Artifact, taskDef: TaskKey[File]): SettingsDefinition = { - val pkgd = packagedArtifacts := packagedArtifacts.value updated (a, taskDef.value) + val pkgd = packagedArtifacts := packagedArtifacts.value.updated(a, taskDef.value) Seq(artifacts += a, pkgd) } @@ -4330,7 +4328,7 @@ trait BuildExtra extends BuildCommon with DefExtra { val artLocal = SettingKey.local[Artifact] val taskLocal = TaskKey.local[File] val art = artifacts := artLocal.value +: artifacts.value - val pkgd = packagedArtifacts := packagedArtifacts.value updated (artLocal.value, taskLocal.value) + val pkgd = packagedArtifacts := packagedArtifacts.value.updated(artLocal.value, taskLocal.value) Seq(artLocal := artifact.value, taskLocal := taskDef.value, art, pkgd) } @@ -4380,7 +4378,7 @@ trait BuildExtra extends BuildCommon with DefExtra { } @deprecated( - "externalIvyFile is not supported by Couriser, and will be removed in the future", + "externalIvyFile is not supported by Coursier, and will be removed in the future", since = "1.5.0" ) def externalIvyFile( diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 1a248b1df..392812271 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -396,6 +396,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?") @@ -418,8 +419,13 @@ 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[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/Main.scala b/main/src/main/scala/sbt/Main.scala index 6bb147f7d..4d30a9920 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -329,7 +329,6 @@ object BuiltinCommands { startServer, eval, last, - oldLastGrep, lastGrep, export, boot, @@ -626,12 +625,6 @@ object BuiltinCommands { s } - @deprecated("Use `lastGrep` instead.", "1.2.0") - def oldLastGrep: Command = - lastGrepCommand(OldLastGrepCommand, oldLastGrepBrief, oldLastGrepDetailed, { s => - lastGrepParser(s) - }) - def lastGrep: Command = lastGrepCommand(LastGrepCommand, lastGrepBrief, lastGrepDetailed, lastGrepParser) @@ -643,9 +636,6 @@ object BuiltinCommands { ): Command = Command(name, briefHelp, detail)(parser) { (s: State, sks: (String, Option[AnyKeys])) => { - if (name == OldLastGrepCommand) - s.log.warn(deprecationWarningText(OldLastGrepCommand, LastGrepCommand)) - (s, sks) match { case (s, (pattern, Some(sks))) => val (str, _, display) = extractLast(s) diff --git a/main/src/main/scala/sbt/MainLoop.scala b/main/src/main/scala/sbt/MainLoop.scala index 351be3700..0a051ffbd 100644 --- a/main/src/main/scala/sbt/MainLoop.scala +++ b/main/src/main/scala/sbt/MainLoop.scala @@ -7,26 +7,22 @@ package sbt -import java.io.PrintWriter -import java.util.concurrent.RejectedExecutionException -import java.util.Properties - import sbt.BasicCommandStrings.{ StashOnFailure, networkExecPrefix } -import sbt.internal.ShutdownHooks import sbt.internal.langserver.ErrorCodes -import sbt.internal.protocol.JsonRpcResponseError import sbt.internal.nio.CheckBuildSources.CheckBuildSourcesKey +import sbt.internal.protocol.JsonRpcResponseError import sbt.internal.util.{ ErrorHandling, GlobalLogBacking, Prompt, Terminal => ITerminal } -import sbt.internal.{ ShutdownHooks, TaskProgress } +import sbt.internal.{ FastTrackCommands, ShutdownHooks, SysProp, TaskProgress } import sbt.io.{ IO, Using } import sbt.protocol._ import sbt.util.{ Logger, LoggerContext } +import java.io.PrintWriter +import java.util.Properties +import java.util.concurrent.RejectedExecutionException import scala.annotation.tailrec import scala.concurrent.duration._ import scala.util.control.NonFatal -import sbt.internal.FastTrackCommands -import sbt.internal.SysProp object MainLoop { @@ -148,7 +144,7 @@ object MainLoop { } def next(state: State): State = { - val context = LoggerContext(useLog4J = state.get(Keys.useLog4J.key).getOrElse(false)) + val context = LoggerContext() val superShellSleep = state.get(Keys.superShellSleep.key).getOrElse(SysProp.supershellSleep.millis) val superShellThreshold = 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/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/ScriptedPlugin.scala b/main/src/main/scala/sbt/ScriptedPlugin.scala index f4822c3fb..35deb1104 100644 --- a/main/src/main/scala/sbt/ScriptedPlugin.scala +++ b/main/src/main/scala/sbt/ScriptedPlugin.scala @@ -8,7 +8,6 @@ package sbt import java.io.File -import java.lang.reflect.Method import sbt.Def._ import sbt.Keys._ @@ -47,7 +46,7 @@ object ScriptedPlugin extends AutoPlugin { val scriptedParallelInstances = settingKey[Int]( "Configures the number of scripted instances for parallel testing, only used in batch mode." ) - val scriptedRun = taskKey[Method]("") + val scriptedRun = taskKey[ScriptedRun]("") val scriptedLaunchOpts = settingKey[Seq[String]]("options to pass to jvm launching scripted tasks") val scriptedDependencies = taskKey[Unit]("") @@ -114,21 +113,8 @@ object ScriptedPlugin extends AutoPlugin { } } - private[sbt] def scriptedRunTask: Initialize[Task[Method]] = Def.taskDyn { - val fCls = classOf[File] - val bCls = classOf[Boolean] - val asCls = classOf[Array[String]] - val lfCls = classOf[java.util.List[File]] - val iCls = classOf[Int] - - val clazz = scriptedTests.value.getClass - val method = - if (scriptedBatchExecution.value) - clazz.getMethod("runInParallel", fCls, bCls, asCls, fCls, asCls, lfCls, iCls) - else - clazz.getMethod("run", fCls, bCls, asCls, fCls, asCls, lfCls) - - Def.task(method) + private[sbt] def scriptedRunTask: Initialize[Task[ScriptedRun]] = Def.task { + ScriptedRun.of(scriptedTests.value, scriptedBatchExecution.value) } private[sbt] final case class ScriptedTestPage(page: Int, total: Int) @@ -191,21 +177,16 @@ object ScriptedPlugin extends AutoPlugin { private[sbt] def scriptedTask: Initialize[InputTask[Unit]] = Def.inputTask { val args = scriptedParser(sbtTestDirectory.value).parsed Def.unit(scriptedDependencies.value) - try { - val method = scriptedRun.value - val scriptedInstance = scriptedTests.value - val dir = sbtTestDirectory.value - val log = Boolean box scriptedBufferLog.value - val launcher = sbtLauncher.value - val opts = scriptedLaunchOpts.value.toArray - val empty = new java.util.ArrayList[File]() - val instances = Int box scriptedParallelInstances.value - - if (scriptedBatchExecution.value) - method.invoke(scriptedInstance, dir, log, args.toArray, launcher, opts, empty, instances) - else method.invoke(scriptedInstance, dir, log, args.toArray, launcher, opts, empty) - () - } catch { case e: java.lang.reflect.InvocationTargetException => throw e.getCause } + scriptedRun.value.run( + sbtTestDirectory.value, + scriptedBufferLog.value, + args, + sbtLauncher.value, + Fork.javaCommand((scripted / javaHome).value, "java").getAbsolutePath, + scriptedLaunchOpts.value, + new java.util.ArrayList[File](), + scriptedParallelInstances.value + ) } private[this] def getJars(config: Configuration): Initialize[Task[PathFinder]] = Def.task { diff --git a/main/src/main/scala/sbt/ScriptedRun.scala b/main/src/main/scala/sbt/ScriptedRun.scala new file mode 100644 index 000000000..ea7d7054c --- /dev/null +++ b/main/src/main/scala/sbt/ScriptedRun.scala @@ -0,0 +1,179 @@ +/* + * sbt + * Copyright 2011 - 2018, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt + +import java.io.File +import java.lang.reflect.Method +import scala.annotation.unused + +sealed trait ScriptedRun { + final def run( + resourceBaseDirectory: File, + bufferLog: Boolean, + tests: Seq[String], + launcherJar: File, + javaCommand: String, + launchOpts: Seq[String], + prescripted: java.util.List[File], + instances: Int, + ): Unit = { + try { + invoke( + resourceBaseDirectory, + bufferLog, + tests.toArray, + launcherJar, + javaCommand, + launchOpts.toArray, + prescripted, + instances, + ) + () + } catch { case e: java.lang.reflect.InvocationTargetException => throw e.getCause } + } + + protected def invoke( + resourceBaseDirectory: File, + bufferLog: java.lang.Boolean, + tests: Array[String], + launcherJar: File, + javaCommand: String, + launchOpts: Array[String], + prescripted: java.util.List[File], + instances: java.lang.Integer, + ): AnyRef + +} + +object ScriptedRun { + + def of(scriptedTests: AnyRef, batchExecution: Boolean): ScriptedRun = { + val fCls = classOf[File] + val bCls = classOf[Boolean] + val asCls = classOf[Array[String]] + val sCls = classOf[String] + val lfCls = classOf[java.util.List[File]] + val iCls = classOf[Int] + + val clazz = scriptedTests.getClass + if (batchExecution) + try new RunInParallelV2( + scriptedTests, + clazz.getMethod("runInParallel", fCls, bCls, asCls, fCls, sCls, asCls, lfCls, iCls) + ) + catch { + case _: NoSuchMethodException => + new RunInParallelV1( + scriptedTests, + clazz.getMethod("runInParallel", fCls, bCls, asCls, fCls, asCls, lfCls, iCls) + ) + } + else + try new RunV2( + scriptedTests, + clazz.getMethod("run", fCls, bCls, asCls, fCls, sCls, asCls, lfCls) + ) + catch { + case _: NoSuchMethodException => + new RunV1(scriptedTests, clazz.getMethod("run", fCls, bCls, asCls, fCls, asCls, lfCls)) + } + } + + private class RunV1(scriptedTests: AnyRef, run: Method) extends ScriptedRun { + override protected def invoke( + resourceBaseDirectory: File, + bufferLog: java.lang.Boolean, + tests: Array[String], + launcherJar: File, + @unused javaCommand: String, + launchOpts: Array[String], + prescripted: java.util.List[File], + @unused instances: java.lang.Integer, + ): AnyRef = + run.invoke( + scriptedTests, + resourceBaseDirectory, + bufferLog, + tests, + launcherJar, + launchOpts, + prescripted, + ) + } + + private class RunInParallelV1(scriptedTests: AnyRef, runInParallel: Method) extends ScriptedRun { + override protected def invoke( + resourceBaseDirectory: File, + bufferLog: java.lang.Boolean, + tests: Array[String], + launcherJar: File, + @unused javaCommand: String, + launchOpts: Array[String], + prescripted: java.util.List[File], + instances: Integer, + ): AnyRef = + runInParallel.invoke( + scriptedTests, + resourceBaseDirectory, + bufferLog, + tests, + launcherJar, + launchOpts, + prescripted, + instances, + ) + } + + private class RunV2(scriptedTests: AnyRef, run: Method) extends ScriptedRun { + override protected def invoke( + resourceBaseDirectory: File, + bufferLog: java.lang.Boolean, + tests: Array[String], + launcherJar: File, + javaCommand: String, + launchOpts: Array[String], + prescripted: java.util.List[File], + @unused instances: java.lang.Integer, + ): AnyRef = + run.invoke( + scriptedTests, + resourceBaseDirectory, + bufferLog, + tests, + launcherJar, + javaCommand, + launchOpts, + prescripted, + ) + } + + private class RunInParallelV2(scriptedTests: AnyRef, runInParallel: Method) extends ScriptedRun { + override protected def invoke( + resourceBaseDirectory: File, + bufferLog: java.lang.Boolean, + tests: Array[String], + launcherJar: File, + javaCommand: String, + launchOpts: Array[String], + prescripted: java.util.List[File], + instances: Integer, + ): AnyRef = + runInParallel.invoke( + scriptedTests, + resourceBaseDirectory, + bufferLog, + tests, + launcherJar, + javaCommand, + launchOpts, + prescripted, + instances, + ) + } + +} diff --git a/main/src/main/scala/sbt/UpperStateOps.scala b/main/src/main/scala/sbt/UpperStateOps.scala index 02c9ef0c3..e53f600b4 100644 --- a/main/src/main/scala/sbt/UpperStateOps.scala +++ b/main/src/main/scala/sbt/UpperStateOps.scala @@ -18,12 +18,12 @@ trait UpperStateOps extends Any { /** * ProjectRef to the current project of the state session that can be change using - * `project` commmand. + * `project` command. */ def currentRef: ProjectRef /** - * Current project of the state session that can be change using `project` commmand. + * Current project of the state session that can be change using `project` command. */ def currentProject: ResolvedProject diff --git a/main/src/main/scala/sbt/coursierint/CoursierRepositoriesTasks.scala b/main/src/main/scala/sbt/coursierint/CoursierRepositoriesTasks.scala index 8bbdb8969..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 @@ -80,10 +71,10 @@ object CoursierRepositoriesTasks { result1 map { case r: FileRepository => val ivyPatterns = r.patterns.ivyPatterns map { - _.replaceAllLiterally("$" + "{ivy.home}", ivyHomeUri) + _.replace("$" + "{ivy.home}", ivyHomeUri) } val artifactPatterns = r.patterns.artifactPatterns map { - _.replaceAllLiterally("$" + "{ivy.home}", ivyHomeUri) + _.replace("$" + "{ivy.home}", ivyHomeUri) } val p = r.patterns.withIvyPatterns(ivyPatterns).withArtifactPatterns(artifactPatterns) diff --git a/main/src/main/scala/sbt/internal/Act.scala b/main/src/main/scala/sbt/internal/Act.scala index ce7a45639..50bdde82d 100644 --- a/main/src/main/scala/sbt/internal/Act.scala +++ b/main/src/main/scala/sbt/internal/Act.scala @@ -304,7 +304,12 @@ object Act { case Some(ProjectRef(uri, _)) => index.keys(Some(BuildRef(uri)), conf, task) case _ => Set() } - val keys: Set[String] = index.keys(proj, conf, task) ++ buildKeys + val globalKeys: Set[String] = + proj match { + case Some(_) => index.keys(None, conf, task) + case _ => Set() + } + val keys: Set[String] = index.keys(proj, conf, task) ++ buildKeys ++ globalKeys keyParser(keys) } diff --git a/main/src/main/scala/sbt/internal/Banner.scala b/main/src/main/scala/sbt/internal/Banner.scala index 35f94bc80..0b5a25e71 100644 --- a/main/src/main/scala/sbt/internal/Banner.scala +++ b/main/src/main/scala/sbt/internal/Banner.scala @@ -10,6 +10,15 @@ package sbt.internal private[sbt] object Banner { def apply(version: String): Option[String] = version match { + case v if v.startsWith("1.6.0") => + Some(s""" + |Here are some highlights of this release: + | - Improved JDK 17 support + | - Improved Build Server Protocol (BSP) support + | - Tab completion of global keys + |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") => Some(s""" |Here are some highlights of this release: diff --git a/main/src/main/scala/sbt/internal/BuildStructure.scala b/main/src/main/scala/sbt/internal/BuildStructure.scala index 4c10fba22..8bd0bcccf 100644 --- a/main/src/main/scala/sbt/internal/BuildStructure.scala +++ b/main/src/main/scala/sbt/internal/BuildStructure.scala @@ -263,7 +263,8 @@ final class BuildUnit( val plugins: LoadedPlugins ) { override def toString = - if (uri.getScheme == "file") localBase.toString else (uri + " (locally: " + localBase + ")") + if (uri.getScheme == "file") localBase.toString + else (uri.toString + " (locally: " + localBase + ")") } final class LoadedBuild(val root: URI, val units: Map[URI, LoadedBuildUnit]) { diff --git a/main/src/main/scala/sbt/internal/CommandStrings.scala b/main/src/main/scala/sbt/internal/CommandStrings.scala index 7c50f506e..439cdc295 100644 --- a/main/src/main/scala/sbt/internal/CommandStrings.scala +++ b/main/src/main/scala/sbt/internal/CommandStrings.scala @@ -70,23 +70,10 @@ $PrintCommand def pluginsDetailed = pluginsBrief // TODO: expand val LastCommand = "last" - val OldLastGrepCommand = "last-grep" val LastGrepCommand = "lastGrep" val ExportCommand = "export" val ExportStream = "export" - val oldLastGrepBrief = - (OldLastGrepCommand, "Shows lines from the last output for 'key' that match 'pattern'.") - val oldLastGrepDetailed = - s"""$OldLastGrepCommand - Displays lines from the logging of previous commands that match `pattern`. - -$OldLastGrepCommand [key] - Displays lines from logging associated with `key` that match `pattern`. The key typically refers to a task (for example, test:compile). The logging that is displayed is restricted to the logging for that particular task. - - is a regular expression interpreted by java.util.Pattern. Matching text is highlighted (when highlighting is supported and enabled). - See also '$LastCommand'.""" - val lastGrepBrief = (LastGrepCommand, "Shows lines from the last output for 'key' that match 'pattern'.") val lastGrepDetailed = diff --git a/main/src/main/scala/sbt/internal/Continuous.scala b/main/src/main/scala/sbt/internal/Continuous.scala index c206cb71e..ea1b5134f 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) = @@ -1208,7 +1229,7 @@ private[sbt] object ContinuousCommands { .channelForName(channelName) .getOrElse(throw new IllegalStateException(s"No channel with name $channelName")) val dynamicInputs = mutable.Set.empty[DynamicInput] - val context = LoggerContext(useLog4J = state.get(Keys.useLog4J.key).getOrElse(false)) + val context = LoggerContext() def cb: Continuous.Callbacks = Continuous.getCallbacks(state, channel, commands, cache, dynamicInputs, context) @@ -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/main/src/main/scala/sbt/internal/CrossJava.scala b/main/src/main/scala/sbt/internal/CrossJava.scala index 4764fc829..a16aca956 100644 --- a/main/src/main/scala/sbt/internal/CrossJava.scala +++ b/main/src/main/scala/sbt/internal/CrossJava.scala @@ -12,7 +12,7 @@ import java.io.File import scala.collection.immutable.ListMap import scala.annotation.tailrec import scala.util.{ Try, Success, Failure } -import sbt.io.Path +import sbt.io.{ IO, Path } import sbt.io.syntax._ import sbt.Cross._ import sbt.Def.{ ScopedKey, Setting } @@ -389,7 +389,7 @@ private[sbt] object CrossJava { object JavaDiscoverConfig { object JavaHomeDir { - private val regex = """(\w+-)?(java-|(?:adoptopen)?jdk-?)(bin-)?(1\.)?([0-9]+).*""".r + private val regex = """(\w+-)??(java-|(?:adoptopen)?jdk-?)?(bin-)?(1\.)?([0-9]+).*""".r def unapply(s: CharSequence): Option[String] = { s match { case regex(vendor, _, _, m, n) => Some(JavaVersion(nullBlank(m) + n).toString) @@ -447,15 +447,21 @@ private[sbt] object CrossJava { }.flatten } - class WindowsDiscoverConfig(base: File) extends JavaDiscoverConf { + class WindowsDiscoverConfig(base: File, vendors: Seq[String] = Seq.empty) + extends JavaDiscoverConf { def candidates() = wrapNull(base.list()) def javaHomes: Vector[(String, File)] = candidates() .collect { - case dir @ JavaHomeDir(version) => - version -> (base / dir) + case dir @ JavaHomeDir(version) => version -> base / dir + } + .flatMap { + case x if vendors.isEmpty => Vector(x) + case (version, home) => + val jv = JavaVersion(version) + vendors.map(jv.withVendor(_).toString -> home) } } @@ -482,10 +488,24 @@ private[sbt] object CrossJava { new LinuxDiscoverConfig(file("/usr") / "java"), new LinuxDiscoverConfig(file("/usr") / "lib" / "jvm"), new MacOsDiscoverConfig, - new WindowsDiscoverConfig(file("C://Program Files/Java")), - new WindowsDiscoverConfig(file("C://Program Files (x86)/Java")), new JavaHomeDiscoverConfig, - ) + ) ++ { + if (IO.isWindows) { + def discover(dir: String, vendors: String*) = new WindowsDiscoverConfig(file(dir), vendors) + Vector( + discover("C://Program Files/Java", "openjdk"), + discover("C://Program Files/Eclipse Foundation", "temurin", "adopt"), + discover("C://Program Files/Semeru", "semeru", "adopt-openj9"), + discover("C://Program Files/Microsoft", "microsoft"), + discover("C://Program Files/Amazon Corretto", "amazon-corretto"), + discover("C://Program Files/Zulu", "zulu"), + discover("C://Program Files/BellSoft", "liberica"), + discover("C://Program Files (x86)/Java", "openjdk"), + discover("C://Program Files (x86)/Eclipse Foundation", "temurin", "adopt"), + discover("C://Program Files (x86)/Semeru", "semeru", "adopt-openj9"), + ) + } else Vector.empty + } } def nullBlank(s: String): String = 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/DefaultBackgroundJobService.scala b/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala index b9effa8a9..5eeb3390e 100644 --- a/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala +++ b/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala @@ -90,7 +90,7 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe // hooks for sending start/stop events protected def onAddJob(@deprecated("unused", "") job: JobHandle): Unit = () protected def onRemoveJob(@deprecated("unused", "") job: JobHandle): Unit = () - private val context = LoggerContext(useLog4J) + private val context = LoggerContext() // this mutable state could conceptually go on State except // that then every task that runs a background job would have diff --git a/main/src/main/scala/sbt/internal/InstallSbtn.scala b/main/src/main/scala/sbt/internal/InstallSbtn.scala index 271a9a0cf..b6a1a2682 100644 --- a/main/src/main/scala/sbt/internal/InstallSbtn.scala +++ b/main/src/main/scala/sbt/internal/InstallSbtn.scala @@ -25,7 +25,7 @@ private[sbt] object InstallSbtn { Def.inputKey[Unit]("install sbtn and tab completions").withRank(KeyRanks.BTask) private[sbt] def installSbtnImpl: Def.Initialize[InputTask[Unit]] = Def.inputTask { val inputVersion = Def.spaceDelimited("version").parsed.headOption - val version = inputVersion.getOrElse(sbtVersion.value.replaceAllLiterally("-SNAPSHOT", "")) + val version = inputVersion.getOrElse(sbtVersion.value.replace("-SNAPSHOT", "")) val term = terminal.value term.setMode(canonical = false, echo = false) val baseDirectory = BuildPaths.getGlobalBase(state.value).toPath diff --git a/main/src/main/scala/sbt/internal/LogManager.scala b/main/src/main/scala/sbt/internal/LogManager.scala index e28fd91ee..a50f3c8e1 100644 --- a/main/src/main/scala/sbt/internal/LogManager.scala +++ b/main/src/main/scala/sbt/internal/LogManager.scala @@ -8,16 +8,15 @@ package sbt package internal -import java.io.PrintWriter - import sbt.Def.ScopedKey import sbt.Keys._ import sbt.Scope.Global import sbt.SlashSyntax0._ import sbt.internal.util.MainAppender._ import sbt.internal.util.{ Terminal => ITerminal, _ } -import sbt.util.{ Level, LogExchange, Logger, LoggerContext } -import org.apache.logging.log4j.core.{ Appender => XAppender } +import sbt.util.{ Level, Logger, LoggerContext } + +import java.io.PrintWriter import scala.annotation.nowarn sealed abstract class LogManager { @@ -88,12 +87,6 @@ object LogManager { def defaultManager(console: ConsoleOut): LogManager = withLoggers((_, _) => defaultScreen(console)) - @deprecated( - "use defaults that takes AppenderSupplier instead of ScopedKey[_] => Seq[Appender]", - "1.4.0" - ) - def defaults(extra: ScopedKey[_] => Seq[XAppender], console: ConsoleOut): LogManager = - defaults((sk: ScopedKey[_]) => extra(sk).map(new ConsoleAppenderFromLog4J("extra", _)), console) // This is called by Defaults. def defaults(extra: AppenderSupplier, console: ConsoleOut): LogManager = withLoggers( @@ -298,14 +291,6 @@ object LogManager { s1 } - @deprecated("No longer used.", "1.4.0") - private[sbt] def progressLogger(appender: ConsoleAppender): ManagedLogger = { - val log = LogExchange.logger("progress", None, None) - LoggerContext.globalContext.clearAppenders("progress") - LoggerContext.globalContext.addAppender("progress", appender -> Level.Info) - log - } - // This is the default implementation for the relay appender val defaultRelay: Unit => ConsoleAppender = _ => defaultRelayImpl diff --git a/main/src/main/scala/sbt/internal/RemoteCache.scala b/main/src/main/scala/sbt/internal/RemoteCache.scala index 200117de7..c3838243b 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)( @@ -303,7 +303,8 @@ object RemoteCache { } found = true case Left(e) => - log.info(s"remote cache not found for ${v}") + val classifier = seqa.map(_.classifier).mkString(" ") + log.info(s"remote cache artifact not found for $p $classifier") log.debug(e.getMessage) } } diff --git a/main/src/main/scala/sbt/internal/SettingGraph.scala b/main/src/main/scala/sbt/internal/SettingGraph.scala index fdf5f30ec..15669675c 100644 --- a/main/src/main/scala/sbt/internal/SettingGraph.scala +++ b/main/src/main/scala/sbt/internal/SettingGraph.scala @@ -12,7 +12,6 @@ import sbt.util.Show import java.io.File import Def.{ ScopedKey, compiled, flattenLocals } -import sbt.internal.util.Terminal import Predef.{ any2stringadd => _, _ } import sbt.io.IO @@ -123,10 +122,4 @@ object Graph { toAsciiLines(top, 0, Set.empty).mkString("\n") } - - def defaultColumnSize: Int = { - val termWidth = Terminal.console.getWidth - if (termWidth > 20) termWidth - 8 - else 80 // ignore termWidth - } } diff --git a/main/src/main/scala/sbt/internal/SysProp.scala b/main/src/main/scala/sbt/internal/SysProp.scala index c761e7f04..737b0b368 100644 --- a/main/src/main/scala/sbt/internal/SysProp.scala +++ b/main/src/main/scala/sbt/internal/SysProp.scala @@ -15,8 +15,9 @@ import scala.util.control.NonFatal import scala.concurrent.duration._ import sbt.internal.util.{ Terminal => ITerminal, Util } import sbt.internal.util.complete.SizeParser -import sbt.nio.Keys._ import sbt.io.syntax._ +import sbt.librarymanagement.ivy.{ Credentials, FileCredentials } +import sbt.nio.Keys._ // See also BuildPaths.scala // See also LineReader.scala @@ -216,4 +217,7 @@ object SysProp { .getOrElse(linuxCache) baseCache.getAbsoluteFile / "v1" } + + 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/TaskProgress.scala b/main/src/main/scala/sbt/internal/TaskProgress.scala index cf9ce2e88..65fea78fc 100644 --- a/main/src/main/scala/sbt/internal/TaskProgress.scala +++ b/main/src/main/scala/sbt/internal/TaskProgress.scala @@ -70,8 +70,9 @@ private[sbt] class TaskProgress( pending.clear() scheduler.shutdownNow() executor.shutdownNow() - if (!executor.awaitTermination(1, TimeUnit.SECONDS) || - !scheduler.awaitTermination(1, TimeUnit.SECONDS)) { + if (!executor.awaitTermination(30, TimeUnit.SECONDS) || + !scheduler.awaitTermination(30, TimeUnit.SECONDS)) { + scala.Console.err.println("timed out closing the executor of supershell") throw new TimeoutException } } diff --git a/main/src/main/scala/sbt/internal/graph/rendering/AsciiTree.scala b/main/src/main/scala/sbt/internal/graph/rendering/AsciiTree.scala index 33f37513a..5c474bf6f 100644 --- a/main/src/main/scala/sbt/internal/graph/rendering/AsciiTree.scala +++ b/main/src/main/scala/sbt/internal/graph/rendering/AsciiTree.scala @@ -13,7 +13,7 @@ package rendering import sbt.internal.util.Terminal.red object AsciiTree { - def asciiTree(graph: ModuleGraph): String = { + def asciiTree(graph: ModuleGraph, graphWidth: Int): String = { val deps = graph.dependencyMap // there should only be one root node (the project itself) @@ -25,7 +25,7 @@ object AsciiTree { root, node => deps.getOrElse(node.id, Seq.empty[Module]), displayModule, - Graph.defaultColumnSize + graphWidth ) } .mkString("\n") @@ -36,7 +36,7 @@ object AsciiTree { module.id.idString + module.extraInfo + module.error.map(" (error: " + _ + ")").getOrElse("") + - module.evictedByVersion.map(_ formatted " (evicted by: %s)").getOrElse(""), + module.evictedByVersion.map(v => s" (evicted by: $v)").getOrElse(""), module.hadError ) } diff --git a/main/src/main/scala/sbt/internal/graph/rendering/DagreHTML.scala b/main/src/main/scala/sbt/internal/graph/rendering/DagreHTML.scala index f00a197d2..0938d9ec9 100644 --- a/main/src/main/scala/sbt/internal/graph/rendering/DagreHTML.scala +++ b/main/src/main/scala/sbt/internal/graph/rendering/DagreHTML.scala @@ -25,7 +25,7 @@ object DagreHTML { val graphString = URLEncoder .encode(dotGraph, "utf8") - .replaceAllLiterally("+", "%20") + .replace("+", "%20") IO.write( new File(targetDirectory, "dependencies.dot.js"), diff --git a/main/src/main/scala/sbt/internal/graph/rendering/LicenseInfo.scala b/main/src/main/scala/sbt/internal/graph/rendering/LicenseInfo.scala index 27aa44708..d00209735 100644 --- a/main/src/main/scala/sbt/internal/graph/rendering/LicenseInfo.scala +++ b/main/src/main/scala/sbt/internal/graph/rendering/LicenseInfo.scala @@ -20,7 +20,7 @@ object LicenseInfo { .map { case (license, modules) => license.getOrElse("No license specified") + "\n" + - modules.map(_.id.idString formatted "\t %s").mkString("\n") + modules.map(m => s"\t ${m.id.idString}").mkString("\n") } .mkString("\n\n") } diff --git a/main/src/main/scala/sbt/internal/graph/rendering/TreeView.scala b/main/src/main/scala/sbt/internal/graph/rendering/TreeView.scala index d5732f5b7..3ff701ca1 100644 --- a/main/src/main/scala/sbt/internal/graph/rendering/TreeView.scala +++ b/main/src/main/scala/sbt/internal/graph/rendering/TreeView.scala @@ -10,21 +10,20 @@ package internal package graph package rendering -import java.io.{ OutputStream, InputStream, FileOutputStream, File } -import java.net.URI - -import graph.{ Module, ModuleGraph } +import sbt.internal.graph.codec.JsonProtocol.ModuleModelFormat import sbt.io.IO +import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter } +import java.io.{ File, FileOutputStream, InputStream, OutputStream } +import java.net.URI import scala.annotation.{ nowarn, tailrec } -import scala.util.parsing.json.{ JSONArray, JSONObject } @nowarn object TreeView { def createJson(graph: ModuleGraph): String = { - val trees = graph.roots + val moduleModels = graph.roots .map(module => processSubtree(graph, module)) - .toList - JSONArray(trees).toString + val js = moduleModels.map(Converter.toJsonUnsafe(_)) + js.map(CompactPrinter).mkString("[", ",", "]") } def createLink(graphJson: String, targetDirectory: File): URI = { @@ -36,19 +35,30 @@ import scala.util.parsing.json.{ JSONArray, JSONObject } new URI(graphHTML.toURI.toString) } - private def processSubtree(graph: ModuleGraph, module: Module): JSONObject = { - val children = graph.dependencyMap - .getOrElse(module.id, List()) - .map(module => processSubtree(graph, module)) - .toList - moduleAsJson(module, children) + private[rendering] def processSubtree( + graph: ModuleGraph, + module: Module, + parents: Set[GraphModuleId] = Set() + ): ModuleModel = { + val cycle = parents.contains(module.id) + val dependencies = if (cycle) List() else graph.dependencyMap.getOrElse(module.id, List()) + val children = + dependencies + .map(dependency => processSubtree(graph, dependency, parents + module.id)) + .toVector + moduleAsModuleAgain(module, cycle, children) } - private def moduleAsJson(module: Module, children: List[JSONObject]): JSONObject = { + private def moduleAsModuleAgain( + module: Module, + isCycle: Boolean, + children: Vector[ModuleModel] + ): ModuleModel = { val eviction = module.evictedByVersion.map(version => s" (evicted by $version)").getOrElse("") + val cycle = if (isCycle) " (cycle)" else "" val error = module.error.map(err => s" (errors: $err)").getOrElse("") - val text = module.id.idString + eviction + error - JSONObject(Map("text" -> text, "children" -> JSONArray(children))) + val text = module.id.idString + eviction + error + cycle + ModuleModel(text, children) } def saveResource(resourcePath: String, to: File): Unit = { diff --git a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala index 401bf6d31..4e2abdbe8 100644 --- a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala @@ -32,13 +32,16 @@ import sjsonnew.shaded.scalajson.ast.unsafe.{ JNull, JValue } import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter, Parser => JsonParser } import xsbti.CompileFailed +import java.nio.file.Path import java.io.File +import java.util.concurrent.atomic.AtomicBoolean import scala.collection.mutable // import scala.annotation.nowarn 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._ @@ -49,7 +52,9 @@ object BuildServerProtocol { RunProvider(BuildServerConnection.languages), dependencySourcesProvider = true, resourcesProvider = true, - canReload = true + canReload = true, + jvmRunEnvironmentProvider = true, + jvmTestEnvironmentProvider = true, ) private val bspReload = "bspReload" @@ -103,11 +108,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 @@ -116,82 +117,63 @@ object BuildServerProtocol { val base = loadedBuildUnit.localBase val sbtFiles = configurationSources(base) val pluginData = loadedBuildUnit.unit.plugins.pluginData - val all = Vector.newBuilder[SourceItem] - def add(fs: Seq[File], sourceItemKind: Int, generated: Boolean): Unit = { - fs.foreach(f => all += (SourceItem(f.toURI, sourceItemKind, generated = generated))) - } - all += (SourceItem( - loadedBuildUnit.unit.plugins.base.toURI, - SourceItemKind.Directory, - generated = false - )) - add(pluginData.unmanagedSourceDirectories, SourceItemKind.Directory, generated = false) - add(pluginData.unmanagedSources, SourceItemKind.File, generated = false) - add(pluginData.managedSourceDirectories, SourceItemKind.Directory, generated = true) - add(pluginData.managedSources, SourceItemKind.File, generated = true) - add(sbtFiles, SourceItemKind.File, generated = false) - Value(SourcesItem(id, all.result())) + val dirs = pluginData.unmanagedSourceDirectories + val sourceFiles = getStandaloneSourceFiles(pluginData.unmanagedSources, dirs) + val managedDirs = pluginData.managedSourceDirectories + val managedSourceFiles = + getStandaloneSourceFiles(pluginData.managedSources, managedDirs) + val items = + dirs.map(toSourceItem(SourceItemKind.Directory, generated = false)) ++ + sourceFiles.map(toSourceItem(SourceItemKind.File, generated = false)) ++ + managedDirs.map(toSourceItem(SourceItemKind.Directory, generated = true)) ++ + managedSourceFiles.map(toSourceItem(SourceItemKind.File, generated = true)) ++ + sbtFiles.map(toSourceItem(SourceItemKind.File, generated = false)) + Value(SourcesItem(id, items.toVector)) } 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 @@ -201,18 +183,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() @@ -233,34 +209,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) + 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 @@ -277,14 +245,14 @@ object BuildServerProtocol { bspBuildTargetSourcesItem := { val id = bspTargetIdentifier.value val dirs = unmanagedSourceDirectories.value - val managed = managedSources.value - val items = (dirs.toVector map { dir => - SourceItem(dir.toURI, SourceItemKind.Directory, generated = false) - }) ++ - (managed.toVector map { x => - SourceItem(x.toURI, SourceItemKind.File, generated = true) - }) - SourcesItem(id, items) + val sourceFiles = getStandaloneSourceFiles(unmanagedSources.value, dirs) + val managedDirs = managedSourceDirectories.value + val managedSourceFiles = getStandaloneSourceFiles(managedSources.value, managedDirs) + val items = dirs.map(toSourceItem(SourceItemKind.Directory, generated = false)) ++ + sourceFiles.map(toSourceItem(SourceItemKind.File, generated = false)) ++ + managedDirs.map(toSourceItem(SourceItemKind.Directory, generated = true)) ++ + managedSourceFiles.map(toSourceItem(SourceItemKind.File, generated = true)) + SourcesItem(id, items.toVector) }, bspBuildTargetResourcesItem := { val id = bspTargetIdentifier.value @@ -302,17 +270,42 @@ object BuildServerProtocol { bspBuildTargetCompileItem := bspCompileTask.value, bspBuildTargetRun := bspRunTask.evaluated, bspBuildTargetScalacOptionsItem := scalacOptionsTask.value, + bspBuildTargetJVMRunEnvironment := bspInputTask { (state, _, _, filter) => + Def.task { + val items = bspBuildTargetJvmEnvironmentItem.result.all(filter).value + val successfulItems = anyOrThrow(items) + val result = JvmRunEnvironmentResult(successfulItems.toVector, None) + state.respondEvent(result) + } + }.evaluated, + bspBuildTargetJVMTestEnvironment := bspInputTask { (state, _, _, filter) => + Def.task { + val items = bspBuildTargetJvmEnvironmentItem.result.all(filter).value + val successfulItems = anyOrThrow(items) + val result = JvmTestEnvironmentResult(successfulItems.toVector, None) + state.respondEvent(result) + } + }.evaluated, + bspBuildTargetJvmEnvironmentItem := jvmEnvironmentItem().value, bspInternalDependencyConfigurations := internalDependencyConfigurationsSetting.value, bspScalaTestClassesItem := scalaTestClassesTask.value, bspScalaMainClassesItem := scalaMainClassesTask.value, Keys.compile / bspReporter := { val targetId = bspTargetIdentifier.value + val bspCompileStateInstance = bspCompileState.value val converter = fileConverter.value val underlying = (Keys.compile / compilerReporter).value val logger = streams.value.log val meta = isMetaBuild.value if (bspEnabled.value) { - new BuildServerReporterImpl(targetId, converter, meta, logger, underlying) + new BuildServerReporterImpl( + targetId, + bspCompileStateInstance, + converter, + meta, + logger, + underlying + ) } else { new BuildServerForwarder(meta, logger, underlying) } @@ -331,6 +324,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" @@ -429,6 +424,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(" ") @@ -456,6 +463,18 @@ object BuildServerProtocol { } } + private def getStandaloneSourceFiles( + sourceFiles: Seq[File], + sourceDirs: Seq[File] + ): Seq[File] = { + sourceFiles.filterNot { f => + sourceDirs.exists(dir => f.toPath.startsWith(dir.toPath)) + } + } + + private def toSourceItem(itemKind: Int, generated: Boolean)(file: File): SourceItem = + SourceItem(file.toURI, itemKind, generated) + private def checkMetalsCompatibility( semanticdbEnabled: Boolean, semanticdbVersion: String, @@ -622,6 +641,38 @@ object BuildServerProtocol { ) } + 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 { val target = Keys.bspTargetIdentifier.value val scalacOptions = Keys.scalacOptions.value @@ -657,6 +708,10 @@ object BuildServerProtocol { DependencySourcesItem(targetId, sources.toVector.distinct) } + private def bspCompileState: Initialize[BuildServerProtocol.BspCompileState] = Def.setting { + new BuildServerProtocol.BspCompileState() + } + private def bspCompileTask: Def.Initialize[Task[Int]] = Def.task { Keys.compile.result.value match { case Value(_) => StatusCode.Success @@ -831,15 +886,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 { @@ -933,4 +998,20 @@ object BuildServerProtocol { ) } } + + /** + * Additional information about compilation status for given build target. + * + * @param hasAnyProblems keeps track of problems in given file so BSP reporter + * can omit unnecessary diagnostics updates. + * @param compiledAtLeastOnce keeps 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[server] final class BspCompileState { + val hasAnyProblems: java.util.Set[Path] = + java.util.concurrent.ConcurrentHashMap.newKeySet[Path] + val compiledAtLeastOnce: AtomicBoolean = new AtomicBoolean(false) + } } diff --git a/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala b/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala index 41a35ee8e..6e04053da 100644 --- a/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala +++ b/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala @@ -12,6 +12,7 @@ import java.nio.file.Path import sbt.StandardMain import sbt.internal.bsp._ import sbt.internal.util.ManagedLogger +import sbt.internal.server.BuildServerProtocol.BspCompileState import xsbti.compile.CompileAnalysis import xsbti.{ FileConverter, @@ -38,7 +39,9 @@ sealed trait BuildServerReporter extends Reporter { protected def publishDiagnostic(problem: Problem): Unit - def sendSuccessReport(analysis: CompileAnalysis): Unit + def sendSuccessReport( + analysis: CompileAnalysis, + ): Unit def sendFailureReport(sources: Array[VirtualFile]): Unit @@ -69,6 +72,7 @@ sealed trait BuildServerReporter extends Reporter { final class BuildServerReporterImpl( buildTarget: BuildTargetIdentifier, + bspCompileState: BspCompileState, converter: FileConverter, protected override val isMetaBuild: Boolean, protected override val logger: ManagedLogger, @@ -80,35 +84,81 @@ final class BuildServerReporterImpl( private lazy val exchange = StandardMain.exchange private val problemsByFile = mutable.Map[Path, Vector[Diagnostic]]() - override def sendSuccessReport(analysis: CompileAnalysis): Unit = { + // sometimes the compiler returns a fake position such as + // on Windows, this causes InvalidPathException (see #5994 and #6720) + private def toSafePath(ref: VirtualFileRef): Option[Path] = + if (ref.id().contains("<")) None + else Some(converter.toPath(ref)) + + /** + * Send diagnostics from the compilation to the client. + * Do not send empty diagnostics if previous ones were also empty ones. + * + * @param analysis current compile analysis + */ + override def sendSuccessReport( + analysis: CompileAnalysis, + ): Unit = { + val shouldReportAllProblems = !bspCompileState.compiledAtLeastOnce.getAndSet(true) for { (source, infos) <- analysis.readSourceInfos.getAllSourceInfos.asScala + filePath <- toSafePath(source) } { - val filePath = converter.toPath(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) + // clear problems for current file + val hadProblems = bspCompileState.hasAnyProblems.remove(filePath) + + val reportedProblems = infos.getReportedProblems.toVector + val diagnostics = reportedProblems.flatMap(toDiagnostic) + + // publish diagnostics if: + // 1. file had any problems previously - we might want to update them with new ones + // 2. file has fresh problems - we might want to update old ones + // 3. build project is compiled first time - shouldReportAllProblems is set + val shouldPublish = hadProblems || diagnostics.nonEmpty || shouldReportAllProblems + + // file can have some warnings + if (diagnostics.nonEmpty) { + bspCompileState.hasAnyProblems.add(filePath) + } + + if (shouldPublish) { + val params = PublishDiagnosticsParams( + textDocument = TextDocumentIdentifier(filePath.toUri), + buildTarget, + originId = None, + diagnostics.toVector, + reset = true + ) + exchange.notifyEvent("build/publishDiagnostics", params) + } } } - override def sendFailureReport(sources: Array[VirtualFile]): Unit = { - for (source <- sources) { - val filePath = converter.toPath(source) - val diagnostics = problemsByFile.getOrElse(filePath, Vector()) - val params = PublishDiagnosticsParams( - textDocument = TextDocumentIdentifier(filePath.toUri), - buildTarget, - originId = None, - diagnostics, - reset = true - ) - exchange.notifyEvent("build/publishDiagnostics", params) + val shouldReportAllProblems = !bspCompileState.compiledAtLeastOnce.get + for { + source <- sources + filePath <- toSafePath(source) + } { + val diagnostics = problemsByFile.getOrElse(filePath, Vector.empty) + + val hadProblems = bspCompileState.hasAnyProblems.remove(filePath) + val shouldPublish = hadProblems || diagnostics.nonEmpty || shouldReportAllProblems + + // mark file as file with problems + if (diagnostics.nonEmpty) { + bspCompileState.hasAnyProblems.add(filePath) + } + + if (shouldPublish) { + val params = PublishDiagnosticsParams( + textDocument = TextDocumentIdentifier(filePath.toUri), + buildTarget, + originId = None, + diagnostics, + reset = true + ) + exchange.notifyEvent("build/publishDiagnostics", params) + } } } @@ -116,9 +166,9 @@ final class BuildServerReporterImpl( for { id <- problem.position.sourcePath.toOption diagnostic <- toDiagnostic(problem) + filePath <- toSafePath(VirtualFileRef.of(id)) } { - val filePath = converter.toPath(VirtualFileRef.of(id)) - problemsByFile(filePath) = problemsByFile.getOrElse(filePath, Vector()) :+ diagnostic + problemsByFile(filePath) = problemsByFile.getOrElse(filePath, Vector.empty) :+ diagnostic val params = PublishDiagnosticsParams( TextDocumentIdentifier(filePath.toUri), buildTarget, @@ -171,7 +221,9 @@ final class BuildServerForwarder( protected override val underlying: Reporter ) extends BuildServerReporter { - override def sendSuccessReport(analysis: CompileAnalysis): Unit = () + override def sendSuccessReport( + analysis: CompileAnalysis, + ): Unit = () override def sendFailureReport(sources: Array[VirtualFile]): Unit = () diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index 8f647f98d..0acab0974 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -602,6 +602,7 @@ final class NetworkChannel( execId: String, err: JsonRpcResponseError ): Unit = { + forceFlush() val m = JsonRpcResponseMessage("2.0", execId, None, Option(err)) val bytes = Serialization.serializeResponseMessage(m) publishBytes(bytes) @@ -611,13 +612,17 @@ final class NetworkChannel( private[sbt] def jsonRpcNotify[A: JsonFormat](method: String, params: A): Unit = { val m = JsonRpcNotificationMessage("2.0", method, Option(Converter.toJson[A](params).get)) - if (method != Serialization.systemOut) log.debug(s"jsonRpcNotify: $m") + if (method != Serialization.systemOut) { + forceFlush() + log.debug(s"jsonRpcNotify: $m") + } val bytes = Serialization.serializeNotificationMessage(m) publishBytes(bytes) } /** Notify to Language Server's client. */ private[sbt] def jsonRpcRequest[A: JsonFormat](id: String, method: String, params: A): Unit = { + forceFlush() val m = JsonRpcRequestMessage("2.0", id, method, Option(Converter.toJson[A](params).get)) log.debug(s"jsonRpcRequest: $m") @@ -654,7 +659,13 @@ final class NetworkChannel( import scala.collection.JavaConverters._ private[this] val outputBuffer = new LinkedBlockingQueue[Byte] - private[this] val flushFuture = new AtomicReference[java.util.concurrent.Future[_]] + private[this] val flushExecutor = Executors.newSingleThreadScheduledExecutor( + r => new Thread(r, s"$name-output-buffer-timer-thread") + ) + private[this] def forceFlush() = { + Util.ignoreResult(flushExecutor.shutdownNow()) + doFlush() + } private[this] def doFlush()() = { val list = new java.util.ArrayList[Byte] outputBuffer.synchronized(outputBuffer.drainTo(list)) @@ -673,13 +684,9 @@ final class NetworkChannel( * probably long enough to catch each burst but short enough to not introduce * noticeable latency. */ - private[this] val executor = - Executors.newSingleThreadScheduledExecutor( - r => new Thread(r, s"$name-output-buffer-timer-thread") - ) + private[this] val flushFuture = new AtomicReference[java.util.concurrent.Future[_]] override def close(): Unit = { - Util.ignoreResult(executor.shutdownNow()) - doFlush() + forceFlush() } override def write(b: Int): Unit = outputBuffer.synchronized { outputBuffer.put(b.toByte) @@ -689,7 +696,7 @@ final class NetworkChannel( case null => try { flushFuture.set( - executor.schedule( + flushExecutor.schedule( (() => { flushFuture.set(null) doFlush() diff --git a/main/src/main/scala/sbt/nio/Settings.scala b/main/src/main/scala/sbt/nio/Settings.scala index 3f78649af..cef86648f 100644 --- a/main/src/main/scala/sbt/nio/Settings.scala +++ b/main/src/main/scala/sbt/nio/Settings.scala @@ -259,7 +259,7 @@ private[sbt] object Settings { * Provides an automatically generated clean method for a task that provides fileOutputs. * * @param taskKey the task for which we add a custom clean implementation - * @return a task specificic clean implementation + * @return a task specific clean implementation */ @nowarn private[sbt] def cleanImpl[T: JsonFormat: ToSeqPath](taskKey: TaskKey[T]): Def.Setting[_] = { diff --git a/main/src/main/scala/sbt/plugins/DependencyTreeSettings.scala b/main/src/main/scala/sbt/plugins/DependencyTreeSettings.scala index 2c20525e9..2ffe91128 100644 --- a/main/src/main/scala/sbt/plugins/DependencyTreeSettings.scala +++ b/main/src/main/scala/sbt/plugins/DependencyTreeSettings.scala @@ -80,7 +80,13 @@ object DependencyTreeSettings { else GraphTransformations.ignoreScalaLibrary(sv, g) }, dependencyTreeModuleGraphStore := (dependencyTreeModuleGraph0 storeAs dependencyTreeModuleGraphStore triggeredBy dependencyTreeModuleGraph0).value, - ) ++ renderingTaskSettings(dependencyTree, rendering.AsciiTree.asciiTree _) + ) ++ { + renderingTaskSettings(dependencyTree) :+ { + dependencyTree / asString := { + rendering.AsciiTree.asciiTree(dependencyTreeModuleGraph0.value, asciiGraphWidth.value) + } + } + } /** * This is the maximum strength settings for DependencyTreePlugin. @@ -130,11 +136,12 @@ object DependencyTreeSettings { case None => graph.nodes.filter(m => m.id.organization == org && m.id.name == name).map(_.id) } + val graphWidth = asciiGraphWidth.value val output = modules .map { module => rendering.AsciiTree - .asciiTree(GraphTransformations.reverseGraphStartingAt(graph, module)) + .asciiTree(GraphTransformations.reverseGraphStartingAt(graph, module), graphWidth) } .mkString("\n") @@ -142,7 +149,7 @@ object DependencyTreeSettings { output }, ) ++ - renderingAlternatives.flatMap((renderingTaskSettings _).tupled) + renderingAlternatives.flatMap { case (key, renderer) => renderingTaskSettings(key, renderer) } def renderingAlternatives: Seq[(TaskKey[Unit], ModuleGraph => String)] = Seq( @@ -152,13 +159,17 @@ object DependencyTreeSettings { ) def renderingTaskSettings(key: TaskKey[Unit], renderer: ModuleGraph => String): Seq[Setting[_]] = + renderingTaskSettings(key) :+ { + key / asString := renderer(dependencyTreeModuleGraph0.value) + } + + def renderingTaskSettings(key: TaskKey[Unit]): Seq[Setting[_]] = Seq( key := { val s = streams.value val str = (key / asString).value s.log.info(str) }, - key / asString := renderer(dependencyTreeModuleGraph0.value), key / toFile := { val (targetFile, force) = targetFileAndForceParser.parsed writeToFile(key.key.label, (key / asString).value, targetFile, force, streams.value) diff --git a/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala b/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala index 76b9c8f1b..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.4.28" + semanticdbVersion := "4.5.9" ) override lazy val projectSettings: Seq[Def.Setting[_]] = Seq( diff --git a/main/src/test/scala/ParseKey.scala b/main/src/test/scala/ParseKey.scala index 343e0bfe2..d5405a0da 100644 --- a/main/src/test/scala/ParseKey.scala +++ b/main/src/test/scala/ParseKey.scala @@ -173,7 +173,7 @@ object ParseKey extends Properties { .log(s"Key: ${Scope.displayPedantic(key.scope, key.key.label)}") .log(s"Mask: $mask") .log(s"Key string: '$s'") - .log(s"Parsed: ${parsed.right.map(displayFull)}") + .log(s"Parsed: ${parsed.map(displayFull)}") .log(s"Structure: $structure") ) } diff --git a/main/src/test/scala/sbt/CrossSpec.scala b/main/src/test/scala/sbt/CrossSpec.scala new file mode 100644 index 000000000..40673d548 --- /dev/null +++ b/main/src/test/scala/sbt/CrossSpec.scala @@ -0,0 +1,20 @@ +/* + * sbt + * Copyright 2011 - 2018, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt + +object CrossSpec extends verify.BasicTestSuite { + import Cross._ + + test("glob filter should work as expected") { + assert(globFilter("2.13.*", Seq("2.12.8", "2.13.16", "3.0.1")) == Seq("2.13.16")) + assert(globFilter("3.*", Seq("2.12.8", "2.13.16", "3.0.1")) == Seq("3.0.1")) + assert(globFilter("3.*", Seq("3.0.1", "30.1")) == Seq("3.0.1")) + assert(globFilter("2.*", Seq("2.12.8", "2.13.16", "3.0.1")) == Seq("2.12.8", "2.13.16")) + assert(globFilter("4.*", Seq("2.12.8", "2.13.16", "3.0.1")) == Nil) + } +} diff --git a/main/src/test/scala/sbt/internal/CrossJavaTest.scala b/main/src/test/scala/sbt/internal/CrossJavaTest.scala index b7a21fdf6..c884c31c8 100644 --- a/main/src/test/scala/sbt/internal/CrossJavaTest.scala +++ b/main/src/test/scala/sbt/internal/CrossJavaTest.scala @@ -8,11 +8,12 @@ package sbt package internal -import org.scalatest._ +import org.scalatest.diagrams.Diagrams +import org.scalatest.funsuite.AnyFunSuite import sbt.internal.CrossJava.JavaDiscoverConfig._ import scala.collection.immutable.ListMap -class CrossJavaTest extends FunSuite with DiagrammedAssertions { +class CrossJavaTest extends AnyFunSuite with Diagrams { test("The Java home selector should select the most recent") { assert( List("jdk1.8.0.jdk", "jdk1.8.0_121.jdk", "jdk1.8.0_45.jdk") @@ -59,6 +60,16 @@ class CrossJavaTest extends FunSuite with DiagrammedAssertions { assert(file.getName == "jdk1.7.0") } + test("The Windows Java home selector should correctly pick up a JDK with vendors") { + val conf = new WindowsDiscoverConfig(sbt.io.syntax.file("."), Seq("xxx", "yyy")) { + override def candidates() = Vector("jdk1.7.0") + } + val homes = conf.javaHomes + assert(homes.size == 2) + assert(homes.map(_._1) == Vector("xxx@1.7", "yyy@1.7")) + assert(homes.map(_._2.getName).forall(_ == "jdk1.7.0")) + } + test("The JAVA_HOME selector should correctly pick up a JDK") { val conf = new JavaHomeDiscoverConfig { override def home() = Some("/opt/jdk8") diff --git a/main/src/test/scala/sbt/internal/FileStampJsonSpec.scala b/main/src/test/scala/sbt/internal/FileStampJsonSpec.scala index fe4dacb53..bf0f127f3 100644 --- a/main/src/test/scala/sbt/internal/FileStampJsonSpec.scala +++ b/main/src/test/scala/sbt/internal/FileStampJsonSpec.scala @@ -9,13 +9,13 @@ package sbt.internal import java.nio.file.{ Path, Paths } -import org.scalatest.FlatSpec +import org.scalatest.flatspec.AnyFlatSpec import sbt.nio.FileStamp import sbt.nio.FileStamp.Formats import sjsonnew.JsonFormat import sjsonnew.support.scalajson.unsafe.Converter -class FileStampJsonSpec extends FlatSpec { +class FileStampJsonSpec extends AnyFlatSpec { "file hashes" should "be serializable" in { val hashes = Seq( Paths.get("foo") -> FileStamp.hash("bar"), diff --git a/main/src/test/scala/sbt/internal/GCMonitorTest.scala b/main/src/test/scala/sbt/internal/GCMonitorTest.scala index 02356e0a3..d646b88f1 100644 --- a/main/src/test/scala/sbt/internal/GCMonitorTest.scala +++ b/main/src/test/scala/sbt/internal/GCMonitorTest.scala @@ -9,12 +9,12 @@ package sbt.internal import java.util.concurrent.atomic.AtomicReference -import org.scalatest.FunSuite +import org.scalatest.funsuite.AnyFunSuite import scala.collection.mutable.ListBuffer import scala.concurrent.duration._ -class GCMonitorTest extends FunSuite { +class GCMonitorTest extends AnyFunSuite { class TestMonitor extends GCMonitorBase { val loggedTotals = ListBuffer.empty[Long] override protected val window = 10.seconds diff --git a/main/src/test/scala/sbt/internal/InstallSbtnSpec.scala b/main/src/test/scala/sbt/internal/InstallSbtnSpec.scala index 3580007ed..392cc1fc9 100644 --- a/main/src/test/scala/sbt/internal/InstallSbtnSpec.scala +++ b/main/src/test/scala/sbt/internal/InstallSbtnSpec.scala @@ -13,10 +13,10 @@ import java.lang.ProcessBuilder import java.lang.ProcessBuilder.Redirect import java.nio.file.{ Files, Path } import java.util.concurrent.TimeUnit -import org.scalatest.FlatSpec +import org.scalatest.flatspec.AnyFlatSpec import sbt.io.IO -class InstallSbtnSpec extends FlatSpec { +class InstallSbtnSpec extends AnyFlatSpec { private def withTemp[R](ext: String)(f: Path => R): R = { val tmp = Files.createTempFile("sbt-1.4.1-", ext) try f(tmp) @@ -41,7 +41,7 @@ class InstallSbtnSpec extends FlatSpec { val tmpDir = Files.createTempDirectory("sbtn-test").toRealPath() Files.createDirectories(tmpDir.resolve("project")) val foo = tmpDir.resolve("foo") - val fooPath = foo.toString.replaceAllLiterally("\\", "\\\\") + val fooPath = foo.toString.replace("\\", "\\\\") val build = s"""TaskKey[Unit]("foo") := IO.write(file("$fooPath"), "foo")""" IO.write(tmpDir.resolve("build.sbt").toFile, build) IO.write( diff --git a/main/src/test/scala/sbt/internal/StressGCMonitor.scala b/main/src/test/scala/sbt/internal/StressGCMonitor.scala index d9593ef79..8566b21d1 100644 --- a/main/src/test/scala/sbt/internal/StressGCMonitor.scala +++ b/main/src/test/scala/sbt/internal/StressGCMonitor.scala @@ -19,7 +19,7 @@ object StressGCMonitor { new GCMonitor(ConsoleLogger()) val deadline = Deadline.now + 10.seconds while (!deadline.isOverdue()) { - println(deadline.timeLeft.toSeconds + " seconds left...") + println(s"${deadline.timeLeft.toSeconds} seconds left...") list = List.fill(1000 * 1000 * 100)(Random.nextInt(100)) System.gc() Thread.sleep(10) diff --git a/main/src/test/scala/sbt/internal/TestBuild.scala b/main/src/test/scala/sbt/internal/TestBuild.scala index 48bf993b9..b9290240e 100644 --- a/main/src/test/scala/sbt/internal/TestBuild.scala +++ b/main/src/test/scala/sbt/internal/TestBuild.scala @@ -51,7 +51,7 @@ abstract class TestBuild { def tGen = genTasks(kebabIdGen, MaxDepsGen, MaxTasksGen) class TestKeys(val env: Env, val scopes: Seq[Scope]) { - override def toString = env + "\n" + scopes.mkString("Scopes:\n\t", "\n\t", "") + override def toString = env.toString + "\n" + scopes.mkString("Scopes:\n\t", "\n\t", "") lazy val delegated = scopes map env.delegates } diff --git a/main/src/test/scala/sbt/internal/graph/rendering/TreeViewTest.scala b/main/src/test/scala/sbt/internal/graph/rendering/TreeViewTest.scala new file mode 100644 index 000000000..6b291b4e3 --- /dev/null +++ b/main/src/test/scala/sbt/internal/graph/rendering/TreeViewTest.scala @@ -0,0 +1,53 @@ +/* + * sbt + * Copyright 2011 - 2018, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt.internal.graph.rendering + +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import sbt.internal.graph.rendering.TreeView.createJson +import sbt.internal.graph.{ GraphModuleId, Module, ModuleGraph, ModuleModel } + +class TreeViewTest extends AnyFlatSpec with Matchers { + val modA = GraphModuleId("orgA", "nameA", "1.0") + val modB = GraphModuleId("orgB", "nameB", "2.0") + val modC = GraphModuleId("orgC", "nameC", "3.0") + + val graph = ModuleGraph( + nodes = Seq(Module(modA), Module(modB), Module(modC)), + edges = Seq( + modA -> modA, + modA -> modB, + modC -> modA, + ) + ) + + "createJson" should "convert ModuleGraph into JSON correctly" in { + val expected = + "[{\"text\":\"orgC:nameC:3.0\",\"children\":[{\"text\":\"orgA:nameA:1.0\",\"children\":[{\"text\":\"orgA:nameA:1.0 (cycle)\",\"children\":[]},{\"text\":\"orgB:nameB:2.0\",\"children\":[]}]}]}]" + Predef.assert( + createJson(graph) == expected, + s"Expected $expected, but got ${createJson(graph)}" + ) + } + + "processSubtree" should "detect cycles and truncate" in { + val expected = ModuleModel( + "orgC:nameC:3.0", + Vector( + ModuleModel( + "orgA:nameA:1.0", + Vector( + ModuleModel("orgA:nameA:1.0 (cycle)", Vector()), + ModuleModel("orgB:nameB:2.0", Vector()) + ) + ) + ) + ) + assert(TreeView.processSubtree(graph, Module(modC), Set()) == expected) + } +} diff --git a/notes/1.6.0/configure-scripted-java-home.md b/notes/1.6.0/configure-scripted-java-home.md new file mode 100644 index 000000000..f2ace91d8 --- /dev/null +++ b/notes/1.6.0/configure-scripted-java-home.md @@ -0,0 +1,14 @@ +[@kxbmap]: https://github.com/kxbmap + +[#6673]: https://github.com/sbt/sbt/pull/6673 + +### Improvements + +- Make javaHome that forks scripted tests configurable. [#6673][] by [@kxbmap][] + - Normally scripted tests are forked using the JVM that is running sbt. If set `scripted / javaHome`, forked using it. + - Or use `java++` command before scripted. + +### Fixes with compatibility implications + +- Change type of `scriptedRun` task key from `TaskKey[java.lang.reflect.Method]` to `TaskKey[sbt.ScriptedRun]` + - `sbt.ScriptedRun` is a new interface for hiding substance of scripted invocation. diff --git a/notes/1.6.0/enable-asciiGraphWidth-in-dependencyTree.md b/notes/1.6.0/enable-asciiGraphWidth-in-dependencyTree.md new file mode 100644 index 000000000..93fd60d1f --- /dev/null +++ b/notes/1.6.0/enable-asciiGraphWidth-in-dependencyTree.md @@ -0,0 +1,13 @@ +[@kijuky]: https://github.com/kijuky + +[5962]: https://github.com/sbt/sbt/issues/5962 + +### Fixes with compatibility implications + +- The output of the dependencyTree will not be truncated at the console width. + +### Improvements + +- Enable the asciiGraphWidth setting in the dependencyTree tasks. + +### Bug fixes diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 974fa291b..368646ed4 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -4,18 +4,18 @@ 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] = sys.env.get("BUILD_VERSION") orElse sys.props.get("sbt.build.version") // sbt modules - private val ioVersion = nightlyVersion.getOrElse("1.6.0-M1") + private val ioVersion = nightlyVersion.getOrElse("1.6.0") private val lmVersion = - sys.props.get("sbt.build.lm.version").orElse(nightlyVersion).getOrElse("1.6.0-M1") - val zincVersion = nightlyVersion.getOrElse("1.6.0-M1") + 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 @@ -69,15 +69,15 @@ 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.8" + val lmCoursierShaded = "io.get-coursier" %% "lm-coursier-shaded" % "2.0.10" def sjsonNew(n: String) = Def.setting("com.eed3si9n" %% n % "0.9.1") // contrabandSjsonNewVersion.value @@ -94,19 +94,37 @@ object Dependencies { val jline3Reader = "org.jline" % "jline-reader" % jline3Version val jline3Builtins = "org.jline" % "jline-builtins" % jline3Version val jansi = "org.fusesource.jansi" % "jansi" % "2.1.0" - val scalatest = "org.scalatest" %% "scalatest" % "3.0.8" - val scalacheck = "org.scalacheck" %% "scalacheck" % "1.14.0" + val scalatest = "org.scalatest" %% "scalatest" % "3.2.10" + val scalacheck = "org.scalacheck" %% "scalacheck" % "1.15.4" val junit = "junit" % "junit" % "4.13.1" val scalaVerify = "com.eed3si9n.verify" %% "verify" % "1.0.0" val templateResolverApi = "org.scala-sbt" % "template-resolver" % "0.1" - val scalaXml = "org.scala-lang.modules" %% "scala-xml" % "1.3.0" - val scalaParsers = "org.scala-lang.modules" %% "scala-parser-combinators" % "1.1.2" - val scalaReflect = Def.setting("org.scala-lang" % "scala-reflect" % scalaVersion.value) + val scalaXml = Def.setting( + if (scalaBinaryVersion.value == "3") { + "org.scala-lang.modules" %% "scala-xml" % "2.0.1" + } else { + "org.scala-lang.modules" %% "scala-xml" % "1.3.0" + } + ) + val scalaParsers = Def.setting( + if (scalaBinaryVersion.value == "3") { + "org.scala-lang.modules" %% "scala-parser-combinators" % "2.1.0" + } else { + "org.scala-lang.modules" %% "scala-parser-combinators" % "1.1.2" + } + ) + val scalaReflect = Def.setting( + if (scalaBinaryVersion.value == "3") { + "org.scala-lang" % "scala-reflect" % scala213 + } else { + "org.scala-lang" % "scala-reflect" % scalaVersion.value + } + ) 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.11.2" + 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") @@ -114,7 +132,7 @@ object Dependencies { val caffeine = "com.github.ben-manes.caffeine" % "caffeine" % "2.8.5" - val hedgehog = "qa.hedgehog" %% "hedgehog-sbt" % "0.6.1" + val hedgehog = "qa.hedgehog" %% "hedgehog-sbt" % "0.7.0" val disruptor = "com.lmax" % "disruptor" % "3.4.2" val kindProjector = ("org.typelevel" % "kind-projector" % "0.13.2").cross(CrossVersion.full) } diff --git a/project/HouseRulesPlugin.scala b/project/HouseRulesPlugin.scala index 4a0b9fa1e..f1532dd8c 100644 --- a/project/HouseRulesPlugin.scala +++ b/project/HouseRulesPlugin.scala @@ -23,6 +23,7 @@ object HouseRulesPlugin extends AutoPlugin { }) .value .toList, + scalacOptions ++= "-Ykind-projector".ifScala3.value.toList, scalacOptions ++= "-Yinline-warnings".ifScala211OrMinus.value.toList, scalacOptions ++= "-Yno-adapted-args".ifScala212OrMinus.value.toList, scalacOptions += "-Ywarn-dead-code", @@ -44,5 +45,9 @@ object HouseRulesPlugin extends AutoPlugin { def ifScala211OrPlus = ifScalaGte(11) def ifScala212OrMinus = ifScalaLte(12) def ifScala213OrMinus = ifScalaLte(13) + def ifScala3 = Def.setting( + if (scalaBinaryVersion.value == "3") Seq(__x) + else Nil + ) } } 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/project/plugins.sbt b/project/plugins.sbt index 245e11f02..00c24984e 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -6,8 +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/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/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/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/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-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-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-scala/sbt/internal/graph/ModuleModel.scala b/protocol/src/main/contraband-scala/sbt/internal/graph/ModuleModel.scala new file mode 100644 index 000000000..0d8a518a6 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/graph/ModuleModel.scala @@ -0,0 +1,36 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.graph +final class ModuleModel private ( + val text: String, + val children: Vector[sbt.internal.graph.ModuleModel]) extends Serializable { + + + + override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { + case x: ModuleModel => (this.text == x.text) && (this.children == x.children) + case _ => false + }) + override def hashCode: Int = { + 37 * (37 * (37 * (17 + "sbt.internal.graph.ModuleModel".##) + text.##) + children.##) + } + override def toString: String = { + "ModuleModel(" + text + ", " + children + ")" + } + private[this] def copy(text: String = text, children: Vector[sbt.internal.graph.ModuleModel] = children): ModuleModel = { + new ModuleModel(text, children) + } + def withText(text: String): ModuleModel = { + copy(text = text) + } + def withChildren(children: Vector[sbt.internal.graph.ModuleModel]): ModuleModel = { + copy(children = children) + } +} +object ModuleModel { + + def apply(text: String, children: Vector[sbt.internal.graph.ModuleModel]): ModuleModel = new ModuleModel(text, children) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/graph/codec/JsonProtocol.scala b/protocol/src/main/contraband-scala/sbt/internal/graph/codec/JsonProtocol.scala new file mode 100644 index 000000000..09155f459 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/graph/codec/JsonProtocol.scala @@ -0,0 +1,9 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.graph.codec +trait JsonProtocol extends sjsonnew.BasicJsonProtocol + with sbt.internal.graph.codec.ModuleModelFormats +object JsonProtocol extends JsonProtocol \ No newline at end of file diff --git a/protocol/src/main/contraband-scala/sbt/internal/graph/codec/ModuleModelFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/graph/codec/ModuleModelFormats.scala new file mode 100644 index 000000000..eaf3c902c --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/graph/codec/ModuleModelFormats.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.graph.codec +import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } +trait ModuleModelFormats { self: sbt.internal.graph.codec.ModuleModelFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val ModuleModelFormat: JsonFormat[sbt.internal.graph.ModuleModel] = new JsonFormat[sbt.internal.graph.ModuleModel] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.graph.ModuleModel = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val text = unbuilder.readField[String]("text") + val children = unbuilder.readField[Vector[sbt.internal.graph.ModuleModel]]("children") + unbuilder.endObject() + sbt.internal.graph.ModuleModel(text, children) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.graph.ModuleModel, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("text", obj.text) + builder.addField("children", obj.children) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband/bsp.contra b/protocol/src/main/contraband/bsp.contra index 01b3a450f..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 { @@ -636,6 +645,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 @@ -693,3 +705,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 diff --git a/protocol/src/main/contraband/treeView.contra b/protocol/src/main/contraband/treeView.contra new file mode 100644 index 000000000..11074e74b --- /dev/null +++ b/protocol/src/main/contraband/treeView.contra @@ -0,0 +1,9 @@ +package sbt.internal.graph +@target(Scala) +@codecPackage("sbt.internal.graph.codec") +@fullCodec("JsonProtocol") + +type ModuleModel { + text: String! + children: [sbt.internal.graph.ModuleModel] +} \ No newline at end of file diff --git a/protocol/src/main/scala/sbt/internal/bsp/BuildServerConnection.scala b/protocol/src/main/scala/sbt/internal/bsp/BuildServerConnection.scala index 85c2bd3cb..fa7fce354 100644 --- a/protocol/src/main/scala/sbt/internal/bsp/BuildServerConnection.scala +++ b/protocol/src/main/scala/sbt/internal/bsp/BuildServerConnection.scala @@ -39,7 +39,7 @@ object BuildServerConnection { val sbtLaunchJar = classPath .split(File.pathSeparator) .find(jar => SbtLaunchJar.findFirstIn(jar).nonEmpty) - .map(_.replaceAllLiterally(" ", "%20")) + .map(_.replace(" ", "%20")) .map(jar => s"--sbt-launch-jar=$jar") val argv = @@ -49,9 +49,11 @@ object BuildServerConnection { "-Xmx100m", "-classpath", classPath, - "xsbt.boot.Boot", - "-bsp" - ) ++ sbtScript.orElse(sbtLaunchJar) + ) ++ + sbtScript ++ + Vector("xsbt.boot.Boot", "-bsp") ++ + (if (sbtScript.isEmpty) sbtLaunchJar else None) + val details = BspConnectionDetails(name, sbtVersion, bspVersion, languages, argv) val json = Converter.toJson(details).get IO.write(bspConnectionFile, CompactPrinter(json), append = false) @@ -61,11 +63,11 @@ object BuildServerConnection { // For those who use an old sbt script, the -Dsbt.script is not set // As a fallback we try to find the sbt script in $PATH val fileName = if (Properties.isWin) "sbt.bat" else "sbt" - val envPath = Option(System.getenv("PATH")).getOrElse("") + val envPath = sys.env.getOrElse("PATH", "") val allPaths = envPath.split(File.pathSeparator).map(Paths.get(_)) allPaths .map(_.resolve(fileName)) .find(file => Files.exists(file) && Files.isExecutable(file)) - .map(_.toString.replaceAllLiterally(" ", "%20")) + .map(_.toString.replace(" ", "%20")) } } diff --git a/run/src/main/scala/sbt/Fork.scala b/run/src/main/scala/sbt/Fork.scala index 9745c28a6..c7e74c0f7 100644 --- a/run/src/main/scala/sbt/Fork.scala +++ b/run/src/main/scala/sbt/Fork.scala @@ -9,10 +9,9 @@ package sbt import java.io.File import java.lang.ProcessBuilder.Redirect - import scala.sys.process.Process import OutputStrategy._ -import sbt.internal.util.Util +import sbt.internal.util.{ RunningProcesses, Util } import Util.{ AnyOps, none } import java.lang.{ ProcessBuilder => JProcessBuilder } @@ -31,7 +30,15 @@ final class Fork(val commandName: String, val runnerClass: Option[String]) { * It is configured according to `config`. * If `runnerClass` is defined for this Fork instance, it is prepended to `arguments` to define the arguments passed to the forked command. */ - def apply(config: ForkOptions, arguments: Seq[String]): Int = fork(config, arguments).exitValue() + def apply(config: ForkOptions, arguments: Seq[String]): Int = { + val p = fork(config, arguments) + RunningProcesses.add(p) + try p.exitValue() + finally { + if (p.isAlive()) p.destroy() + RunningProcesses.remove(p) + } + } /** * Forks the configured process and returns a `Process` that can be used to wait for completion or to terminate the forked process. @@ -114,7 +121,7 @@ object Fork { (classpathOption, newOptions) } - private def javaCommand(javaHome: Option[File], name: String): File = { + private[sbt] def javaCommand(javaHome: Option[File], name: String): File = { val home = javaHome.getOrElse(new File(System.getProperty("java.home"))) new File(new File(home, "bin"), name) } diff --git a/run/src/main/scala/sbt/Run.scala b/run/src/main/scala/sbt/Run.scala index 4df43f707..637c1473a 100644 --- a/run/src/main/scala/sbt/Run.scala +++ b/run/src/main/scala/sbt/Run.scala @@ -10,7 +10,6 @@ package sbt import java.io.File import java.lang.reflect.Method import java.lang.reflect.Modifier.{ isPublic, isStatic } - import sbt.internal.inc.ScalaInstance import sbt.internal.inc.classpath.{ ClasspathFilter, ClasspathUtil } import sbt.internal.util.MessageOnlyException @@ -34,29 +33,40 @@ class ForkRun(config: ForkOptions) extends ScalaRun { s"""Nonzero exit code returned from $label: $exitCode""".stripMargin ) ) - val process = fork(mainClass, classpath, options, log) - def cancel() = { - log.warn("Run canceled.") - process.destroy() - 1 + + log.info(s"running (fork) $mainClass ${Run.runOptionsStr(options)}") + val c = configLogged(log) + val scalaOpts = scalaOptions(mainClass, classpath, options) + val exitCode = try Fork.java(c, scalaOpts) + catch { + case _: InterruptedException => + log.warn("Run canceled.") + 1 } - val exitCode = try process.exitValue() - catch { case _: InterruptedException => cancel() } processExitCode(exitCode, "runner") } def fork(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Process = { log.info(s"running (fork) $mainClass ${Run.runOptionsStr(options)}") - val scalaOptions = classpathOption(classpath) ::: mainClass :: options.toList - val configLogged = - if (config.outputStrategy.isDefined) config - else config.withOutputStrategy(OutputStrategy.LoggedOutput(log)) + val c = configLogged(log) + val scalaOpts = scalaOptions(mainClass, classpath, options) + // fork with Java because Scala introduces an extra class loader (#702) - Fork.java.fork(configLogged, scalaOptions) + Fork.java.fork(c, scalaOpts) } - private def classpathOption(classpath: Seq[File]) = - "-classpath" :: Path.makeString(classpath) :: Nil + + private def configLogged(log: Logger): ForkOptions = { + if (config.outputStrategy.isDefined) config + else config.withOutputStrategy(OutputStrategy.LoggedOutput(log)) + } + + private def scalaOptions( + mainClass: String, + classpath: Seq[File], + options: Seq[String] + ): Seq[String] = + "-classpath" :: Path.makeString(classpath) :: mainClass :: options.toList } class Run(private[sbt] val newLoader: Seq[File] => ClassLoader, trapExit: Boolean) diff --git a/sbt b/sbt index 08041983c..19505a818 100755 --- a/sbt +++ b/sbt @@ -1,7 +1,7 @@ #!/usr/bin/env bash set +e -declare builtin_sbt_version="1.5.4" +declare builtin_sbt_version="1.6.2" declare -a residual_args declare -a java_args declare -a scalac_args @@ -10,6 +10,7 @@ declare -a sbt_options declare -a print_version declare -a print_sbt_version declare -a print_sbt_script_version +declare -a shutdownall declare -a original_args declare java_cmd=java declare java_version @@ -21,6 +22,7 @@ declare sbt_verbose= declare sbt_debug= declare build_props_sbt_version= declare use_sbtn= +declare no_server= declare sbtn_command="$SBTN_CMD" declare sbtn_version="1.4.7" @@ -124,6 +126,9 @@ download_url () { elif command -v wget > /dev/null; then wget --quiet -O "$jar" "$url" exit_code=$? + else + echoerr "failed to download $url: Neither curl nor wget is avaialble" + exit 2 fi $(exit "$exit_code") && [[ -f "$jar" ]] } || { @@ -502,6 +507,12 @@ run() { elif [[ $print_version ]]; then execRunner "$java_cmd" -jar "$sbt_jar" "sbtVersion" | tail -1 | sed -e 's/\[info\]/sbt version in this project:/g' echo "sbt script version: $init_sbt_version" + elif [[ $shutdownall ]]; then + local sbt_processes=( $(jps -v | grep sbt-launch | cut -f1 -d ' ') ) + for procId in "${sbt_processes[@]}"; do + kill -9 $procId + done + echo "shutdown ${#sbt_processes[@]} sbt processes" else # run sbt execRunner "$java_cmd" \ @@ -540,6 +551,7 @@ Usage: `basename "$0"` [options] -V | --version print sbt version information --numeric-version print the numeric sbt version (sbt sbtVersion) --script-version print the version of sbt script + shutdownall shutdown all running sbt-launch processes -d | --debug set sbt log level to debug -debug-inc | --debug-inc enable extra debugging for the incremental debugger @@ -579,7 +591,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. @@ -631,6 +642,7 @@ map_args () { -traces|--traces) options=( "${options[@]}" "-Dsbt.traces=true" ) && shift ;; --supershell=*) options=( "${options[@]}" "-Dsbt.supershell=${1:13}" ) && shift ;; -supershell=*) options=( "${options[@]}" "-Dsbt.supershell=${1:12}" ) && shift ;; + -no-server|--no-server) options=( "${options[@]}" "-Dsbt.io.virtual=false" "-Dsbt.server.autostart=false" ) && shift ;; --color=*) options=( "${options[@]}" "-Dsbt.color=${1:8}" ) && shift ;; -color=*) options=( "${options[@]}" "-Dsbt.color=${1:7}" ) && shift ;; -no-share|--no-share) options=( "${options[@]}" "${noshare_opts[@]}" ) && shift ;; @@ -655,6 +667,7 @@ process_args () { -V|-version|--version) print_version=1 && shift ;; --numeric-version) print_sbt_version=1 && shift ;; --script-version) print_sbt_script_version=1 && shift ;; + shutdownall) shutdownall=1 && shift ;; -d|-debug|--debug) sbt_debug=1 && addSbt "-debug" && shift ;; -client|--client) use_sbtn=1 && shift ;; --server) use_sbtn=0 && shift ;; 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/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/actions/cross-multiproject/test b/sbt-app/src/sbt-test/actions/cross-multiproject/test index 68b8383d5..baf4db9b8 100644 --- a/sbt-app/src/sbt-test/actions/cross-multiproject/test +++ b/sbt-app/src/sbt-test/actions/cross-multiproject/test @@ -41,3 +41,24 @@ $ exists lib/target/scala-2.13 -$ exists lib/target/scala-2.12 # -$ exists sbt-foo/target/scala-2.12 -$ exists sbt-foo/target/scala-2.13 + +# test wildcard switching (2.12) +> clean +> ++ 2.12.* -v compile +$ exists lib/target/scala-2.12 +-$ exists lib/target/scala-2.13 +$ exists sbt-foo/target/scala-2.12 +-$ exists sbt-foo/target/scala-2.13 + +# test wildcard switching (2.13) +> clean +> ++ 2.13.* -v compile +$ exists lib/target/scala-2.13 +-$ exists lib/target/scala-2.12 +# -$ exists sbt-foo/target/scala-2.12 +-$ exists sbt-foo/target/scala-2.13 + +# test wildcard switching (no matches) +-> ++ 3.* +# test wildcard switching (multiple matches) +-> ++ 2.* 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/asciiGraphWidth/build.sbt b/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/build.sbt new file mode 100644 index 000000000..aa4780397 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/build.sbt @@ -0,0 +1,24 @@ +ThisBuild / version := "0.1.0-SNAPSHOT" +ThisBuild / scalaVersion := "2.13.5" + +name := "asciiGraphWidthSpecs" + +lazy val whenIsDefault = (project in file("when-is-default")) + .settings( + libraryDependencies += "org.typelevel" %% "cats-effect" % "3.1.0", + check := checkTask.value + ) +lazy val whenIs20 = (project in file("when-is-20")) + .settings( + asciiGraphWidth := 20, + libraryDependencies += "org.typelevel" %% "cats-effect" % "3.1.0", + check := checkTask.value + ) + +lazy val check = taskKey[Unit]("check") +lazy val checkTask = Def.task { + val context = thisProject.value + val expected = IO.read(file(s"${context.base}/expected.txt")) + val actual = (Compile / dependencyTree / asString).value + require(actual == expected, s"${context.id} is failed.") +} diff --git a/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/project/plugins.sbt b/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/project/plugins.sbt new file mode 100644 index 000000000..93c66d2a9 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/project/plugins.sbt @@ -0,0 +1 @@ +addDependencyTreePlugin diff --git a/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/test b/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/test new file mode 100644 index 000000000..15675b169 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/test @@ -0,0 +1 @@ +> check diff --git a/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/when-is-20/expected.txt b/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/when-is-20/expected.txt new file mode 100644 index 000000000..bad2b603a --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/when-is-20/expected.txt @@ -0,0 +1,13 @@ +whenis20:whenis20_.. + +-org.typelevel:.. + +-org.typeleve.. + | +-org.typele.. + | +-org.type.. + | +-org.type.. + | + +-org.typeleve.. + +-org.typele.. + +-org.type.. + +-org.ty.. + +-org.ty.. + \ No newline at end of file diff --git a/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/when-is-default/expected.txt b/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/when-is-default/expected.txt new file mode 100644 index 000000000..284feffc8 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-graph/asciiGraphWidth/when-is-default/expected.txt @@ -0,0 +1,13 @@ +whenisdefault:whenisdefault_2.13:0.1.0-SNAPSHOT [S] + +-org.typelevel:cats-effect_2.13:3.1.0 [S] + +-org.typelevel:cats-effect-kernel_2.13:3.1.0 [S] + | +-org.typelevel:cats-core_2.13:2.6.0 [S] + | +-org.typelevel:cats-kernel_2.13:2.6.0 [S] + | +-org.typelevel:simulacrum-scalafix-annotations_2.13:0.5.4 [S] + | + +-org.typelevel:cats-effect-std_2.13:3.1.0 [S] + +-org.typelevel:cats-effect-kernel_2.13:3.1.0 [S] + +-org.typelevel:cats-core_2.13:2.6.0 [S] + +-org.typelevel:cats-kernel_2.13:2.6.0 [S] + +-org.typelevel:simulacrum-scalafix-annotations_2.13:0.5.4 [S] + \ No newline at end of file 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 b2aec7b01..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", @@ -10,7 +10,7 @@ TaskKey[Unit]("check") := { val graph = (Test / dependencyTree / asString).value def sanitize(str: String): String = str.split('\n').drop(1).map(_.trim).mkString("\n") val expectedGraph = - """default:default-e95e05_2.9.2:0.1-SNAPSHOT [S] + """default:default-e95e05_2.12:0.1-SNAPSHOT [S] | +-ch.qos.logback:logback-classic:1.0.7 | | +-ch.qos.logback:logback-core:1.0.7 | | +-org.slf4j:slf4j-api:1.6.6 (evicted by: 1.7.2) diff --git a/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/pending b/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/test similarity index 100% rename from sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/pending rename to sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/test 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/dependency-graph/toFileSubTask/pending b/sbt-app/src/sbt-test/dependency-graph/toFileSubTask/test similarity index 100% rename from sbt-app/src/sbt-test/dependency-graph/toFileSubTask/pending rename to sbt-app/src/sbt-test/dependency-graph/toFileSubTask/test 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/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") } diff --git a/sbt-app/src/sbt-test/project/remove/build.sbt b/sbt-app/src/sbt-test/project/remove/build.sbt index d3ae2985d..5a16e59d8 100644 --- a/sbt-app/src/sbt-test/project/remove/build.sbt +++ b/sbt-app/src/sbt-test/project/remove/build.sbt @@ -1,6 +1,8 @@ val intsTask = taskKey[Seq[Int]]("A seq of ints task") val intsSetting = settingKey[Seq[Int]]("A seq of ints setting") val intsFromScalaV = settingKey[Seq[Int]]("a seq of ints from scalaVersion") +val intsSetSetting = settingKey[Set[Int]]("A set of ints setting") +val stringIntMapSetting = settingKey[Map[String, Int]]("A map of string to int setting") scalaVersion := "2.11.6" @@ -22,9 +24,19 @@ intsFromScalaV --= { if (scalaVersion.value == "2.11.6") Seq(1, 2) else Seq(4) } intsFromScalaV -= { if (scalaVersion.value == "2.11.6") Option(6) else None } intsFromScalaV --= { if (scalaVersion.value == "2.11.6") Option(7) else None } +intsSetSetting := Set(1, 2, 3, 4, 5, 6, 7) +intsSetSetting -= 3 +intsSetSetting --= Set(1, 2) + +stringIntMapSetting := Map("a" -> 1, "b" -> 2 , "c" -> 3, "d" -> 4, "e" -> 5) +stringIntMapSetting -= "c" +stringIntMapSetting --= Seq("a", "b") + val check = taskKey[Unit]("Runs the check") check := { assert(intsTask.value == Seq(4, 5), s"intsTask should be Seq(4, 5) but is ${intsTask.value}") assert(intsSetting.value == Seq(4, 5), s"intsSetting should be Seq(4, 5) but is ${intsSetting.value}") assert(intsFromScalaV.value == Seq(4, 5), s"intsFromScalaV should be Seq(4, 5) but is ${intsFromScalaV.value}") + assert(intsSetSetting.value == Set(4, 5, 6, 7), s"intsSetSetting should be Set(4, 5, 6, 7) but is ${intsSetSetting.value}") + assert(stringIntMapSetting.value == Map("d" -> 4, "e" -> 5), s"stringIntMapSetting should be Map(d -> 4, e -> 5) but is ${stringIntMapSetting.value}") } 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/scripted-java-home/build.sbt b/sbt-app/src/sbt-test/project/scripted-java-home/build.sbt new file mode 100644 index 000000000..207694da1 --- /dev/null +++ b/sbt-app/src/sbt-test/project/scripted-java-home/build.sbt @@ -0,0 +1,23 @@ +lazy val scriptedJavaVersion = settingKey[Long]("") + +lazy val root = (project in file(".")) + .enablePlugins(SbtPlugin) + .settings( + scriptedJavaVersion := { + val versions = discoveredJavaHomes.value + .map { case (jv, _) => JavaVersion(jv).numbers } + .collect { + case Vector(1L, ver, _*) => ver + case Vector(ver, _*) => ver + } + if (versions.isEmpty) sys.error("No Java versions discovered") + else versions.max + }, + commands += Command.command("setJavaVersion") { state => + val extracted = Project.extract(state) + import extracted._ + val jv = (currentRef / scriptedJavaVersion).get(structure.data).get + s"java++ $jv!" :: state + }, + scriptedLaunchOpts += s"-Dscripted.java.version=${scriptedJavaVersion.value}" + ) diff --git a/sbt-app/src/sbt-test/project/scripted-java-home/changes/build.sbt b/sbt-app/src/sbt-test/project/scripted-java-home/changes/build.sbt new file mode 100644 index 000000000..c72b1c31d --- /dev/null +++ b/sbt-app/src/sbt-test/project/scripted-java-home/changes/build.sbt @@ -0,0 +1,13 @@ +lazy val check = taskKey[Unit]("check") + +lazy val root = (project in file(".")) + .settings( + check := { + val version = sys.props("java.version").stripPrefix("1.").takeWhile(_.isDigit) + val expected = sys.props("scripted.java.version") + assert( + version == expected, + s"Expected Java version is '$expected', but actual is '$version'" + ) + } + ) diff --git a/sbt-app/src/sbt-test/project/scripted-java-home/changes/test b/sbt-app/src/sbt-test/project/scripted-java-home/changes/test new file mode 100644 index 000000000..15675b169 --- /dev/null +++ b/sbt-app/src/sbt-test/project/scripted-java-home/changes/test @@ -0,0 +1 @@ +> check diff --git a/sbt-app/src/sbt-test/project/scripted-java-home/test b/sbt-app/src/sbt-test/project/scripted-java-home/test new file mode 100644 index 000000000..c41dd3978 --- /dev/null +++ b/sbt-app/src/sbt-test/project/scripted-java-home/test @@ -0,0 +1,5 @@ +$ copy-file changes/build.sbt src/sbt-test/group/name/build.sbt +$ copy-file changes/test src/sbt-test/group/name/test + +> setJavaVersion +> scripted 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/run/fork/src/main/scala/ForkFail.scala b/sbt-app/src/sbt-test/run/fork/src/main/scala/ForkFail.scala index 847230c81..a1d489232 100644 --- a/sbt-app/src/sbt-test/run/fork/src/main/scala/ForkFail.scala +++ b/sbt-app/src/sbt-test/run/fork/src/main/scala/ForkFail.scala @@ -1,6 +1,6 @@ object ForkTest { def main(args:Array[String]): Unit = { - val name = Option(System.getenv("flag.name")) getOrElse("flag") + val name = sys.env.getOrElse("flag.name", "flag") println("Name: " + name) val cwd = (new java.io.File(name)).getAbsoluteFile cwd.getParentFile.mkdirs() 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/fork2/changes/Test.scala b/sbt-app/src/sbt-test/tests/fork2/changes/Test.scala index 6706da71f..f2d45c34f 100644 --- a/sbt-app/src/sbt-test/tests/fork2/changes/Test.scala +++ b/sbt-app/src/sbt-test/tests/fork2/changes/Test.scala @@ -1,7 +1,7 @@ import org.scalatest.FlatSpec class Test extends FlatSpec { - val v = Option(System.getenv("tests.max.value")) getOrElse Int.MaxValue + val v = sys.env.getOrElse("tests.max.value", Int.MaxValue) "A simple equation" should "hold" in { assert(Int.MaxValue == v) } 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/sbt-app/src/sbt-test/tests/test-quick/build.sbt b/sbt-app/src/sbt-test/tests/test-quick/build.sbt index 0fb62b441..9fd396d27 100644 --- a/sbt-app/src/sbt-test/tests/test-quick/build.sbt +++ b/sbt-app/src/sbt-test/tests/test-quick/build.sbt @@ -1,9 +1,8 @@ val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" -val scalaxml = "org.scala-lang.modules" %% "scala-xml" % "1.1.1" ThisBuild / scalaVersion := "2.12.12" lazy val root = (project in file(".")) .settings( - libraryDependencies ++= List(scalaxml, scalatest), + libraryDependencies += scalatest % Test, Test / parallelExecution := false ) diff --git a/sbt-app/src/sbt-test/tests/test-quick/changed/MathFunction.scala b/sbt-app/src/sbt-test/tests/test-quick/changed/MathFunction.scala new file mode 100644 index 000000000..1be75e613 --- /dev/null +++ b/sbt-app/src/sbt-test/tests/test-quick/changed/MathFunction.scala @@ -0,0 +1,3 @@ +object MathFunction { + def times2(i: Int): Int = 2 * 2 +} diff --git a/sbt-app/src/sbt-test/tests/test-quick/src/main/scala/MathFunction.scala b/sbt-app/src/sbt-test/tests/test-quick/src/main/scala/MathFunction.scala new file mode 100644 index 000000000..08d0ec501 --- /dev/null +++ b/sbt-app/src/sbt-test/tests/test-quick/src/main/scala/MathFunction.scala @@ -0,0 +1,3 @@ +object MathFunction { + def times2(i: Int): Int = i * 2 +} diff --git a/sbt-app/src/sbt-test/tests/test-quick/src/test/scala/Create.scala b/sbt-app/src/sbt-test/tests/test-quick/src/test/scala/Create.scala index 121de95b0..651807d8d 100644 --- a/sbt-app/src/sbt-test/tests/test-quick/src/test/scala/Create.scala +++ b/sbt-app/src/sbt-test/tests/test-quick/src/test/scala/Create.scala @@ -1,11 +1,10 @@ import org.scalatest.FlatSpec -import org.scalatest.matchers.ShouldMatchers -class Create extends FlatSpec with ShouldMatchers with Base { +class Create extends FlatSpec with Base { "a file" should "not exist" in { A(new B).foo - marker.exists should equal(false) - marker.createNewFile() should equal (true) + assert(marker.exists == false) + assert(marker.createNewFile() == true) } } diff --git a/sbt-app/src/sbt-test/tests/test-quick/src/test/scala/Delete.scala b/sbt-app/src/sbt-test/tests/test-quick/src/test/scala/Delete.scala index cd2eb6617..ff1caacb1 100644 --- a/sbt-app/src/sbt-test/tests/test-quick/src/test/scala/Delete.scala +++ b/sbt-app/src/sbt-test/tests/test-quick/src/test/scala/Delete.scala @@ -1,9 +1,8 @@ import org.scalatest.FlatSpec -import org.scalatest.matchers.ShouldMatchers -class Delete extends FlatSpec with ShouldMatchers with Base { +class Delete extends FlatSpec with Base { "a file" should "exist" in { - marker.exists should equal(true) + assert(marker.exists == true) marker.delete() } diff --git a/sbt-app/src/sbt-test/tests/test-quick/src/test/scala/MathFunctionSpec.scala b/sbt-app/src/sbt-test/tests/test-quick/src/test/scala/MathFunctionSpec.scala new file mode 100644 index 000000000..7ee6336da --- /dev/null +++ b/sbt-app/src/sbt-test/tests/test-quick/src/test/scala/MathFunctionSpec.scala @@ -0,0 +1,7 @@ +import org.scalatest.FlatSpec + +class MathFunctionTest extends FlatSpec { + "times2" should "double the input" in { + assert(MathFunction.times2(4) == 8) + } +} diff --git a/sbt-app/src/sbt-test/tests/test-quick/disabled b/sbt-app/src/sbt-test/tests/test-quick/test similarity index 82% rename from sbt-app/src/sbt-test/tests/test-quick/disabled rename to sbt-app/src/sbt-test/tests/test-quick/test index d88f8a185..c86f0276f 100644 --- a/sbt-app/src/sbt-test/tests/test-quick/disabled +++ b/sbt-app/src/sbt-test/tests/test-quick/test @@ -32,3 +32,9 @@ $ sleep 2000 -> testQuick Create > testQuick Delete > testQuick Create + +# https://github.com/sbt/sbt/issues/5504 +$ copy-file changed/MathFunction.scala src/test/scala/MathFunction.scala +> compile +$ sleep 2000 +-> testQuick MathFunctionTest diff --git a/sbt-app/src/sbt-test/watch/on-error/build.sbt b/sbt-app/src/sbt-test/watch/on-error/build.sbt new file mode 100644 index 000000000..3e1169f6d --- /dev/null +++ b/sbt-app/src/sbt-test/watch/on-error/build.sbt @@ -0,0 +1,3 @@ +import sbt.watch.task.Build + +val root = Build.root 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-error/test b/sbt-app/src/sbt-test/watch/on-error/test new file mode 100644 index 000000000..32a8094d0 --- /dev/null +++ b/sbt-app/src/sbt-test/watch/on-error/test @@ -0,0 +1,7 @@ +# This tests that we can override the state transformation in the watch task +# In the build, watchOnEvent should return CancelWatch which should be successful, but we +# override watchTasks to fail the state instead + +-> ~ root / setStringValue foo.txt bar + +> checkStringValue foo.txt bar 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 index 3e1169f6d..798f18016 100644 --- a/sbt-app/src/sbt-test/watch/on-termination/build.sbt +++ b/sbt-app/src/sbt-test/watch/on-termination/build.sbt @@ -1,3 +1,15 @@ -import sbt.watch.task.Build - -val root = Build.root +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 index 32a8094d0..1634ee847 100644 --- a/sbt-app/src/sbt-test/watch/on-termination/test +++ b/sbt-app/src/sbt-test/watch/on-termination/test @@ -1,7 +1,8 @@ -# This tests that we can override the state transformation in the watch task -# In the build, watchOnEvent should return CancelWatch which should be successful, but we -# override watchTasks to fail the state instead +$ exists foo.txt +> ~compile +$ absent foo.txt +> set watchOnIteration := { (_, _, _) => new Watch.HandleError(new IllegalStateException("fail")) } +$ exists bar.txt +-> ~compile +$ absent bar.txt --> ~ root / setStringValue foo.txt bar - -> checkStringValue foo.txt bar diff --git a/sbt-app/src/test/scala/sbt/IllegalReferenceSpec.scala b/sbt-app/src/test/scala/sbt/IllegalReferenceSpec.scala index e94817b57..de4dfb96c 100644 --- a/sbt-app/src/test/scala/sbt/IllegalReferenceSpec.scala +++ b/sbt-app/src/test/scala/sbt/IllegalReferenceSpec.scala @@ -8,11 +8,11 @@ package sbt import org.scalatest -import org.scalatest.{ TestData, fixture } +import org.scalatest.{ TestData, fixture, funsuite } import scala.tools.reflect.{ FrontEnd, ToolBoxError } -class IllegalReferenceSpec extends fixture.FunSuite with fixture.TestDataFixture { +class IllegalReferenceSpec extends funsuite.FixtureAnyFunSuite with fixture.TestDataFixture { private def toolboxClasspath(td: TestData): String = td.configMap.get("sbt.server.classpath") match { case Some(s: String) => s diff --git a/sbt-app/src/test/scala/sbt/RunFromSourceMain.scala b/sbt-app/src/test/scala/sbt/RunFromSourceMain.scala index ae6240a8b..edac09a33 100644 --- a/sbt-app/src/test/scala/sbt/RunFromSourceMain.scala +++ b/sbt-app/src/test/scala/sbt/RunFromSourceMain.scala @@ -14,7 +14,6 @@ import sbt.util.LoggerContext import scala.annotation.tailrec import scala.sys.process.Process -import sbt.internal.SysProp object RunFromSourceMain { def fork( @@ -44,7 +43,7 @@ object RunFromSourceMain { implicit val runner = new ForkRun(fo) val options = Vector(workingDirectory.toString, scalaVersion, sbtVersion, cp.mkString(pathSeparator)) - val context = LoggerContext(useLog4J = SysProp.useLog4J) + val context = LoggerContext() val log = context.logger("RunFromSourceMain.fork", None, None) try runner.fork("sbt.RunFromSourceMain", cp, options, log) finally context.close() @@ -58,7 +57,7 @@ object RunFromSourceMain { case Array(wd, scalaVersion, sbtVersion, classpath, args @ _*) => System.setProperty("jna.nosys", "true") if (args.exists(_.startsWith("<"))) System.setProperty("sbt.io.virtual", "false") - val context = LoggerContext(useLog4J = SysProp.useLog4J) + val context = LoggerContext() try run(file(wd), scalaVersion, sbtVersion, classpath, args, context) finally context.close() } diff --git a/scripted-sbt-redux/src/main/scala/sbt/scriptedtest/RemoteSbtCreator.scala b/scripted-sbt-redux/src/main/scala/sbt/scriptedtest/RemoteSbtCreator.scala index 41044871f..b35b6a396 100644 --- a/scripted-sbt-redux/src/main/scala/sbt/scriptedtest/RemoteSbtCreator.scala +++ b/scripted-sbt-redux/src/main/scala/sbt/scriptedtest/RemoteSbtCreator.scala @@ -27,14 +27,22 @@ final class LauncherBasedRemoteSbtCreator( directory: File, launcher: File, log: Logger, - launchOpts: Seq[String] = Nil, + javaCommand: String, + launchOpts: Seq[String], ) extends RemoteSbtCreator { - def newRemote(server: IPC.Server) = { + def this( + directory: File, + launcher: File, + log: Logger, + launchOpts: Seq[String] = Nil, + ) = this(directory, launcher, log, "java", launchOpts) + + def newRemote(server: IPC.Server): Process = { val launcherJar = launcher.getAbsolutePath val globalBase = "-Dsbt.global.base=" + (new File(directory, "global")).getAbsolutePath val scripted = "-Dsbt.scripted=true" val args = List("<" + server.port) - val cmd = "java" :: launchOpts.toList ::: globalBase :: scripted :: "-jar" :: launcherJar :: args ::: Nil + val cmd = javaCommand :: launchOpts.toList ::: globalBase :: scripted :: "-jar" :: launcherJar :: args ::: Nil val io = BasicIO(false, log).withInput(_.close()) val p = Process(cmd, directory) run (io) val thread = new Thread() { override def run() = { p.exitValue(); server.close() } } @@ -46,11 +54,21 @@ final class LauncherBasedRemoteSbtCreator( final class RunFromSourceBasedRemoteSbtCreator( directory: File, log: Logger, - launchOpts: Seq[String] = Nil, + javaCommand: String, + launchOpts: Seq[String], scalaVersion: String, sbtVersion: String, classpath: Seq[File], ) extends RemoteSbtCreator { + def this( + directory: File, + log: Logger, + launchOpts: Seq[String] = Nil, + scalaVersion: String, + sbtVersion: String, + classpath: Seq[File], + ) = this(directory, log, "java", launchOpts, scalaVersion, sbtVersion, classpath) + def newRemote(server: IPC.Server): Process = { val globalBase = "-Dsbt.global.base=" + new File(directory, "global").getAbsolutePath val scripted = "-Dsbt.scripted=true" @@ -58,7 +76,7 @@ final class RunFromSourceBasedRemoteSbtCreator( val cpString = classpath.mkString(java.io.File.pathSeparator) val args = List(mainClassName, directory.toString, scalaVersion, sbtVersion, cpString, "<" + server.port) - val cmd = "java" :: launchOpts.toList ::: globalBase :: scripted :: "-cp" :: cpString :: args ::: Nil + val cmd = javaCommand :: launchOpts.toList ::: globalBase :: scripted :: "-cp" :: cpString :: args ::: Nil val io = BasicIO(false, log).withInput(_.close()) val p = Process(cmd, directory) run (io) val thread = new Thread() { override def run() = { p.exitValue(); server.close() } } diff --git a/scripted-sbt-redux/src/main/scala/sbt/scriptedtest/ScriptedTests.scala b/scripted-sbt-redux/src/main/scala/sbt/scriptedtest/ScriptedTests.scala index cec073175..5a836b885 100644 --- a/scripted-sbt-redux/src/main/scala/sbt/scriptedtest/ScriptedTests.scala +++ b/scripted-sbt-redux/src/main/scala/sbt/scriptedtest/ScriptedTests.scala @@ -26,8 +26,15 @@ import scala.util.control.NonFatal final class ScriptedTests( resourceBaseDirectory: File, bufferLog: Boolean, + javaCommand: String, launchOpts: Seq[String], ) { + def this( + resourceBaseDirectory: File, + bufferLog: Boolean, + launchOpts: Seq[String], + ) = this(resourceBaseDirectory, bufferLog, "java", launchOpts) + import ScriptedTests.TestRunner private val testResources = new Resources(resourceBaseDirectory) @@ -80,11 +87,12 @@ final class ScriptedTests( val remoteSbtCreator = prop match { case LauncherBased(launcherJar) => - new LauncherBasedRemoteSbtCreator(testDir, launcherJar, buffered, launchOpts) + new LauncherBasedRemoteSbtCreator(testDir, launcherJar, buffered, javaCommand, launchOpts) case RunFromSourceBased(scalaVersion, sbtVersion, classpath) => new RunFromSourceBasedRemoteSbtCreator( testDir, buffered, + javaCommand, launchOpts, scalaVersion, sbtVersion, @@ -383,6 +391,7 @@ class ScriptedRunner { bufferLog, tests, logger, + javaCommand = "java", launchOpts, prescripted = new java.util.ArrayList[File], LauncherBased(launcherJar), @@ -411,6 +420,36 @@ class ScriptedRunner { bufferLog, tests, logger, + javaCommand = "java", + launchOpts, + prescripted, + LauncherBased(launcherJar), + Int.MaxValue, + parallelExecution = false, + ) + } + + /** + * This is the entry point used by SbtPlugin in sbt 2.0.x etc. + * Removing this method will break scripted and sbt plugin cross building. + * See https://github.com/sbt/sbt/issues/3245 + */ + def run( + resourceBaseDirectory: File, + bufferLog: Boolean, + tests: Array[String], + launcherJar: File, + javaCommand: String, + launchOpts: Array[String], + prescripted: java.util.List[File], + ): Unit = { + val logger = TestConsoleLogger() + run( + resourceBaseDirectory, + bufferLog, + tests, + logger, + javaCommand, launchOpts, prescripted, LauncherBased(launcherJar), @@ -440,6 +479,36 @@ class ScriptedRunner { bufferLog, tests, logger, + javaCommand = "java", + launchOpts, + prescripted, + LauncherBased(launcherJar), + instance, + ) + } + + /** + * This is the entry point used by SbtPlugin in sbt 2.0.x etc. + * Removing this method will break scripted and sbt plugin cross building. + * See https://github.com/sbt/sbt/issues/3245 + */ + def runInParallel( + resourceBaseDirectory: File, + bufferLog: Boolean, + tests: Array[String], + launcherJar: File, + javaCommand: String, + launchOpts: Array[String], + prescripted: java.util.List[File], + instance: Int, + ): Unit = { + val logger = TestConsoleLogger() + runInParallel( + resourceBaseDirectory, + bufferLog, + tests, + logger, + javaCommand, launchOpts, prescripted, LauncherBased(launcherJar), @@ -466,6 +535,7 @@ class ScriptedRunner { bufferLog, tests, logger, + javaCommand = "java", launchOpts, prescripted, RunFromSourceBased(scalaVersion, sbtVersion, classpath), @@ -477,11 +547,24 @@ class ScriptedRunner { bufferLog: Boolean, tests: Array[String], logger: Logger, + javaCommand: String, launchOpts: Array[String], prescripted: java.util.List[File], prop: RemoteSbtCreatorProp, instances: Int - ) = run(baseDir, bufferLog, tests, logger, launchOpts, prescripted, prop, instances, true) + ): Unit = + run( + baseDir, + bufferLog, + tests, + logger, + javaCommand, + launchOpts, + prescripted, + prop, + instances, + parallelExecution = true + ) @nowarn private[this] def run( @@ -489,6 +572,7 @@ class ScriptedRunner { bufferLog: Boolean, tests: Array[String], logger: Logger, + javaCommand: String, launchOpts: Array[String], prescripted: java.util.List[File], prop: RemoteSbtCreatorProp, @@ -496,7 +580,7 @@ class ScriptedRunner { parallelExecution: Boolean, ): Unit = { val addTestFile = (f: File) => { prescripted.add(f); () } - val runner = new ScriptedTests(baseDir, bufferLog, launchOpts) + val runner = new ScriptedTests(baseDir, bufferLog, javaCommand, launchOpts) val sbtVersion = prop match { case LauncherBased(launcherJar) => diff --git a/server-test/src/server-test/buildserver/BaseSource.scala b/server-test/src/server-test/buildserver/BaseSource.scala new file mode 100644 index 000000000..3359bd24f --- /dev/null +++ b/server-test/src/server-test/buildserver/BaseSource.scala @@ -0,0 +1 @@ +object BaseSource diff --git a/server-test/src/server-test/buildserver/build.sbt b/server-test/src/server-test/buildserver/build.sbt index 52691a0a5..fe2cf4db5 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) @@ -26,6 +32,8 @@ lazy val respondError = project.in(file("respond-error")) lazy val util = project +lazy val diagnostics = project + def somethingBad = throw new MessageOnlyException("I am a bad build target") // other build targets should not be affected by this bad build target lazy val badBuildTarget = project.in(file("bad-build-target")) diff --git a/server-test/src/server-test/buildserver/diagnostics/src/main/scala/Diagnostics.scala b/server-test/src/server-test/buildserver/diagnostics/src/main/scala/Diagnostics.scala new file mode 100644 index 000000000..a74b3da77 --- /dev/null +++ b/server-test/src/server-test/buildserver/diagnostics/src/main/scala/Diagnostics.scala @@ -0,0 +1,3 @@ +object Diagnostics { + private val a: Int = 5 +} \ No newline at end of file diff --git a/server-test/src/server-test/buildserver/diagnostics/src/main/scala/PatternMatch.scala b/server-test/src/server-test/buildserver/diagnostics/src/main/scala/PatternMatch.scala new file mode 100644 index 000000000..626a59f8f --- /dev/null +++ b/server-test/src/server-test/buildserver/diagnostics/src/main/scala/PatternMatch.scala @@ -0,0 +1,7 @@ + +class PatternMatch { + val opt: Option[Int] = None + opt match { + case Some(value) => () + } +} 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 diff --git a/server-test/src/test/scala/testpkg/BuildServerTest.scala b/server-test/src/test/scala/testpkg/BuildServerTest.scala index 47aad7b31..1b8fd06fb 100644 --- a/server-test/src/test/scala/testpkg/BuildServerTest.scala +++ b/server-test/src/test/scala/testpkg/BuildServerTest.scala @@ -10,8 +10,13 @@ package testpkg import sbt.internal.bsp._ import sbt.internal.langserver.ErrorCodes import sbt.IO +import sbt.internal.protocol.JsonRpcRequestMessage +import sbt.internal.protocol.codec.JsonRPCProtocol._ +import sjsonnew.JsonWriter +import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter } import java.io.File +import java.net.URI import java.nio.file.Paths import scala.concurrent.duration._ @@ -47,36 +52,40 @@ object BuildServerTest extends AbstractServerTest { test("buildTarget/sources") { _ => val buildTarget = buildTargetUri("util", "Compile") val badBuildTarget = buildTargetUri("badBuildTarget", "Compile") - svr.sendJsonRpc( - s"""{ "jsonrpc": "2.0", "id": "24", "method": "buildTarget/sources", "params": { - | "targets": [{ "uri": "$buildTarget" }, { "uri": "$badBuildTarget" }] - |} }""".stripMargin - ) + svr.sendJsonRpc(buildTargetSources(24, Seq(buildTarget, badBuildTarget))) assert(processing("buildTarget/sources")) val s = svr.waitFor[SourcesResult](10.seconds) val sources = s.items.head.sources.map(_.uri) assert(sources.contains(new File(svr.baseDirectory, "util/src/main/scala").toURI)) } - test("buildTarget/sources SBT") { _ => - val x = s"${svr.baseDirectory.getAbsoluteFile.toURI}#buildserver-build" - svr.sendJsonRpc( - s"""{ "jsonrpc": "2.0", "id": "25", "method": "buildTarget/sources", "params": { - | "targets": [{ "uri": "$x" }] - |} }""".stripMargin + test("buildTarget/sources: base sources") { _ => + val buildTarget = buildTargetUri("buildserver", "Compile") + svr.sendJsonRpc(buildTargetSources(25, Seq(buildTarget))) + assert(processing("buildTarget/sources")) + val s = svr.waitFor[SourcesResult](10.seconds) + val sources = s.items.head.sources + val expectedSource = SourceItem( + new File(svr.baseDirectory, "BaseSource.scala").toURI, + SourceItemKind.File, + generated = false ) + assert(sources.contains(expectedSource)) + } + + test("buildTarget/sources: sbt") { _ => + val x = new URI(s"${svr.baseDirectory.getAbsoluteFile.toURI}#buildserver-build") + svr.sendJsonRpc(buildTargetSources(26, Seq(x))) assert(processing("buildTarget/sources")) val s = svr.waitFor[SourcesResult](10.seconds) val sources = s.items.head.sources.map(_.uri).sorted val expectedSources = Vector( "build.sbt", - "project/", "project/A.scala", "project/src/main/java", "project/src/main/scala-2", "project/src/main/scala-2.12", "project/src/main/scala-sbt-1.0", "project/src/main/scala/", - "project/src/main/scala/B.scala", "project/target/scala-2.12/sbt-1.0/src_managed/main" ).map(rel => new File(svr.baseDirectory.getAbsoluteFile, rel).toURI).sorted assert(sources == expectedSources) @@ -84,11 +93,9 @@ object BuildServerTest extends AbstractServerTest { test("buildTarget/compile") { _ => val buildTarget = buildTargetUri("util", "Compile") - svr.sendJsonRpc( - s"""{ "jsonrpc": "2.0", "id": "32", "method": "buildTarget/compile", "params": { - | "targets": [{ "uri": "$buildTarget" }] - |} }""".stripMargin - ) + + compile(buildTarget, id = 32) + assert(processing("buildTarget/compile")) val res = svr.waitFor[BspCompileResult](10.seconds) assert(res.statusCode == StatusCode.Success) @@ -96,11 +103,8 @@ object BuildServerTest extends AbstractServerTest { test("buildTarget/compile - reports compilation progress") { _ => val buildTarget = buildTargetUri("runAndTest", "Compile") - svr.sendJsonRpc( - s"""{ "jsonrpc": "2.0", "id": "33", "method": "buildTarget/compile", "params": { - | "targets": [{ "uri": "$buildTarget" }] - |} }""".stripMargin - ) + + compile(buildTarget, id = 33) // This doesn't always come back in 10s on CI. assert(svr.waitForString(60.seconds) { s => @@ -118,12 +122,123 @@ 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"""") }) } + test( + "buildTarget/compile [diagnostics] don't publish unnecessary for successful compilation case" + ) { _ => + val buildTarget = buildTargetUri("diagnostics", "Compile") + val mainFile = new File(svr.baseDirectory, "diagnostics/src/main/scala/Diagnostics.scala") + + compile(buildTarget, id = 33) + + assert(svr.waitForString(30.seconds) { s => + s.contains("build/taskFinish") && + s.contains(""""message":"Compiled diagnostics"""") + }) + + // introduce compile error + IO.write( + mainFile, + """|object Diagnostics { + | private val a: Int = "" + |}""".stripMargin + ) + + reloadWorkspace(id = 55) + compile(buildTarget, id = 66) + + assert( + svr.waitForString(30.seconds) { s => + s.contains("build/publishDiagnostics") && + s.contains("Diagnostics.scala") && + s.contains("\"message\":\"type mismatch") + }, + "should send publishDiagnostics with type error for Main.scala" + ) + + // fix compilation error + IO.write( + mainFile, + """|object Diagnostics { + | private val a: Int = 5 + |}""".stripMargin + ) + + reloadWorkspace(id = 77) + compile(buildTarget, id = 88) + + assert( + svr.waitForString(30.seconds) { s => + s.contains("build/publishDiagnostics") && + s.contains("Diagnostics.scala") && + s.contains("\"diagnostics\":[]") + }, + "should send publishDiagnostics with empty diagnostics" + ) + + // trigger no-op compilation + compile(buildTarget, id = 99) + + assert( + !svr.waitForString(20.seconds) { s => + s.contains("build/publishDiagnostics") && + s.contains("Diagnostics.scala") + }, + "shouldn't send publishDiagnostics if there's no change in diagnostics (were empty, are empty)" + ) + } + + test("buildTarget/compile [diagnostics] clear stale warnings") { _ => + val buildTarget = buildTargetUri("diagnostics", "Compile") + val testFile = new File(svr.baseDirectory, s"diagnostics/src/main/scala/PatternMatch.scala") + + compile(buildTarget, id = 33) + + assert( + svr.waitForString(30.seconds) { s => + s.contains("build/publishDiagnostics") && + s.contains("PatternMatch.scala") && + s.contains(""""message":"match may not be exhaustive""") + }, + "should send publishDiagnostics with type error for PatternMatch.scala" + ) + + IO.write( + testFile, + """|class PatternMatch { + | val opt: Option[Int] = None + | opt match { + | case Some(value) => () + | case None => () + | } + |} + |""".stripMargin + ) + + reloadWorkspace(id = 55) + compile(buildTarget, id = 66) + + assert( + svr.waitForString(30.seconds) { s => + s.contains("build/publishDiagnostics") && + s.contains("PatternMatch.scala") && + s.contains("\"diagnostics\":[]") + }, + "should send publishDiagnostics with empty diagnostics" + ) + + } + test("buildTarget/scalacOptions") { _ => val buildTarget = buildTargetUri("util", "Compile") val badBuildTarget = buildTargetUri("badBuildTarget", "Compile") @@ -135,7 +250,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") }) } @@ -149,11 +264,7 @@ object BuildServerTest extends AbstractServerTest { .toFile val buildTarget = buildTargetUri("runAndTest", "Compile") - svr.sendJsonRpc( - s"""{ "jsonrpc": "2.0", "id": "43", "method": "buildTarget/compile", "params": { - | "targets": [{ "uri": "$buildTarget" }] - |} }""".stripMargin - ) + compile(buildTarget, id = 43) svr.waitFor[BspCompileResult](10.seconds) assert(targetDir.list().contains("Main.class")) @@ -211,9 +322,7 @@ object BuildServerTest extends AbstractServerTest { |""".stripMargin ) // reload - svr.sendJsonRpc( - """{ "jsonrpc": "2.0", "id": "52", "method": "workspace/reload"}""" - ) + reloadWorkspace(id = 52) assert( svr.waitForString(10.seconds) { s => s.contains(s""""buildTarget":{"uri":"$metaBuildTarget"}""") && @@ -239,9 +348,7 @@ object BuildServerTest extends AbstractServerTest { |) |""".stripMargin ) - svr.sendJsonRpc( - """{ "jsonrpc": "2.0", "id": "52", "method": "workspace/reload"}""" - ) + reloadWorkspace(id = 52) // assert received an empty diagnostic assert( svr.waitForString(10.seconds) { s => @@ -289,6 +396,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") @@ -301,7 +451,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"""") }) } @@ -344,11 +495,7 @@ object BuildServerTest extends AbstractServerTest { test("buildTarget/compile: report error") { _ => val buildTarget = buildTargetUri("reportError", "Compile") - svr.sendJsonRpc( - s"""{ "jsonrpc": "2.0", "id": "88", "method": "buildTarget/compile", "params": { - | "targets": [{ "uri": "$buildTarget" }] - |} }""".stripMargin - ) + compile(buildTarget, id = 88) assert(svr.waitForString(10.seconds) { s => (s contains s""""buildTarget":{"uri":"$buildTarget"}""") && (s contains """"severity":1""") && @@ -358,11 +505,7 @@ object BuildServerTest extends AbstractServerTest { test("buildTarget/compile: report warning") { _ => val buildTarget = buildTargetUri("reportWarning", "Compile") - svr.sendJsonRpc( - s"""{ "jsonrpc": "2.0", "id": "90", "method": "buildTarget/compile", "params": { - | "targets": [{ "uri": "$buildTarget" }] - |} }""".stripMargin - ) + compile(buildTarget, id = 90) assert(svr.waitForString(10.seconds) { s => (s contains s""""buildTarget":{"uri":"$buildTarget"}""") && (s contains """"severity":2""") && @@ -372,11 +515,7 @@ object BuildServerTest extends AbstractServerTest { test("buildTarget/compile: respond error") { _ => val buildTarget = buildTargetUri("respondError", "Compile") - svr.sendJsonRpc( - s"""{ "jsonrpc": "2.0", "id": "92", "method": "buildTarget/compile", "params": { - | "targets": [{ "uri": "$buildTarget" }] - |} }""".stripMargin - ) + compile(buildTarget, id = 92) assert(svr.waitForString(10.seconds) { s => s.contains(""""id":"92"""") && s.contains(""""error"""") && @@ -421,8 +560,35 @@ object BuildServerTest extends AbstractServerTest { } } - private def buildTargetUri(project: String, config: String): String = - s"${svr.baseDirectory.getAbsoluteFile.toURI}#$project/$config" + private def reloadWorkspace(id: Int): Unit = + svr.sendJsonRpc( + s"""{ "jsonrpc": "2.0", "id": "$id", "method": "workspace/reload"}""" + ) + + private def compile(buildTarget: URI, id: Int): Unit = + compile(buildTarget.toString, id) + + private def compile(buildTarget: String, id: Int): Unit = + svr.sendJsonRpc( + s"""{ "jsonrpc": "2.0", "id": "$id", "method": "buildTarget/compile", "params": { + | "targets": [{ "uri": "$buildTarget" }] + |} }""".stripMargin + ) + + private def buildTargetSources(id: Int, buildTargets: Seq[URI]): String = { + val targets = buildTargets.map(BuildTargetIdentifier.apply).toVector + request(id, "buildTarget/sources", SourcesParams(targets)) + } + + private def request[T: JsonWriter](id: Int, method: String, params: T): String = { + val request = JsonRpcRequestMessage("2.0", id.toString, method, Converter.toJson(params).get) + val json = Converter.toJson(request).get + CompactPrinter(json) + } + + private def buildTargetUri(project: String, config: String): URI = { + new URI(s"${svr.baseDirectory.getAbsoluteFile.toURI}#$project/$config") + } private def metaBuildTarget: String = s"${svr.baseDirectory.getAbsoluteFile.toURI}project/#buildserver-build/Compile" diff --git a/server-test/src/test/scala/testpkg/ClientTest.scala b/server-test/src/test/scala/testpkg/ClientTest.scala index e03bd8a11..b4352062a 100644 --- a/server-test/src/test/scala/testpkg/ClientTest.scala +++ b/server-test/src/test/scala/testpkg/ClientTest.scala @@ -118,14 +118,16 @@ object ClientTest extends AbstractServerTest { "compileIncSetup", "compileIncremental", "compileJava", + "compileOrder", "compileOutputs", "compileProgress", "compileScalaBackend", "compileSplit", + "compilerCache", "compilers", ) - assert(complete("compi") == expected) + assert(complete("compi").toVector == expected) } test("testOnly completions") { _ => val testOnlyExpected = Vector( diff --git a/tasks-standard/src/main/scala/sbt/std/TaskExtra.scala b/tasks-standard/src/main/scala/sbt/std/TaskExtra.scala index 4483b9eec..3d2061d2c 100644 --- a/tasks-standard/src/main/scala/sbt/std/TaskExtra.scala +++ b/tasks-standard/src/main/scala/sbt/std/TaskExtra.scala @@ -200,7 +200,7 @@ trait TaskExtra extends TaskExtra0 { val pio = TaskExtra .processIO(s) .withInput(out => { BasicIO.transferFully(in, out); out.close() }) - (p run pio).exitValue + (p run pio).exitValue() } } @@ -246,7 +246,7 @@ trait TaskExtra extends TaskExtra0 { implicit def processToTask(p: ProcessBuilder)(implicit streams: Task[TaskStreams[_]]): Task[Int] = streams map { s => val pio = TaskExtra.processIO(s) - (p run pio).exitValue + (p run pio).exitValue() } } object TaskExtra extends TaskExtra { diff --git a/tasks-standard/src/test/scala/Test.scala b/tasks-standard/src/test/scala/Test.scala index 72daac5bc..36091c583 100644 --- a/tasks-standard/src/test/scala/Test.scala +++ b/tasks-standard/src/test/scala/Test.scala @@ -20,25 +20,25 @@ object Test extends std.TaskExtra { val b2 = task(true) val c = task("asdf") - val h1 = t3(a, b, c).map { case (aa, bb, cc) => aa + " " + bb + " " + cc } - val h2 = t3(a, b2, c).map { case (aa, bb, cc) => aa + " " + bb + " " + cc } + val h1 = t3(a, b, c).map { case (aa, bb, cc) => s"$aa $bb $cc" } + val h2 = t3(a, b2, c).map { case (aa, bb, cc) => s"$aa $bb $cc" } type Values = (Result[Int], Result[Boolean], Result[String]) val f: Values => Any = { - case (Value(aa), Value(bb), Value(cc)) => aa + " " + bb + " " + cc + case (Value(aa), Value(bb), Value(cc)) => s"$aa $bb $cc" case x => val cs = x.productIterator.toList.collect { case Inc(x) => x } // workaround for double definition bug throw Incomplete(None, causes = cs) } val d2 = t3(a, b2, c) mapR f val f2: Values => Task[Any] = { - case (Value(aa), Value(bb), Value(cc)) => task(aa + " " + bb + " " + cc) + case (Value(aa), Value(bb), Value(cc)) => task(s"$aa $bb $cc") case _ => d3 } lazy val d = t3(a, b, c) flatMapR f2 val f3: Values => Task[Any] = { - case (Value(aa), Value(bb), Value(cc)) => task(aa + " " + bb + " " + cc) + case (Value(aa), Value(bb), Value(cc)) => task(s"$aa $bb $cc") case _ => d2 } lazy val d3 = t3(a, b, c) flatMapR f3 diff --git a/tasks/src/main/scala/sbt/ConcurrentRestrictions.scala b/tasks/src/main/scala/sbt/ConcurrentRestrictions.scala index f57b2f02e..dadb4fa6c 100644 --- a/tasks/src/main/scala/sbt/ConcurrentRestrictions.scala +++ b/tasks/src/main/scala/sbt/ConcurrentRestrictions.scala @@ -235,7 +235,7 @@ object ConcurrentRestrictions { sentinels.toList foreach { s => s.cancel(true) } - sentinels.clear + sentinels.clear() } def submit(node: A, work: () => R): Unit = synchronized { diff --git a/testing/agent/src/main/java/sbt/ForkMain.java b/testing/agent/src/main/java/sbt/ForkMain.java index 2ba171603..8856a09fb 100644 --- a/testing/agent/src/main/java/sbt/ForkMain.java +++ b/testing/agent/src/main/java/sbt/ForkMain.java @@ -27,12 +27,12 @@ public final class ForkMain { // serializables // ----------------------------------------------------------------------------- - static final class SubclassFingerscan implements SubclassFingerprint, Serializable { + public static final class SubclassFingerscan implements SubclassFingerprint, Serializable { private final boolean isModule; private final String superclassName; private final boolean requireNoArgConstructor; - SubclassFingerscan(final SubclassFingerprint print) { + public SubclassFingerscan(final SubclassFingerprint print) { isModule = print.isModule(); superclassName = print.superclassName(); requireNoArgConstructor = print.requireNoArgConstructor(); @@ -51,11 +51,11 @@ public final class ForkMain { } } - static final class AnnotatedFingerscan implements AnnotatedFingerprint, Serializable { + public static final class AnnotatedFingerscan implements AnnotatedFingerprint, Serializable { private final boolean isModule; private final String annotationName; - AnnotatedFingerscan(final AnnotatedFingerprint print) { + public AnnotatedFingerscan(final AnnotatedFingerprint print) { isModule = print.isModule(); annotationName = print.annotationName(); } diff --git a/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala b/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala index 95bbee3b6..ebe66bfcd 100644 --- a/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala +++ b/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala @@ -112,9 +112,9 @@ class JUnitXmlTestsListener(val targetDir: File, legacyTestReport: Boolean, logg val classnameRegex = s"^($name|${name.split('.').last})\\.?".r val result = - {properties} diff --git a/testing/src/main/scala/sbt/TestFramework.scala b/testing/src/main/scala/sbt/TestFramework.scala index 77f5089dd..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 } @@ -256,7 +267,7 @@ object TestFramework { def foreachListenerSafe(f: TestsListener => Unit): () => Unit = () => safeForeach(testsListeners, log)(f) - val startTask = foreachListenerSafe(_.doInit) + val startTask = foreachListenerSafe(_.doInit()) val testTasks = Map(tests.toSeq.flatMap { case (framework, testDefinitions) => diff --git a/testing/src/main/scala/sbt/TestStatusReporter.scala b/testing/src/main/scala/sbt/TestStatusReporter.scala index 38a7987ac..72b1a40ae 100644 --- a/testing/src/main/scala/sbt/TestStatusReporter.scala +++ b/testing/src/main/scala/sbt/TestStatusReporter.scala @@ -19,7 +19,7 @@ import scala.collection.concurrent private[sbt] class TestStatusReporter(f: File) extends TestsListener { private lazy val succeeded: concurrent.Map[String, Long] = TestStatus.read(f) - def doInit = () + def doInit(): Unit = () def startGroup(name: String): Unit = { succeeded remove name; () } def testEvent(event: TestEvent): Unit = () def endGroup(name: String, t: Throwable): Unit = () diff --git a/testing/src/main/scala/sbt/internal/testing/TestLogger.scala b/testing/src/main/scala/sbt/internal/testing/TestLogger.scala index 86ca21de2..0e7bd1437 100644 --- a/testing/src/main/scala/sbt/internal/testing/TestLogger.scala +++ b/testing/src/main/scala/sbt/internal/testing/TestLogger.scala @@ -125,7 +125,7 @@ class TestLogger(val logging: TestLogging) extends TestsListener { import logging.{ global => log, logTest, managed } import sbt.protocol.testing.codec.JsonProtocol._ - def doInit: Unit = managed.logEvent(Level.Info, TestInitEvent()) + def doInit(): Unit = managed.logEvent(Level.Info, TestInitEvent()) def startGroup(name: String): Unit = managed.logEvent(Level.Info, StartTestGroupEvent(name)) diff --git a/util-cache/src/main/scala/sbt/util/SeparatedCache.scala b/util-cache/src/main/scala/sbt/util/SeparatedCache.scala index 9596436fb..c986bfdfc 100644 --- a/util-cache/src/main/scala/sbt/util/SeparatedCache.scala +++ b/util-cache/src/main/scala/sbt/util/SeparatedCache.scala @@ -31,7 +31,7 @@ object SingletonCache { implicit def basicSingletonCache[A: JsonFormat]: SingletonCache[A] = new SingletonCache[A] { - override def read(from: Input): A = from.read[A] + override def read(from: Input): A = from.read[A]() override def write(to: Output, value: A) = to.write(value) } diff --git a/util-cache/src/test/scala/CacheSpec.scala b/util-cache/src/test/scala/CacheSpec.scala index 587efed32..6eab4cdaa 100644 --- a/util-cache/src/test/scala/CacheSpec.scala +++ b/util-cache/src/test/scala/CacheSpec.scala @@ -12,15 +12,15 @@ import sbt.io.syntax._ import CacheImplicits._ -import org.scalatest.FlatSpec +import org.scalatest.flatspec.AnyFlatSpec -class CacheSpec extends FlatSpec { +class CacheSpec extends AnyFlatSpec { "A cache" should "NOT throw an exception if read without being written previously" in { testCache[String, Int] { case (cache, store) => cache(store)("missing") match { - case Hit(_) => fail + case Hit(_) => fail() case Miss(_) => () } } @@ -30,7 +30,7 @@ class CacheSpec extends FlatSpec { testCache[String, Int] { case (cache, store) => cache(store)("missing") match { - case Hit(_) => fail + case Hit(_) => fail() case Miss(update) => update(5) } } @@ -41,13 +41,13 @@ class CacheSpec extends FlatSpec { case (cache, store) => val value = 5 cache(store)("someKey") match { - case Hit(_) => fail + case Hit(_) => fail() case Miss(update) => update(value) } cache(store)("someKey") match { case Hit(read) => assert(read === value); () - case Miss(_) => fail + case Miss(_) => fail() } } } @@ -58,13 +58,13 @@ class CacheSpec extends FlatSpec { val key = "someKey" val value = 5 cache(store)(key) match { - case Hit(_) => fail + case Hit(_) => fail() case Miss(update) => update(value) } cache(store)(key) match { case Hit(read) => assert(read === value); () - case Miss(_) => fail + case Miss(_) => fail() } } } diff --git a/util-cache/src/test/scala/FileInfoSpec.scala b/util-cache/src/test/scala/FileInfoSpec.scala index cfaf3af37..5ea495195 100644 --- a/util-cache/src/test/scala/FileInfoSpec.scala +++ b/util-cache/src/test/scala/FileInfoSpec.scala @@ -9,10 +9,10 @@ package sbt.util import sjsonnew.shaded.scalajson.ast.unsafe._ import sjsonnew._, support.scalajson.unsafe._ -import org.scalatest.FlatSpec +import org.scalatest.flatspec.AnyFlatSpec import sbt.io.IO -class FileInfoSpec extends FlatSpec { +class FileInfoSpec extends AnyFlatSpec { val file = new java.io.File(".").getAbsoluteFile val fileInfo: ModifiedFileInfo = FileModified(file, IO.getModifiedTimeOrZero(file)) val filesInfo = FilesInfo(Set(fileInfo)) diff --git a/util-cache/src/test/scala/SingletonCacheSpec.scala b/util-cache/src/test/scala/SingletonCacheSpec.scala index 568a5e2aa..ab6de3adb 100644 --- a/util-cache/src/test/scala/SingletonCacheSpec.scala +++ b/util-cache/src/test/scala/SingletonCacheSpec.scala @@ -13,9 +13,9 @@ import sbt.io.syntax._ import CacheImplicits._ import sjsonnew.{ Builder, deserializationError, JsonFormat, Unbuilder } -import org.scalatest.FlatSpec +import org.scalatest.flatspec.AnyFlatSpec -class SingletonCacheSpec extends FlatSpec { +class SingletonCacheSpec extends AnyFlatSpec { case class ComplexType(val x: Int, y: String, z: List[Int]) object ComplexType { diff --git a/util-tracking/src/main/scala/sbt/util/Tracked.scala b/util-tracking/src/main/scala/sbt/util/Tracked.scala index 883872f1d..b4bc765c1 100644 --- a/util-tracking/src/main/scala/sbt/util/Tracked.scala +++ b/util-tracking/src/main/scala/sbt/util/Tracked.scala @@ -69,7 +69,7 @@ object Tracked { /** Creates a tracker that provides the output of the most recent invocation of the function */ def lastOutput[I, O: JsonFormat](store: CacheStore)(f: (I, Option[O]) => O): I => O = { in => - val previous = Try { store.read[O] }.toOption + val previous = Try { store.read[O]() }.toOption val next = f(in, previous) store.write(next) next @@ -277,7 +277,7 @@ object Tracked { } def changed(store: CacheStore, value: I): Boolean = - Try { store.read[Long] } match { + Try { store.read[Long]() } match { case USuccess(prev: Long) => Hasher.hash(value) match { case USuccess(keyHash: Int) => keyHash.toLong != prev @@ -321,7 +321,7 @@ class Timestamp(val store: CacheStore, useStartTime: Boolean)(implicit format: J private def now() = System.currentTimeMillis def readTimestamp: Long = - Try { store.read[Long] } getOrElse 0 + Try { store.read[Long]() } getOrElse 0 } @deprecated("Use Tracked.inputChanged and Tracked.outputChanged instead", "1.0.1") @@ -342,7 +342,7 @@ class Changed[O: Equiv: JsonFormat](val store: CacheStore) extends Tracked { def uptodate(value: O): Boolean = { val equiv: Equiv[O] = implicitly - equiv.equiv(value, store.read[O]) + equiv.equiv(value, store.read[O]()) } } diff --git a/util-tracking/src/test/scala/sbt/util/TrackedSpec.scala b/util-tracking/src/test/scala/sbt/util/TrackedSpec.scala index f456931f0..6c640a818 100644 --- a/util-tracking/src/test/scala/sbt/util/TrackedSpec.scala +++ b/util-tracking/src/test/scala/sbt/util/TrackedSpec.scala @@ -7,7 +7,7 @@ package sbt.util -import org.scalatest.FlatSpec +import org.scalatest.flatspec.AnyFlatSpec import sbt.io.IO import sbt.io.syntax._ import sbt.util.CacheImplicits._ @@ -15,7 +15,7 @@ import sjsonnew.{ Builder, JsonWriter } import scala.concurrent.Promise -class TrackedSpec extends FlatSpec { +class TrackedSpec extends AnyFlatSpec { "lastOutput" should "store the last output" in { withStore { store => val value = 5 @@ -160,7 +160,7 @@ class TrackedSpec extends FlatSpec { withStore { store => val beforeCompletion: String = "before-completion" val afterCompletion: String = "after-completion" - val sideEffectCompleted = Promise[Unit] + val sideEffectCompleted = Promise[Unit]() val p0: () => String = () => { if (sideEffectCompleted.isCompleted) { afterCompletion diff --git a/zinc-lm-integration/src/test/scala/sbt/internal/inc/IvyBridgeProviderSpecification.scala b/zinc-lm-integration/src/test/scala/sbt/internal/inc/IvyBridgeProviderSpecification.scala index a4c30bf0c..e465c397a 100644 --- a/zinc-lm-integration/src/test/scala/sbt/internal/inc/IvyBridgeProviderSpecification.scala +++ b/zinc-lm-integration/src/test/scala/sbt/internal/inc/IvyBridgeProviderSpecification.scala @@ -17,6 +17,7 @@ import sbt.librarymanagement.ivy._ import sbt.util.Logger import xsbti.compile.CompilerBridgeProvider import org.scalatest._ +import org.scalatest.matchers.should.Matchers /** * Base class for test suites that must be able to fetch and compile the compiler bridge. @@ -24,7 +25,7 @@ import org.scalatest._ * This is a very good example on how to instantiate the compiler bridge provider. */ abstract class IvyBridgeProviderSpecification - extends fixture.FlatSpec + extends flatspec.FixtureAnyFlatSpec with fixture.TestDataFixture with Matchers { def currentBase: File = new File(".")