diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5ace4600a1..a9173d91a7 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,10 @@ updates: directory: "/" schedule: interval: "weekly" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + target-branch: "develop" + commit-message: + prefix: "[2.x] " diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3c4913f7c9..88c47b39e1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,51 +12,50 @@ jobs: fail-fast: false matrix: include: - - os: ubuntu-20.04 - java: 17 + - os: ubuntu-22.04 + java: 21 distribution: temurin jobtype: 1 - os: ubuntu-latest - java: 17 + java: 21 distribution: temurin jobtype: 2 - os: ubuntu-latest - java: 17 + java: 21 distribution: temurin jobtype: 3 - os: ubuntu-latest - java: 17 + java: 21 distribution: temurin jobtype: 4 - os: ubuntu-latest - java: 17 + java: 21 distribution: temurin jobtype: 5 - - os: ubuntu-latest - java: 8 - distribution: adopt - jobtype: 6 + # - os: ubuntu-latest + # java: 8 + # distribution: adopt + # jobtype: 6 - os: ubuntu-latest java: 8 distribution: adopt jobtype: 7 - os: macos-latest - java: 8 - distribution: adopt + java: 17 + distribution: temurin jobtype: 8 - os: windows-latest java: 8 distribution: adopt jobtype: 9 runs-on: ${{ matrix.os }} + timeout-minutes: 25 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.18 - SCALA_3: 3.1.0 + SCALA_212: 2.12.20 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 + TEST_SBT_VER: 1.10.7 SBT_ETC_FILE: $HOME/etc/sbt/sbtopts JDK11: adopt@1.11.0-9 SPARK_LOCAL_IP: "127.0.0.1" @@ -79,21 +78,23 @@ jobs: uses: actions/checkout@v4 with: repository: sbt/zinc - ref: develop + ref: 1.10.x path: zinc - name: Setup JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: "${{ matrix.distribution }}" java-version: "${{ matrix.java }}" - - name: Set up Python 3.7 - uses: actions/setup-python@v4 + - name: Setup SBT + uses: sbt/setup-sbt@v1 + - name: Set up Python 3.12 + uses: actions/setup-python@v5 with: - python-version: 3.7 + python-version: 3.12 - name: Coursier cache uses: coursier/cache-action@v6 - name: Cache sbt - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.sbt key: ${{ runner.os }}-sbt-cache-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} @@ -140,13 +141,13 @@ jobs: if: ${{ matrix.jobtype == 5 }} shell: bash run: | - ./sbt -v "++2.13.x; all utilControl/test utilRelation/test utilPosition/test; ++$SCALA_3!; all utilControl/test utilRelation/test utilPosition/test" - - name: Build and test (6) + ./sbt -v "++2.13.x; all utilControl/test utilRelation/test utilPosition/test" + - name: Multirepo integration test if: ${{ matrix.jobtype == 6 }} shell: bash run: | # build from fresh IO, LM, and Zinc - BUILD_VERSION="1.5.0-SNAPSHOT" + BUILD_VERSION="${TEST_SBT_VER}-SNAPSHOT" cd io sbt -v -Dsbt.build.version=${BUILD_VERSION} +publishLocal cd ../ @@ -159,11 +160,14 @@ jobs: run: | # test building sbtn on Linux sbt "-Dsbt.io.virtual=false" nativeImage + # smoke test native Image + ./client/target/bin/sbtn --sbt-script=$(pwd)/sbt about + ./client/target/bin/sbtn --sbt-script=$(pwd)/sbt shutdown # 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 + sbt -Dsbt.build.version=$TEST_SBT_VER integrationTest/test cd citest && ./test.sh $HOME/bin/jabba install $JDK11 && exec $HOME/bin/jabba which --home $JDK11 java -Xmx32m -version @@ -173,24 +177,27 @@ jobs: shell: bash run: | # test building sbtn on macOS - sbt "-Dsbt.io.virtual=false" nativeImage + ./sbt "-Dsbt.io.virtual=false" nativeImage # 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 + ../sbt -Dsbt.build.version=$TEST_SBT_VER integrationTest/test + # This fails due to the JLine issue + # cd citest && ./test.sh - name: Build and test (9) if: ${{ matrix.jobtype == 9 }} shell: bash run: | # test building sbtn on Windows sbt "-Dsbt.io.virtual=false" nativeImage + # smoke test native Image + ./client/target/bin/sbtn --sbt-script=$(pwd)/launcher-package/src/universal/bin/sbt.bat about + ./client/target/bin/sbtn --sbt-script=$(pwd)/launcher-package/src/universal/bin/sbt.bat shutdown # 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 + sbt -Dsbt.build.version=$TEST_SBT_VER integrationTest/test cd citest ./test.bat test3/test3.bat diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index f9ead39fdc..c7ec83e666 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -4,21 +4,7 @@ jobs: check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Check CLA - env: - AUTHOR: ${{ github.event.pull_request.user.login }} - run: | - echo "Pull request submitted by $AUTHOR"; - signed=$(curl -s "https://www.lightbend.com/contribute/cla/scala/check/$AUTHOR" | jq -r ".signed"); - if [ "$signed" = "true" ] ; then - echo "CLA check for $AUTHOR successful"; - else - echo "CLA check for $AUTHOR failed"; - echo "Please sign the Scala CLA to contribute to the Scala compiler."; - echo "Go to https://www.lightbend.com/contribute/cla/scala and then"; - echo "comment on the pull request to ask for a new check."; - echo ""; - echo "Check if CLA is signed: https://www.lightbend.com/contribute/cla/scala/check/$AUTHOR"; - exit 1; - fi; + - name: Check CLA + uses: scala/cla-checker@v1 + with: + author: ${{ github.event.pull_request.user.login }} diff --git a/.github/workflows/dependency-graph.yml b/.github/workflows/dependency-graph.yml index 8e5f9dc7ae..387a2a275f 100644 --- a/.github/workflows/dependency-graph.yml +++ b/.github/workflows/dependency-graph.yml @@ -2,7 +2,7 @@ name: Submit Dependency Graph on: push: - branches: [1.9.x] # default branch of the project + branches: [1.10.x, develop] permissions: {} jobs: submit-graph: @@ -13,4 +13,5 @@ jobs: runs-on: ubuntu-latest # or windows-latest, or macOS-latest steps: - uses: actions/checkout@v4 - - uses: scalacenter/sbt-dependency-submission@v2 + - uses: sbt/setup-sbt@v1 + - uses: scalacenter/sbt-dependency-submission@v3 diff --git a/.github/workflows/lock-thread.yml b/.github/workflows/lock-thread.yml new file mode 100644 index 0000000000..7181837cf0 --- /dev/null +++ b/.github/workflows/lock-thread.yml @@ -0,0 +1,23 @@ +name: 'Lock Threads' + +on: + schedule: + # once a week on Saturday + - cron: '0 1 * * 6' + workflow_dispatch: + +permissions: + issues: write + pull-requests: write + discussions: write + +concurrency: + group: lock-threads + +jobs: + action: + runs-on: ubuntu-latest + steps: + - uses: dessant/lock-threads@v5 + with: + process-only: "issues" diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 88e6f48c1b..2ae4630801 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -38,10 +38,10 @@ jobs: uses: actions/checkout@v4 with: repository: sbt/zinc - ref: develop + ref: 1.10.x path: zinc - name: Setup JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: "${{ matrix.distribution }}" java-version: "${{ matrix.java }}" diff --git a/.gitignore b/.gitignore index b36926580a..214fa10f96 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ npm-debug.log metals.sbt launcher-package/citest/freshly-baked .vscode +sbt-launch.jar diff --git a/.java-version b/.java-version deleted file mode 100644 index 6259340971..0000000000 --- a/.java-version +++ /dev/null @@ -1 +0,0 @@ -1.8 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 74e2220032..7fd032b341 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -98,34 +98,6 @@ Pull Requests See below for the branch to work against. -### Adding notes - -Most pull requests should include a "Notes" file which documents the change. This file should reside in the -directory: - - - notes/ - / - .md - -Notes files should have the following contents: - -* Bullet item description under one of the following sections: - - `### Bug fixes` - - `### Improvements` - - `### Fixes with compatibility implications` -* Complete section describing new features. - -### Clean history - -Make sure you document each commit and squash them appropriately. You can use the following guides as a reference: - -* Scala's documentation on [Git Hygiene](https://github.com/scala/scala/tree/v2.12.0-M3#git-hygiene) -* Play's documentation on [Working with Git](https://www.playframework.com/documentation/2.4.4/WorkingWithGit#Squashing-commits) - -Build from source ------------------ - See [DEVELOPING](./DEVELOPING.md) Profiling sbt @@ -133,22 +105,8 @@ Profiling sbt See [PROFILING](./PROFILING.md) -Other notes for maintainers ---------------------------- - -### Publishing VS Code Extensions - -Reference https://code.visualstudio.com/docs/extensions/publish-extension - -``` -$ sbt -> vscodePlugin/compile -> exit -cd vscode-sbt-scala/client -# update version number in vscode-sbt-scala/client/package.json -$ vsce package -$ vsce publish -``` +Other notes +----------- ## Signing the CLA diff --git a/DEVELOPING.md b/DEVELOPING.md index 227415d954..a56641dd08 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -7,17 +7,23 @@ Create a [fork](https://docs.github.com/en/github/getting-started-with-github/fo ### Branch to work against -sbt uses **two or three** branches for development: -Generally the default branch set on Github is what we recommend as the base line for PRs. +sbt uses two or three branches for development: +Use the **default** branch set on Github for bug fixes. -- Next minor branch: `1.$MINOR.x`, where `$MINOR` is next minor version (e.g. `1.9.x` during 1.8.x series) +- Next minor branch: `1.$MINOR.x`, where `$MINOR` is next minor version (e.g. `1.10.x` during 1.9.x series) - Development branch: `develop` -- Stable branch: `1.$MINOR.x`, where `$MINOR` is current minor version (e.g. `1.8.x` during 1.8.x series) +- Stable branch: `1.$MINOR.x`, where `$MINOR` is current minor version (e.g. `1.9.x` during 1.9.x series) -Currently `develop` branch represents the next major version of sbt, i.e. sbt 2. -Next minor branch is where new features can be added as long as it is binary compatible with sbt 1.0. +The `develop` branch represents sbt 2.x, the next major sbt series. +Next minor branch is where new features should be added as long as it is binary compatible with sbt 1.x. The `stable` branch represents the current stable sbt release. Only bug fixes are back-ported to the stable branch. +### Note on supported JDK version for the sbt build + +The sbt build itself currently doesn't support any JDK beyond version 21. You may run into deprecation warnings (which would become build errors due to build configuration) if you use any later JDK version to build sbt. + +If you're using Metals as IDE, also check the `Java Version` setting. The default at the time of writing this is `17`, but this may change in the future, or you may have set it to a later version yourself. (Be aware that Metals may download a JDK in the background if you haven't switch to a local JDK matching the version before changing this setting. Also, this setting is currently only available as a `User` setting, so you can't set it for a single workspace. Don't forget to switch back if you need to for other projects). + ### Instruction to build just sbt Sbt has a number of sub-modules. If the change you are making is just contained in sbt/sbt (not one of the sub-modules), @@ -167,6 +173,13 @@ command. To run a single test, such as the test in ### Random tidbits +### Clean history + +Make sure you document each commit and squash them appropriately. You can use the following guides as a reference: + +* Scala's documentation on [Git Hygiene](https://github.com/scala/scala/tree/v2.12.0-M3#git-hygiene) +* Play's documentation on [Working with Git](https://www.playframework.com/documentation/2.4.4/WorkingWithGit#Squashing-commits) + #### Import statements You'd need alternative DSL import since you can't rely on sbt package object. diff --git a/build.sbt b/build.sbt index 1199e135bc..47c94aebda 100644 --- a/build.sbt +++ b/build.sbt @@ -11,7 +11,7 @@ import scala.util.Try // ThisBuild settings take lower precedence, // but can be shared across the multi projects. ThisBuild / version := { - val v = "1.9.7-SNAPSHOT" + val v = "1.11.1-SNAPSHOT" nightlyVersion.getOrElse(v) } ThisBuild / version2_13 := "2.0.0-SNAPSHOT" @@ -48,7 +48,7 @@ ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" Global / semanticdbEnabled := !(Global / insideCI).value // Change main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala too, if you change this. -Global / semanticdbVersion := "4.7.8" +Global / semanticdbVersion := "4.9.9" val excludeLint = SettingKey[Set[Def.KeyedInitialize[_]]]("excludeLintKeys") Global / excludeLint := (Global / excludeLint).?.value.getOrElse(Set.empty) Global / excludeLint += componentID @@ -176,6 +176,9 @@ def mimaSettingsSince(versions: Seq[String]): Seq[Def.Setting[_]] = Def settings exclude[DirectMissingMethodProblem]("sbt.PluginData.apply"), exclude[DirectMissingMethodProblem]("sbt.PluginData.copy"), exclude[DirectMissingMethodProblem]("sbt.PluginData.this"), + exclude[IncompatibleResultTypeProblem]("sbt.EvaluateTask.executeProgress"), + exclude[DirectMissingMethodProblem]("sbt.Keys.currentTaskProgress"), + exclude[IncompatibleResultTypeProblem]("sbt.PluginData.copy$default$10") ), ) @@ -188,11 +191,11 @@ lazy val sbtRoot: Project = (project in file(".")) minimalSettings, onLoadMessage := { val version = sys.props("java.specification.version") - """ __ __ + """ __ __ | _____/ /_ / /_ | / ___/ __ \/ __/ - | (__ ) /_/ / /_ - | /____/_.___/\__/ + | (__ ) /_/ / /_ + | /____/_.___/\__/ |Welcome to the build for sbt. |""".stripMargin + (if (version != "1.8") @@ -375,8 +378,8 @@ lazy val utilLogging = (project in file("internal") / "util-logging") Seq( jline, jline3Terminal, - jline3JNA, - jline3Jansi, + jline3JNI, + jline3Native, log4jApi, log4jCore, disruptor, @@ -427,6 +430,7 @@ lazy val utilLogging = (project in file("internal") / "util-logging") exclude[MissingTypesProblem]("sbt.internal.util.ConsoleAppender"), exclude[MissingTypesProblem]("sbt.internal.util.BufferedAppender"), exclude[MissingClassProblem]("sbt.internal.util.Terminal$BlockingInputStream$"), + exclude[IncompatibleResultTypeProblem]("sbt.util.LoggerContext#Log4JLoggerContext.loggers"), ), ) .configure(addSbtIO) @@ -667,12 +671,14 @@ lazy val dependencyTreeProj = (project in file("dependency-tree")) crossScalaVersions := Seq(baseScalaVersion), name := "sbt-dependency-tree", publishMavenStyle := true, + sbtPluginPublishLegacyMavenStyle := false, // mimaSettings, mimaPreviousArtifacts := Set.empty, ) // Implementation and support code for defining actions. lazy val actionsProj = (project in file("main-actions")) + .enablePlugins(ContrabandPlugin, JsonCodecPlugin) .dependsOn( completeProj, runProj, @@ -686,8 +692,13 @@ lazy val actionsProj = (project in file("main-actions")) .settings( testedBaseSettings, name := "Actions", + Compile / scalacOptions += "-Xsource:3", libraryDependencies += sjsonNewScalaJson.value, - libraryDependencies += jline3Terminal, + libraryDependencies ++= Seq(gigahorseOkHttp, jline3Terminal), + Compile / managedSourceDirectories += + baseDirectory.value / "src" / "main" / "contraband-scala", + Compile / generateContrabands / sourceManaged := baseDirectory.value / "src" / "main" / "contraband-scala", + Compile / generateContrabands / contrabandFormatsForType := ContrabandConfig.getFormats, mimaSettings, mimaBinaryIssueFilters ++= Seq( // Removed unused private[sbt] nested class @@ -754,7 +765,7 @@ lazy val protocolProj = (project in file("protocol")) // General command support and core commands not specific to a build system lazy val commandProj = (project in file("main-command")) .enablePlugins(ContrabandPlugin, JsonCodecPlugin) - .dependsOn(protocolProj, completeProj, utilLogging) + .dependsOn(protocolProj, completeProj, utilLogging, runProj) .settings( testedBaseSettings, name := "Command", @@ -1067,6 +1078,8 @@ lazy val mainProj = (project in file("main")) exclude[MissingClassProblem]("sbt.internal.server.BuildServerReporter$"), exclude[IncompatibleTemplateDefProblem]("sbt.internal.server.BuildServerReporter"), exclude[MissingClassProblem]("sbt.internal.CustomHttp*"), + exclude[ReversedMissingMethodProblem]("sbt.JobHandle.isAutoCancel"), + exclude[ReversedMissingMethodProblem]("sbt.BackgroundJobService.createWorkingDirectory"), ) ) .configure( @@ -1161,23 +1174,32 @@ lazy val sbtClientProj = (project in file("client")) .dependsOn(commandProj) .settings( commonBaseSettings, - scalaVersion := "2.12.11", // The thin client does not build with 2.12.12 publish / skip := true, name := "sbt-client", mimaPreviousArtifacts := Set.empty, crossPaths := false, exportJars := true, - libraryDependencies += jansi, libraryDependencies += scalatest % Test, Compile / mainClass := Some("sbt.client.Client"), nativeImageReady := { () => () }, - nativeImageVersion := "22.2.0", - nativeImageOutput := target.value / "bin" / "sbtn", + nativeImageVersion := "23.0", + nativeImageJvm := "graalvm-java23", + nativeImageOutput := { + val outputDir = (target.value / "bin").toPath + if (!Files.exists(outputDir)) { + Files.createDirectories(outputDir) + } + outputDir.resolve("sbtn").toFile + }, nativeImageOptions ++= Seq( "--no-fallback", s"--initialize-at-run-time=sbt.client", + // "The current machine does not support all of the following CPU features that are required by + // the image: [CX8, CMOV, FXSR, MMX, SSE, SSE2, SSE3, SSSE3, SSE4_1, SSE4_2, POPCNT, LZCNT, AVX, + // AVX2, BMI1, BMI2, FMA, F16C]." + "-march=compatibility", // "--verbose", "-H:IncludeResourceBundles=jline.console.completer.CandidateListCompletionHandler", "-H:+ReportExceptionStackTraces", @@ -1301,52 +1323,6 @@ lazy val sbtIgnoredProblems = { ) } -def runNpm(command: String, base: File, log: sbt.internal.util.ManagedLogger) = { - import scala.sys.process._ - try { - val exitCode = Process(s"npm $command", Option(base)) ! log - if (exitCode != 0) throw new Exception("Process returned exit code: " + exitCode) - } catch { - case e: java.io.IOException => log.warn("failed to run npm " + e.getMessage) - } -} - -lazy val vscodePlugin = (project in file("vscode-sbt-scala")) - .settings( - bspEnabled := false, - crossPaths := false, - crossScalaVersions := Seq(baseScalaVersion), - publish / skip := true, - Compile / compile := { - val _ = update.value - runNpm("run compile", baseDirectory.value, streams.value.log) - sbt.internal.inc.Analysis.empty - }, - update := { - val old = update.value - val t = target.value / "updated" - val base = baseDirectory.value - val log = streams.value.log - if (t.exists) () - else { - runNpm("install", base, log) - IO.touch(t) - } - old - }, - cleanFiles ++= { - val base = baseDirectory.value - Vector( - target.value / "updated", - base / "node_modules", - base / "client" / "node_modules", - base / "client" / "server", - base / "client" / "out", - base / "server" / "node_modules" - ) filter { _.exists } - } - ) - def scriptedTask(launch: Boolean): Def.Initialize[InputTask[Unit]] = Def.inputTask { val _ = publishLocalBinAll.value val launchJar = s"-Dsbt.launch.jar=${(bundledLauncherProj / Compile / packageBin).value}" @@ -1460,7 +1436,7 @@ lazy val docProjects: ScopeFilter = ScopeFilter( inConfigurations(Compile) ) lazy val javafmtOnCompile = taskKey[Unit]("Formats java sources before compile") -lazy val scriptedProjects = ScopeFilter(inAnyProject -- inProjects(vscodePlugin)) +lazy val scriptedProjects = ScopeFilter(inAnyProject) def customCommands: Seq[Setting[_]] = Seq( commands += Command.command("setupBuildScala212") { state => @@ -1519,7 +1495,9 @@ ThisBuild / pomIncludeRepository := { _ => false } ThisBuild / publishTo := { - val nexus = "https://oss.sonatype.org/" - Some("releases" at nexus + "service/local/staging/deploy/maven2") + val centralSnapshots = "https://central.sonatype.com/repository/maven-snapshots/" + val v = (ThisBuild / version).value + if (v.endsWith("SNAPSHOT")) Some("central-snapshots" at centralSnapshots) + else localStaging.value } ThisBuild / publishMavenStyle := true diff --git a/client/src/main/java/sbt/client/Client.java b/client/src/main/java/sbt/client/Client.java index 6ffe838ffe..491d725b88 100644 --- a/client/src/main/java/sbt/client/Client.java +++ b/client/src/main/java/sbt/client/Client.java @@ -9,19 +9,17 @@ package sbt.client; import sbt.internal.client.NetworkClient; -import java.nio.file.Paths; -import org.fusesource.jansi.AnsiConsole; public class Client { public static void main(final String[] args) { - boolean isWin = System.getProperty("os.name").toLowerCase().startsWith("win"); + boolean hadError = false; try { - if (isWin) AnsiConsole.systemInstall(); NetworkClient.main(args); } catch (final Throwable t) { t.printStackTrace(); + hadError = true; } finally { - if (isWin) AnsiConsole.systemUninstall(); + if (hadError) System.exit(1); } } } 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 324c62dfb2..4e4d3e8b7c 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 @@ -8,10 +8,13 @@ package sbt.internal.util +import java.nio.file.{ Path, Paths } import java.util.Locale import scala.reflect.macros.blackbox import scala.language.experimental.macros +import scala.language.reflectiveCalls +import scala.util.control.NonFatal object Util { def makeList[T](size: Int, value: T): List[T] = List.fill(size)(value) @@ -77,4 +80,50 @@ object Util { class Macro(val c: blackbox.Context) { def ignore(f: c.Tree): c.Expr[Unit] = c.universe.reify({ c.Expr[Any](f).splice; () }) } + + /** + * Given a list of event handlers expressed partial functions, combine them + * together using orElse from the left. + */ + def reduceIntents[A1, A2](intents: PartialFunction[A1, A2]*): PartialFunction[A1, A2] = + intents.toList.reduceLeft(_ orElse _) + + lazy val majorJavaVersion: Int = + try { + val javaVersion = sys.props.get("java.version").getOrElse("1.0") + if (javaVersion.startsWith("1.")) { + javaVersion.split("\\.")(1).toInt + } else { + javaVersion.split("\\.")(0).toInt + } + } catch { + case NonFatal(_) => 0 + } + + private type GetId = { + def getId: Long + } + private type ThreadId = { + def threadId: Long + } + + /** + * Returns current thread id. + * Thread.threadId was added in JDK 19, and deprecated Thread#getId + * https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Thread.html#threadId() + */ + def threadId: Long = + if (majorJavaVersion < 19) { + (Thread.currentThread(): AnyRef) match { + case g: GetId @unchecked => g.getId + } + } else { + (Thread.currentThread(): AnyRef) match { + case g: ThreadId @unchecked => g.threadId + } + } + + lazy val javaHome: Path = + if (sys.props("java.home").endsWith("jre")) Paths.get(sys.props("java.home")).getParent() + else Paths.get(sys.props("java.home")) } 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 20db30ab02..9b8b86d4f2 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 @@ -130,7 +130,7 @@ object LineReader { Option(mask.map(reader.readLine(prompt, _)).getOrElse(reader.readLine(prompt))) } catch { case e: EndOfFileException => - if (terminal == Terminal.console && System.console == null) None + if (terminal == Terminal.console && !Terminal.hasConsole) None else Some("exit") case _: IOError | _: ClosedException => Some("exit") case _: UserInterruptException | _: ClosedByInterruptException | diff --git a/internal/util-control/src/main/scala/sbt/internal/util/ErrorHandling.scala b/internal/util-control/src/main/scala/sbt/internal/util/ErrorHandling.scala index 06160b3cd0..58b460ccbc 100644 --- a/internal/util-control/src/main/scala/sbt/internal/util/ErrorHandling.scala +++ b/internal/util-control/src/main/scala/sbt/internal/util/ErrorHandling.scala @@ -23,9 +23,16 @@ object ErrorHandling { try { Right(f) } catch { - case ex @ (_: Exception | _: StackOverflowError) => Left(ex) - case err @ (_: ThreadDeath | _: VirtualMachineError) => throw err - case x: Throwable => Left(x) + case ex @ (_: Exception | _: StackOverflowError) => + Left(ex) + case err: VirtualMachineError => + throw err + case err if err.getClass.getName == "java.lang.ThreadDeath" => + // ThreadDeath is deprecated + // https://bugs.openjdk.org/browse/JDK-8289610 + throw err + case x: Throwable => + Left(x) } def convert[T](f: => T): Either[Exception, T] = diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala index 58fada63b9..bea64ff9ed 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala @@ -20,7 +20,7 @@ import org.apache.logging.log4j.{ Level => XLevel } import sbt.internal.util.ConsoleAppender._ import sbt.util._ import org.apache.logging.log4j.core.AbstractLogEvent -import org.apache.logging.log4j.message.StringFormatterMessageFactory +import org.apache.logging.log4j.message.SimpleMessageFactory import java.util.concurrent.atomic.AtomicReference object ConsoleLogger { @@ -598,7 +598,7 @@ private[sbt] class ConsoleAppenderFromLog4J( delegate.append(new AbstractLogEvent { override def getLevel(): XLevel = ConsoleAppender.toXLevel(level) override def getMessage(): Message = - StringFormatterMessageFactory.INSTANCE.newMessage(message.toString, Array.empty) + SimpleMessageFactory.INSTANCE.newMessage(message.toString, Array.empty) }) } } diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/JLine3.scala b/internal/util-logging/src/main/scala/sbt/internal/util/JLine3.scala index 6c2a043116..51b2c5e408 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/JLine3.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/JLine3.scala @@ -19,9 +19,8 @@ import org.jline.terminal.{ Attributes, Size, Terminal => JTerminal } import org.jline.terminal.Attributes.{ InputFlag, LocalFlag } import org.jline.terminal.Terminal.SignalHandler import org.jline.terminal.impl.{ AbstractTerminal, DumbTerminal } -import org.jline.terminal.impl.jansi.JansiSupportImpl -import org.jline.terminal.impl.jansi.win.JansiWinSysTerminal -import org.jline.utils.OSUtils +import org.jline.terminal.spi.{ SystemStream, TerminalProvider } +import sbt.internal.util.Terminal.hasConsole import scala.collection.JavaConverters._ import scala.util.Try import java.util.concurrent.LinkedBlockingQueue @@ -29,44 +28,13 @@ import java.util.concurrent.LinkedBlockingQueue private[sbt] object JLine3 { private[util] val initialAttributes = new AtomicReference[Attributes] - private[this] val forceWindowsJansiHolder = new AtomicBoolean(false) - private[sbt] def forceWindowsJansi(): Unit = forceWindowsJansiHolder.set(true) - private[this] def windowsJansi(): org.jline.terminal.Terminal = { - val support = new JansiSupportImpl - val winConsole = support.isWindowsConsole(); - val termType = sys.props.get("org.jline.terminal.type").orElse(sys.env.get("TERM")).orNull - val term = JansiWinSysTerminal.createTerminal( - "console", - termType, - OSUtils.IS_CONEMU, - Charset.forName("UTF-8"), - -1, - false, - SignalHandler.SIG_DFL, - true - ) - term.disableScrolling() - term - } - private val jansi = { - val (major, minor) = - (JansiSupportImpl.getJansiMajorVersion, JansiSupportImpl.getJansiMinorVersion) - (major > 1 || minor >= 18) && Util.isWindows - } private[util] def system: org.jline.terminal.Terminal = { val term = - if (forceWindowsJansiHolder.get) windowsJansi() - else { - // Only use jna on windows. Both jna and jansi use illegal reflective - // accesses on posix system. - org.jline.terminal.TerminalBuilder - .builder() - .system(System.console != null) - .jna(Util.isWindows && !jansi) - .jansi(jansi) - .paused(true) - .build() - } + org.jline.terminal.TerminalBuilder + .builder() + .system(hasConsole) + .paused(true) + .build() initialAttributes.get match { case null => initialAttributes.set(term.getAttributes) case _ => @@ -119,6 +87,11 @@ private[sbt] object JLine3 { } } } + + // returns 'null' if the terminal was created with no provider + override def getProvider(): TerminalProvider = null + // returns 'null' if the terminal is not bound to a system stream. + override def getSystemStream(): SystemStream = null override val input: InputStream = new InputStream { override def read: Int = { val res = term.inputStream match { @@ -180,7 +153,7 @@ private[sbt] object JLine3 { case null => -1 case i => i.toInt } - override def readBuffered(buf: Array[Char]): Int = { + override def readBuffered(buf: Array[Char], off: Int, len: Int, timeout: Long): Int = { if (buffer.isEmpty) fillBuffer() buffer.take match { case i if i == -1 => -1 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 85176a9889..9a1a3c8a87 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 @@ -273,7 +273,7 @@ object Terminal { * the sbt client to detach from the server it launches. */ def close(): Unit = { - if (System.console == null) { + if (!hasConsole) { originalOut.close() originalIn.close() originalErr.close() @@ -350,7 +350,17 @@ object Terminal { 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[sbt] val hasConsole = { + System.console != null && { + try { + val isTerminal = System.console.getClass.getMethod("isTerminal") + isTerminal.invoke(System.console).asInstanceOf[Boolean] + } catch { + case _: NoSuchMethodException => + true + } + } + } private[this] def useColorDefault: Boolean = { // This approximates that both stdin and stdio are connected, // so by default color will be turned off for pipes and redirects. @@ -692,7 +702,7 @@ object Terminal { inputStream.read match { case -1 => case `NO_BOOT_CLIENTS_CONNECTED` => - if (System.console == null) { + if (!Terminal.hasConsole) { result.put(-1) running.set(false) } 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 4c0f42f01e..0fd469a502 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 @@ -11,7 +11,7 @@ package sbt.internal.util import java.io.InputStream import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.atomic.AtomicBoolean -import org.fusesource.jansi.internal.Kernel32 +import org.jline.nativ.Kernel32 import org.jline.utils.InfoCmp.Capability import scala.annotation.tailrec import Terminal.SimpleInputStream diff --git a/internal/util-logging/src/main/scala/sbt/util/Logger.scala b/internal/util-logging/src/main/scala/sbt/util/Logger.scala index 978a36323a..c407f409a8 100644 --- a/internal/util-logging/src/main/scala/sbt/util/Logger.scala +++ b/internal/util-logging/src/main/scala/sbt/util/Logger.scala @@ -40,11 +40,13 @@ abstract class Logger extends xLogger { def success(message: => String): Unit def log(level: Level.Value, message: => String): Unit + def verbose(msg: Supplier[String]): Unit = debug(msg) def debug(msg: Supplier[String]): Unit = log(Level.Debug, msg) def warn(msg: Supplier[String]): Unit = log(Level.Warn, msg) def info(msg: Supplier[String]): Unit = log(Level.Info, msg) def error(msg: Supplier[String]): Unit = log(Level.Error, msg) def trace(msg: Supplier[Throwable]): Unit = trace(msg.get()) + def success(msg: Supplier[String]): Unit = success(msg.get()) def log(level: Level.Value, msg: Supplier[String]): Unit = log(level, msg.get) } 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 c887c786fe..58aa5107a5 100644 --- a/internal/util-logging/src/main/scala/sbt/util/LoggerContext.scala +++ b/internal/util-logging/src/main/scala/sbt/util/LoggerContext.scala @@ -46,7 +46,7 @@ object LoggerContext { case a: AbstractConfiguration => a case _ => throw new IllegalStateException("") } - val loggers = new java.util.Vector[String] + val loggers = new java.util.HashSet[String] private[this] val closed = new AtomicBoolean(false) override def logger( name: String, 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 1de3ff6075..a8bd975446 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 @@ -11,11 +11,15 @@ package internal package scripted import java.io.File +import sbt.nio.file.{ FileTreeView, Glob, PathFilter, RecursiveGlob } import sbt.io.{ IO, Path } import sbt.io.syntax._ import Path._ class FileCommands(baseDirectory: File) extends BasicStatementHandler { + final val OR = "||" + lazy val view = FileTreeView.Ops(FileTreeView.default) + val baseGlob = Glob(baseDirectory) lazy val commands = commandMap def commandMap = Map( @@ -51,8 +55,42 @@ class FileCommands(baseDirectory: File) extends BasicStatementHandler { def spaced[T](l: Seq[T]) = l.mkString(" ") def fromStrings(paths: List[String]) = paths.map(fromString) def fromString(path: String) = new File(baseDirectory, path) + def filterFromStrings(exprs: List[String]): List[PathFilter] = { + def globs(exprs: List[String]): List[PathFilter] = + exprs.map { g => + if (g.startsWith("/")) (Glob(g): PathFilter) + else (Glob(baseDirectory, g): PathFilter) + } + def orGlobs = { + val exprs1 = exprs + .mkString("") + .split(OR) + .filter(_ != OR) + .toList + .map(_.trim) + val combined = globs(exprs1) match { + case Nil => sys.error("unexpected Nil") + case g :: Nil => g + case g :: gs => + gs.foldLeft(g) { + case (acc, g) => acc || g + } + } + List(combined) + } + if (exprs.contains("||")) orGlobs + else globs(exprs) + } + def touch(paths: List[String]): Unit = IO.touch(fromStrings(paths)) - def delete(paths: List[String]): Unit = IO.delete(fromStrings(paths)) + def delete(paths: List[String]): Unit = + IO.delete( + (filterFromStrings(paths) + .flatMap { filter => + view.list(baseGlob / RecursiveGlob, filter) + }) + .map(_._1.toFile) + ) /*def sync(from: String, to: String) = IO.sync(fromString(from), fromString(to), log)*/ def copyFile(from: String, to: String): Unit = @@ -78,13 +116,16 @@ class FileCommands(baseDirectory: File) extends BasicStatementHandler { scriptError(s"$pathA is not newer than $pathB") } } + // use FileTreeView to test if a file with the given filter exists + def exists0(filter: PathFilter): Boolean = + view.list(baseGlob / RecursiveGlob, filter).nonEmpty def exists(paths: List[String]): Unit = { - val notPresent = fromStrings(paths).filter(!_.exists) + val notPresent = filterFromStrings(paths).filter(!exists0(_)) if (notPresent.nonEmpty) scriptError("File(s) did not exist: " + notPresent.mkString("[ ", " , ", " ]")) } def absent(paths: List[String]): Unit = { - val present = fromStrings(paths).filter(_.exists) + val present = filterFromStrings(paths).filter(exists0) if (present.nonEmpty) scriptError("File(s) existed: " + present.mkString("[ ", " , ", " ]")) } diff --git a/launcher-package/build.sbt b/launcher-package/build.sbt index 66eade64d0..b58d8b2770 100755 --- a/launcher-package/build.sbt +++ b/launcher-package/build.sbt @@ -26,7 +26,7 @@ lazy val sbtVersionToRelease = sys.props.getOrElse("sbt.build.version", sys.env. })) lazy val scala210 = "2.10.7" -lazy val scala212 = "2.12.18" +lazy val scala212 = "2.12.20" lazy val scala210Jline = "org.scala-lang" % "jline" % scala210 lazy val jansi = { if (sbtVersionToRelease startsWith "1.") "org.fusesource.jansi" % "jansi" % "1.12" @@ -34,8 +34,8 @@ lazy val jansi = { } lazy val scala212Compiler = "org.scala-lang" % "scala-compiler" % scala212 lazy val scala212Jline = "jline" % "jline" % "2.14.6" -// use the scala-xml version used by the compiler not the latest: https://github.com/scala/scala/blob/v2.12.18/versions.properties#L21 -lazy val scala212Xml = "org.scala-lang.modules" % "scala-xml_2.12" % "2.1.0" +// use the scala-xml version used by the compiler not the latest: https://github.com/scala/scala/blob/v2.12.20/versions.properties +lazy val scala212Xml = "org.scala-lang.modules" % "scala-xml_2.12" % "2.2.0" lazy val sbtActual = "org.scala-sbt" % "sbt" % sbtVersionToRelease lazy val sbt013ExtraDeps = { @@ -70,13 +70,11 @@ val debianBuildId = settingKey[Int]("build id for Debian") val exportRepoUsingCoursier = taskKey[File]("export Maven style repository") val exportRepoCsrDirectory = settingKey[File]("") -val x86MacPlatform = "x86_64-apple-darwin" -val aarch64MacPlatform = "aarch64-apple-darwin" +val universalMacPlatform = "universal-apple-darwin" val x86LinuxPlatform = "x86_64-pc-linux" val aarch64LinuxPlatform = "aarch64-pc-linux" val x86WindowsPlatform = "x86_64-pc-win32" -val x86MacImageName = s"sbtn-$x86MacPlatform" -val aarch64MacImageName = s"sbtn-$aarch64MacPlatform" +val universalMacImageName = s"sbtn-$universalMacPlatform" val x86LinuxImageName = s"sbtn-$x86LinuxPlatform" val aarch64LinuxImageName = s"sbtn-$aarch64LinuxPlatform" val x86WindowsImageName = s"sbtn-$x86WindowsPlatform.exe" @@ -123,42 +121,30 @@ val root = (project in file(".")). file }, // update sbt.sh at root - sbtnVersion := "1.9.0", + sbtnVersion := "1.10.8", sbtnJarsBaseUrl := "https://github.com/sbt/sbtn-dist/releases/download", sbtnJarsMappings := { val baseUrl = sbtnJarsBaseUrl.value val v = sbtnVersion.value - val macosX86ImageTar = s"sbtn-$x86MacPlatform-$v.tar.gz" - val macosAarch64ImageTar = s"sbtn-$aarch64MacPlatform-$v.tar.gz" + val macosUniversalImageTar = s"sbtn-$universalMacPlatform-$v.tar.gz" val linuxX86ImageTar = s"sbtn-$x86LinuxPlatform-$v.tar.gz" val linuxAarch64ImageTar = s"sbtn-$aarch64LinuxPlatform-$v.tar.gz" val windowsImageZip = s"sbtn-$x86WindowsPlatform-$v.zip" val t = target.value - val macosX86Tar = t / macosX86ImageTar - val macosAarch64Tar = t / macosAarch64ImageTar + val macosUniversalTar = t / macosUniversalImageTar val linuxX86Tar = t / linuxX86ImageTar val linuxAarch64Tar = t / linuxAarch64ImageTar val windowsZip = t / windowsImageZip import dispatch.classic._ - if(!macosX86Tar.exists && !isWindows && sbtIncludeSbtn) { - IO.touch(macosX86Tar) - val writer = new java.io.BufferedOutputStream(new java.io.FileOutputStream(macosX86Tar)) - try Http(url(s"$baseUrl/v$v/$macosX86ImageTar") >>> writer) + if(!macosUniversalTar.exists && !isWindows && sbtIncludeSbtn) { + IO.touch(macosUniversalTar) + val writer = new java.io.BufferedOutputStream(new java.io.FileOutputStream(macosUniversalTar)) + try Http(url(s"$baseUrl/v$v/$macosUniversalImageTar") >>> writer) finally writer.close() - val platformDir = t / x86MacPlatform + val platformDir = t / universalMacPlatform IO.createDirectory(platformDir) - s"tar zxvf $macosX86Tar --directory $platformDir".! - IO.move(platformDir / "sbtn", t / x86MacImageName) - } - if(!macosAarch64Tar.exists && !isWindows && sbtIncludeSbtn) { - IO.touch(macosAarch64Tar) - val writer = new java.io.BufferedOutputStream(new java.io.FileOutputStream(macosAarch64Tar)) - try Http(url(s"$baseUrl/v$v/$macosAarch64ImageTar") >>> writer) - finally writer.close() - val platformDir = t / aarch64MacPlatform - IO.createDirectory(platformDir) - s"tar zxvf $macosAarch64Tar --directory $platformDir".! - IO.move(platformDir / "sbtn", t / aarch64MacImageName) + s"tar zxvf $macosUniversalTar --directory $platformDir".! + IO.move(platformDir / "sbtn", t / universalMacImageName) } if(!linuxX86Tar.exists && !isWindows && sbtIncludeSbtn) { IO.touch(linuxX86Tar) @@ -192,8 +178,7 @@ val root = (project in file(".")). if (!sbtIncludeSbtn) Seq() else if (isWindows) Seq(t / x86WindowsImageName -> s"bin/$x86WindowsImageName") else - Seq(t / x86MacImageName -> s"bin/$x86MacImageName", - t / aarch64MacImageName -> s"bin/$aarch64MacImageName", + Seq(t / universalMacImageName -> s"bin/$universalMacImageName", t / x86LinuxImageName -> s"bin/$x86LinuxImageName", t / aarch64LinuxImageName -> s"bin/$aarch64LinuxImageName", t / x86WindowsImageName -> s"bin/$x86WindowsImageName") @@ -379,7 +364,13 @@ lazy val integrationTest = (project in file("integration-test")) "com.eed3si9n.expecty" %% "expecty" % "0.11.0" % Test, "org.scala-sbt" %% "io" % "1.3.1" % Test ), - testFrameworks += new TestFramework("minitest.runner.Framework") + testFrameworks += new TestFramework("minitest.runner.Framework"), + test in Test := { + (test in Test).dependsOn(((packageBin in Universal) in LocalRootProject).dependsOn(((stage in (Universal) in LocalRootProject)))).value + }, + testOnly in Test := { + (testOnly in Test).dependsOn(((packageBin in Universal) in LocalRootProject).dependsOn(((stage in (Universal) in LocalRootProject)))).evaluated + } ) def downloadUrlForVersion(v: String) = (v split "[^\\d]" flatMap (i => catching(classOf[Exception]) opt (i.toInt))) match { diff --git a/launcher-package/integration-test/src/test/scala/RunnerTest.scala b/launcher-package/integration-test/src/test/scala/RunnerTest.scala index 6d43be04a3..4ea58563b0 100755 --- a/launcher-package/integration-test/src/test/scala/RunnerTest.scala +++ b/launcher-package/integration-test/src/test/scala/RunnerTest.scala @@ -3,12 +3,15 @@ package example.test import minitest._ import scala.sys.process._ import java.io.File +import java.util.Locale +import sbt.io.IO object SbtRunnerTest extends SimpleTestSuite with PowerAssertions { // 1.3.0, 1.3.0-M4 private[test] val versionRegEx = "\\d(\\.\\d+){2}(-\\w+)?" - lazy val isWindows: Boolean = sys.props("os.name").toLowerCase(java.util.Locale.ENGLISH).contains("windows") + lazy val isWindows: Boolean = sys.props("os.name").toLowerCase(Locale.ENGLISH).contains("windows") + lazy val isMac: Boolean = sys.props("os.name").toLowerCase(Locale.ENGLISH).contains("mac") lazy val sbtScript = if (isWindows) new File("target/universal/stage/bin/sbt.bat") else new File("target/universal/stage/bin/sbt") @@ -18,6 +21,10 @@ object SbtRunnerTest extends SimpleTestSuite with PowerAssertions { sbt.internal.Process(Seq(sbtScript.getAbsolutePath) ++ args, new File("citest"), "JAVA_OPTS" -> javaOpts, "SBT_OPTS" -> sbtOpts) + def sbtProcessInDir(dir: File)(args: String*) = + sbt.internal.Process(Seq(sbtScript.getAbsolutePath) ++ args, dir, + "JAVA_OPTS" -> "", + "SBT_OPTS" -> "") test("sbt runs") { assert(sbtScript.exists) @@ -26,19 +33,32 @@ object SbtRunnerTest extends SimpleTestSuite with PowerAssertions { () } + def testVersion(lines: List[String]): Unit = { + assert(lines.size >= 2) + val expected0 = s"(?m)^sbt version in this project: $versionRegEx(\\r)?" + assert(lines(0).matches(expected0)) + val expected1 = s"sbt runner version: $versionRegEx$$" + assert(lines(1).matches(expected1)) + } + test("sbt -V|-version|--version should print sbtVersion") { val out = sbtProcess("-version").!!.trim - val expectedVersion = - s"""|(?m)^sbt version in this project: $versionRegEx(\\r)? - |sbt script version: $versionRegEx$$ - |""".stripMargin.trim.replace("\n", "\\n") - assert(out.matches(expectedVersion)) + testVersion(out.linesIterator.toList) val out2 = sbtProcess("--version").!!.trim - assert(out2.matches(expectedVersion)) + testVersion(out2.linesIterator.toList) val out3 = sbtProcess("-V").!!.trim - assert(out3.matches(expectedVersion)) + testVersion(out3.linesIterator.toList) + } + + test("sbt -V in empty directory") { + IO.withTemporaryDirectory { tmp => + val out = sbtProcessInDir(tmp)("-V").!!.trim + val expectedVersion = "^"+versionRegEx+"$" + val targetDir = new File(tmp, "target") + assert(!targetDir.exists, "expected target directory to not exist, but existed") + } () } @@ -58,8 +78,32 @@ object SbtRunnerTest extends SimpleTestSuite with PowerAssertions { } test("sbt \"testOnly *\"") { - val out = sbtProcess("testOnly *", "--no-colors", "-v").!!.linesIterator.toList - assert(out.contains[String]("[info] HelloTest")) + if (isMac) () + else { + val out = sbtProcess("testOnly *", "--no-colors", "-v").!!.linesIterator.toList + assert(out.contains[String]("[info] HelloTest")) + () + } + } + + test("sbt in empty directory") { + IO.withTemporaryDirectory { tmp => + val out = sbtProcessInDir(tmp)("about").! + assert(out == 1) + } + IO.withTemporaryDirectory { tmp => + val out = sbtProcessInDir(tmp)("about", "--allow-empty").! + assert(out == 0) + } + () + } + + test("sbt --script-version in empty directory") { + IO.withTemporaryDirectory { tmp => + val out = sbtProcessInDir(tmp)("--script-version").!!.trim + val expectedVersion = "^"+versionRegEx+"$" + assert(out.matches(expectedVersion)) + } () } diff --git a/launcher-package/integration-test/src/test/scala/ScriptTest.scala b/launcher-package/integration-test/src/test/scala/ScriptTest.scala index e1fe01b5f3..512cd43455 100644 --- a/launcher-package/integration-test/src/test/scala/ScriptTest.scala +++ b/launcher-package/integration-test/src/test/scala/ScriptTest.scala @@ -1,7 +1,11 @@ package example.test import minitest._ +import sbt.io.IO + import java.io.File +import java.io.PrintWriter +import java.nio.file.Files object SbtScriptTest extends SimpleTestSuite with PowerAssertions { lazy val isWindows: Boolean = @@ -16,30 +20,42 @@ object SbtScriptTest extends SimpleTestSuite with PowerAssertions { name: String, javaOpts: String = "", sbtOpts: String = "", + sbtOptsFileContents: String = "", + javaToolOptions: String = "" )(args: String*)(f: List[String] => Any) = { test(name) { - val out = - sbtProcessWithOpts(args: _*)(javaOpts = javaOpts, sbtOpts = sbtOpts).!!.linesIterator.toList - f(out) - () + val workingDirectory = Files.createTempDirectory("sbt-launcher-package-test").toFile + IO.copyDirectory(new File("citest"), workingDirectory) + + try { + val sbtOptsFile = new File(workingDirectory, ".sbtopts") + sbtOptsFile.createNewFile() + val writer = new PrintWriter(sbtOptsFile) + try { + writer.write(sbtOptsFileContents) + } finally { + writer.close() + } + val path = sys.env.getOrElse("PATH", sys.env("Path")) + val out = sbt.internal.Process( + Seq(sbtScript.getAbsolutePath) ++ args, + workingDirectory, + "JAVA_OPTS" -> javaOpts, + "SBT_OPTS" -> sbtOpts, + "JAVA_TOOL_OPTIONS" -> javaToolOptions, + if (isWindows) + "JAVACMD" -> new File(javaBinDir, "java").getAbsolutePath() + else + "PATH" -> (javaBinDir + File.pathSeparator + path) + ).!!.linesIterator.toList + f(out) + () + } finally { + IO.delete(workingDirectory) + } } } - 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"), - "JAVA_OPTS" -> javaOpts, - "SBT_OPTS" -> sbtOpts, - if (isWindows) - "JAVACMD" -> new File(javaBinDir, "java.cmd").getAbsolutePath() - else - "PATH" -> (javaBinDir + File.pathSeparator + path) - ) - } - makeTest("sbt -no-colors")("compile", "-no-colors", "-v") { out: List[String] => assert(out.contains[String]("-Dsbt.log.noformat=true")) } @@ -161,11 +177,9 @@ object SbtScriptTest extends SimpleTestSuite with PowerAssertions { assert(!out.contains[String]("-XX:+UseG1GC=-XX:+PrintGC")) } - test("sbt with -debug in SBT_OPTS appears in sbt commands") { + makeTest("sbt with -debug in SBT_OPTS appears in sbt commands", javaOpts = "", sbtOpts = "-debug")("compile", "-v") {out: List[String] => if (isWindows) cancel("Test not supported on windows") - 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 { @@ -196,14 +210,45 @@ object SbtScriptTest extends SimpleTestSuite with PowerAssertions { assert(out.contains[String]("-Dsbt.ivy.home=/ivy/dir")) } - test("sbt --script-version should print sbtVersion") { - val out = sbtProcess("--script-version").!!.trim + makeTest("sbt --script-version should print sbtVersion")("--script-version") { out: List[String] => val expectedVersion = "^" + SbtRunnerTest.versionRegEx + "$" - assert(out.matches(expectedVersion)) + assert(out.mkString(System.lineSeparator()).trim.matches(expectedVersion)) () } makeTest("--sbt-cache")("--sbt-cache", "./cachePath") { out: List[String] => assert(out.contains[String](s"-Dsbt.global.localcache=./cachePath")) } + + makeTest( + "sbt use .sbtopts file for memory options", sbtOptsFileContents = + """-J-XX:MaxInlineLevel=20 + |-J-Xmx222m + |-J-Xms111m + |-J-Xss12m""".stripMargin + + )("compile", "-v") { out: List[String] => + assert(out.contains[String]("-XX:MaxInlineLevel=20")) + assert(out.contains[String]("-Xmx222m")) + assert(out.contains[String]("-Xms111m")) + assert(out.contains[String]("-Xss12m")) + } + + makeTest( + "sbt use JAVA_OPTS for memory options", javaOpts = "-XX:MaxInlineLevel=20 -Xmx222m -Xms111m -Xss12m" + )("compile", "-v") { out: List[String] => + assert(out.contains[String]("-XX:MaxInlineLevel=20")) + assert(out.contains[String]("-Xmx222m")) + assert(out.contains[String]("-Xms111m")) + assert(out.contains[String]("-Xss12m")) + } + + makeTest( + "sbt use JAVA_TOOL_OPTIONS for memory options", javaToolOptions = "-XX:MaxInlineLevel=20 -Xmx222m -Xms111m -Xss12m" + )("compile", "-v") { out: List[String] => + assert(out.contains[String]("-XX:MaxInlineLevel=20")) + assert(out.contains[String]("-Xmx222m")) + assert(out.contains[String]("-Xms111m")) + assert(out.contains[String]("-Xss12m")) + } } diff --git a/launcher-package/src/universal/bin/sbt.bat b/launcher-package/src/universal/bin/sbt.bat index dc1f3c8a18..fc4986e0b2 100755 --- a/launcher-package/src/universal/bin/sbt.bat +++ b/launcher-package/src/universal/bin/sbt.bat @@ -45,12 +45,13 @@ set sbt_args_timings= set sbt_args_traces= set sbt_args_sbt_boot= set sbt_args_sbt_cache= -set sbt_args_sbt_create= +set sbt_args_allow_empty= set sbt_args_sbt_dir= set sbt_args_sbt_version= set sbt_args_mem= set sbt_args_client= set sbt_args_no_server= +set is_this_dir_sbt=0 rem users can set SBT_OPTS via .sbtopts if exist .sbtopts for /F %%A in (.sbtopts) do ( @@ -72,11 +73,13 @@ rem TODO: remove/deprecate sbtconfig.txt and parse the sbtopts files rem FIRST we load the config file of extra options. set SBT_CONFIG=!SBT_HOME!\conf\sbtconfig.txt set SBT_CFG_OPTS= -for /F "tokens=* eol=# usebackq delims=" %%i in ("!SBT_CONFIG!") do ( - set DO_NOT_REUSE_ME=%%i - rem ZOMG (Part #2) WE use !! here to delay the expansion of - rem SBT_CFG_OPTS, otherwise it remains "" for this loop. - set SBT_CFG_OPTS=!SBT_CFG_OPTS! !DO_NOT_REUSE_ME! +if exist "!SBT_CONFIG!" ( + for /F "tokens=* eol=# usebackq delims=" %%i in ("!SBT_CONFIG!") do ( + set DO_NOT_REUSE_ME=%%i + rem ZOMG (Part #2) WE use !! here to delay the expansion of + rem SBT_CFG_OPTS, otherwise it remains "" for this loop. + set SBT_CFG_OPTS=!SBT_CFG_OPTS! !DO_NOT_REUSE_ME! + ) ) rem poor man's jenv (which is not available on Windows) @@ -235,12 +238,14 @@ if defined _traces_arg ( goto args_loop ) -if "%~0" == "-sbt-create" set _sbt_create_arg=true -if "%~0" == "--sbt-create" set _sbt_create_arg=true +if "%~0" == "-sbt-create" set _allow_empty_arg=true +if "%~0" == "--sbt-create" set _allow_empty_arg=true +if "%~0" == "-allow-empty" set _allow_empty_arg=true +if "%~0" == "--allow-empty" set _allow_empty_arg=true -if defined _sbt_create_arg ( - set _sbt_create_arg= - set sbt_args_sbt_create=1 +if defined _allow_empty_arg ( + set _allow_empty_arg= + set sbt_args_allow_empty=1 goto args_loop ) @@ -472,6 +477,11 @@ if "%~0" == "new" ( set sbt_new=true ) ) +if "%~0" == "init" ( + if not defined SBT_ARGS ( + set sbt_new=true + ) +) if "%g:~0,2%" == "-D" ( rem special handling for -D since '=' gets parsed away @@ -523,28 +533,22 @@ set SBT_ARGS=!SBT_ARGS! %0 goto args_loop :args_end +if exist build.sbt ( + set is_this_dir_sbt=1 +) +if exist project\build.properties ( + set is_this_dir_sbt=1 +) + 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 defined shutdownall if not exist build.sbt ( - if not exist project\ ( +if not defined sbt_args_allow_empty 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 !is_this_dir_sbt! equ 1 ( if not defined sbt_new ( - echo [warn] Neither build.sbt nor a 'project' directory in the current directory: "%CD%" - setlocal -:confirm - echo c^) continue - echo q^) quit - - set /P reply=^? - if /I "!reply!" == "c" ( - goto confirm_end - ) else if /I "!reply!" == "q" ( - exit /B 1 - ) - - goto confirm -:confirm_end - endlocal + >&2 echo [error] Neither build.sbt nor a 'project' directory in the current directory: "%CD%" + >&2 echo [error] run 'sbt new', touch build.sbt, or run 'sbt --allow-empty'. + goto error ) ) ) @@ -559,7 +563,7 @@ if !shutdownall! equ 1 ( taskkill /F /PID %%i set /a count=!count!+1 ) - echo shutdown !count! sbt processes + >&2 echo shutdown !count! sbt processes goto :eof ) @@ -671,9 +675,14 @@ if !sbt_args_print_sbt_version! equ 1 ( ) if !sbt_args_print_version! equ 1 ( - call :set_sbt_version - echo sbt version in this project: !sbt_version! - echo sbt script version: !init_sbt_version! + if !is_this_dir_sbt! equ 1 ( + call :set_sbt_version + echo sbt version in this project: !sbt_version! + ) + echo sbt runner version: !init_sbt_version! + >&2 echo. + >&2 echo [info] sbt runner ^(sbt-the-batch-script^) is a runner to run any declared version of sbt. + >&2 echo [info] Actual version of the sbt is declared using project\build.properties for each build. goto :eof ) @@ -682,6 +691,7 @@ if defined sbt_args_verbose ( echo "!_JAVACMD!" if defined _JAVA_OPTS ( call :echolist !_JAVA_OPTS! ) if defined _SBT_OPTS ( call :echolist !_SBT_OPTS! ) + if defined JAVA_TOOL_OPTIONS ( call :echolist %JAVA_TOOL_OPTIONS% ) echo -cp echo "!sbt_jar!" echo xsbt.boot.Boot @@ -689,7 +699,7 @@ if defined sbt_args_verbose ( echo. ) -"!_JAVACMD!" !_JAVA_OPTS! !_SBT_OPTS! -cp "!sbt_jar!" xsbt.boot.Boot %* +"!_JAVACMD!" !_JAVA_OPTS! !_SBT_OPTS! %JAVA_TOOL_OPTIONS% -cp "!sbt_jar!" xsbt.boot.Boot %* goto :eof @@ -828,21 +838,21 @@ exit /B 0 set _has_memory_args= - if defined _JAVA_OPTS for /F %%g in ("!_JAVA_OPTS!") do ( + if defined _JAVA_OPTS for %%g in (%_JAVA_OPTS%) do ( set "p=%%g" if "!p:~0,4!" == "-Xmx" set _has_memory_args=1 if "!p:~0,4!" == "-Xms" set _has_memory_args=1 if "!p:~0,4!" == "-Xss" set _has_memory_args=1 ) - if defined JAVA_TOOL_OPTIONS for /F %%g in ("%JAVA_TOOL_OPTIONS%") do ( + if defined JAVA_TOOL_OPTIONS for %%g in (%JAVA_TOOL_OPTIONS%) do ( set "p=%%g" if "!p:~0,4!" == "-Xmx" set _has_memory_args=1 if "!p:~0,4!" == "-Xms" set _has_memory_args=1 if "!p:~0,4!" == "-Xss" set _has_memory_args=1 ) - if defined _SBT_OPTS for /F %%g in ("!_SBT_OPTS!") do ( + if defined _SBT_OPTS for %%g in (%_SBT_OPTS%) do ( set "p=%%g" if "!p:~0,4!" == "-Xmx" set _has_memory_args=1 if "!p:~0,4!" == "-Xms" set _has_memory_args=1 @@ -889,38 +899,38 @@ for /F "delims=.-_ tokens=1-2" %%v in ("!sbtV!") do ( set sbtBinaryV_1=%%v set sbtBinaryV_2=%%w ) -set native_client_ready= +rem default to run_native_client=1 for sbt 2.x if !sbtBinaryV_1! geq 2 ( - set native_client_ready=1 + if !sbt_args_client! equ 0 ( + set run_native_client= + ) else ( + set run_native_client=1 + ) ) else ( if !sbtBinaryV_1! geq 1 ( if !sbtBinaryV_2! geq 4 ( - set native_client_ready=1 + if !sbt_args_client! equ 1 ( + set run_native_client=1 + ) ) ) ) -if !native_client_ready! equ 1 ( - if !sbt_args_client! equ 1 ( - set run_native_client=1 - ) -) -set native_client_ready= exit /B 0 :checkjava -set /a required_version=6 +set /a required_version=8 if /I !JAVA_VERSION! GEQ !required_version! ( exit /B 0 ) -echo. -echo The Java Development Kit ^(JDK^) installation you have is not up to date. -echo sbt requires at least version !required_version!+, you have -echo version "!JAVA_VERSION!" -echo. -echo Please go to http://www.oracle.com/technetwork/java/javase/downloads/ and download -echo a valid JDK and install before running sbt. -echo. +>&2 echo. +>&2 echo The Java Development Kit ^(JDK^) installation you have is not up to date. +>&2 echo sbt requires at least version !required_version!+, you have +>&2 echo version "!JAVA_VERSION!" +>&2 echo. +>&2 echo Go to https://adoptium.net/ etc and download +>&2 echo a valid JDK and install before running sbt. +>&2 echo. exit /B 1 :copyrt diff --git a/launcher-package/src/universal/conf/sbtopts b/launcher-package/src/universal/conf/sbtopts index c6f4e7bec2..5990223b7c 100644 --- a/launcher-package/src/universal/conf/sbtopts +++ b/launcher-package/src/universal/conf/sbtopts @@ -9,7 +9,7 @@ # Starts sbt even if the current directory contains no sbt project. # --sbt-create +#--allow-empty # Path to global settings/plugins directory (default: ~/.sbt) # @@ -31,11 +31,11 @@ # #-no-share -# Put SBT in offline mode. +# Put sbt in offline mode. # #-offline -# Sets the SBT version to use. +# Sets the sbt version to use. #-sbt-version 0.11.3 # Scala version (default: latest release) diff --git a/main-actions/src/main/contraband-scala/sbt/internal/sona/DeploymentState.scala b/main-actions/src/main/contraband-scala/sbt/internal/sona/DeploymentState.scala new file mode 100644 index 0000000000..977a683840 --- /dev/null +++ b/main-actions/src/main/contraband-scala/sbt/internal/sona/DeploymentState.scala @@ -0,0 +1,17 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.sona +sealed abstract class DeploymentState extends Serializable +object DeploymentState { + + + case object PENDING extends DeploymentState + case object VALIDATING extends DeploymentState + case object VALIDATED extends DeploymentState + case object PUBLISHING extends DeploymentState + case object PUBLISHED extends DeploymentState + case object FAILED extends DeploymentState +} diff --git a/main-actions/src/main/contraband-scala/sbt/internal/sona/PublisherStatus.scala b/main-actions/src/main/contraband-scala/sbt/internal/sona/PublisherStatus.scala new file mode 100644 index 0000000000..6e1a764b19 --- /dev/null +++ b/main-actions/src/main/contraband-scala/sbt/internal/sona/PublisherStatus.scala @@ -0,0 +1,45 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.sona +/** https://central.sonatype.org/publish/publish-portal-api/#uploading-a-deployment-bundle */ +final class PublisherStatus private ( + val deploymentId: String, + val deploymentName: String, + val deploymentState: sbt.internal.sona.DeploymentState, + val purls: Vector[String]) extends Serializable { + + + + override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { + case x: PublisherStatus => (this.deploymentId == x.deploymentId) && (this.deploymentName == x.deploymentName) && (this.deploymentState == x.deploymentState) && (this.purls == x.purls) + case _ => false + }) + override def hashCode: Int = { + 37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.sona.PublisherStatus".##) + deploymentId.##) + deploymentName.##) + deploymentState.##) + purls.##) + } + override def toString: String = { + "PublisherStatus(" + deploymentId + ", " + deploymentName + ", " + deploymentState + ", " + purls + ")" + } + private[this] def copy(deploymentId: String = deploymentId, deploymentName: String = deploymentName, deploymentState: sbt.internal.sona.DeploymentState = deploymentState, purls: Vector[String] = purls): PublisherStatus = { + new PublisherStatus(deploymentId, deploymentName, deploymentState, purls) + } + def withDeploymentId(deploymentId: String): PublisherStatus = { + copy(deploymentId = deploymentId) + } + def withDeploymentName(deploymentName: String): PublisherStatus = { + copy(deploymentName = deploymentName) + } + def withDeploymentState(deploymentState: sbt.internal.sona.DeploymentState): PublisherStatus = { + copy(deploymentState = deploymentState) + } + def withPurls(purls: Vector[String]): PublisherStatus = { + copy(purls = purls) + } +} +object PublisherStatus { + + def apply(deploymentId: String, deploymentName: String, deploymentState: sbt.internal.sona.DeploymentState, purls: Vector[String]): PublisherStatus = new PublisherStatus(deploymentId, deploymentName, deploymentState, purls) +} diff --git a/main-actions/src/main/contraband-scala/sbt/internal/sona/codec/DeploymentStateFormats.scala b/main-actions/src/main/contraband-scala/sbt/internal/sona/codec/DeploymentStateFormats.scala new file mode 100644 index 0000000000..9da34bf742 --- /dev/null +++ b/main-actions/src/main/contraband-scala/sbt/internal/sona/codec/DeploymentStateFormats.scala @@ -0,0 +1,37 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.sona.codec +import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } +trait DeploymentStateFormats { self: sjsonnew.BasicJsonProtocol => +implicit lazy val DeploymentStateFormat: JsonFormat[sbt.internal.sona.DeploymentState] = new JsonFormat[sbt.internal.sona.DeploymentState] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.sona.DeploymentState = { + __jsOpt match { + case Some(__js) => + unbuilder.readString(__js) match { + case "PENDING" => sbt.internal.sona.DeploymentState.PENDING + case "VALIDATING" => sbt.internal.sona.DeploymentState.VALIDATING + case "VALIDATED" => sbt.internal.sona.DeploymentState.VALIDATED + case "PUBLISHING" => sbt.internal.sona.DeploymentState.PUBLISHING + case "PUBLISHED" => sbt.internal.sona.DeploymentState.PUBLISHED + case "FAILED" => sbt.internal.sona.DeploymentState.FAILED + } + case None => + deserializationError("Expected JsString but found None") + } + } + override def write[J](obj: sbt.internal.sona.DeploymentState, builder: Builder[J]): Unit = { + val str = obj match { + case sbt.internal.sona.DeploymentState.PENDING => "PENDING" + case sbt.internal.sona.DeploymentState.VALIDATING => "VALIDATING" + case sbt.internal.sona.DeploymentState.VALIDATED => "VALIDATED" + case sbt.internal.sona.DeploymentState.PUBLISHING => "PUBLISHING" + case sbt.internal.sona.DeploymentState.PUBLISHED => "PUBLISHED" + case sbt.internal.sona.DeploymentState.FAILED => "FAILED" + } + builder.writeString(str) + } +} +} diff --git a/main-actions/src/main/contraband-scala/sbt/internal/sona/codec/JsonProtocol.scala b/main-actions/src/main/contraband-scala/sbt/internal/sona/codec/JsonProtocol.scala new file mode 100644 index 0000000000..01f3409a12 --- /dev/null +++ b/main-actions/src/main/contraband-scala/sbt/internal/sona/codec/JsonProtocol.scala @@ -0,0 +1,10 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.sona.codec +trait JsonProtocol extends sjsonnew.BasicJsonProtocol + with sbt.internal.sona.codec.DeploymentStateFormats + with sbt.internal.sona.codec.PublisherStatusFormats +object JsonProtocol extends JsonProtocol \ No newline at end of file diff --git a/main-actions/src/main/contraband-scala/sbt/internal/sona/codec/PublisherStatusFormats.scala b/main-actions/src/main/contraband-scala/sbt/internal/sona/codec/PublisherStatusFormats.scala new file mode 100644 index 0000000000..5235269902 --- /dev/null +++ b/main-actions/src/main/contraband-scala/sbt/internal/sona/codec/PublisherStatusFormats.scala @@ -0,0 +1,33 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.sona.codec +import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } +trait PublisherStatusFormats { self: sbt.internal.sona.codec.DeploymentStateFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val PublisherStatusFormat: JsonFormat[sbt.internal.sona.PublisherStatus] = new JsonFormat[sbt.internal.sona.PublisherStatus] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.sona.PublisherStatus = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val deploymentId = unbuilder.readField[String]("deploymentId") + val deploymentName = unbuilder.readField[String]("deploymentName") + val deploymentState = unbuilder.readField[sbt.internal.sona.DeploymentState]("deploymentState") + val purls = unbuilder.readField[Vector[String]]("purls") + unbuilder.endObject() + sbt.internal.sona.PublisherStatus(deploymentId, deploymentName, deploymentState, purls) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.sona.PublisherStatus, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("deploymentId", obj.deploymentId) + builder.addField("deploymentName", obj.deploymentName) + builder.addField("deploymentState", obj.deploymentState) + builder.addField("purls", obj.purls) + builder.endObject() + } +} +} diff --git a/main-actions/src/main/contraband/sona.contra b/main-actions/src/main/contraband/sona.contra new file mode 100644 index 0000000000..1e4bdd90cd --- /dev/null +++ b/main-actions/src/main/contraband/sona.contra @@ -0,0 +1,21 @@ +package sbt.internal.sona +@target(Scala) +@codecPackage("sbt.internal.sona.codec") +@fullCodec("JsonProtocol") + +enum DeploymentState { + PENDING + VALIDATING + VALIDATED + PUBLISHING + PUBLISHED + FAILED +} + +## https://central.sonatype.org/publish/publish-portal-api/#uploading-a-deployment-bundle +type PublisherStatus { + deploymentId: String! + deploymentName: String! + deploymentState: sbt.internal.sona.DeploymentState! + purls: [String] +} diff --git a/main-actions/src/main/scala/sbt/internal/sona/Sona.scala b/main-actions/src/main/scala/sbt/internal/sona/Sona.scala new file mode 100644 index 0000000000..2b3e1d88a6 --- /dev/null +++ b/main-actions/src/main/scala/sbt/internal/sona/Sona.scala @@ -0,0 +1,195 @@ +/* + * sbt + * Copyright 2023, Scala center + * Copyright 2011 - 2022, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt +package internal +package sona + +import gigahorse.*, support.apachehttp.Gigahorse +import java.net.URLEncoder +import java.util.Base64 +import java.nio.charset.StandardCharsets +import java.nio.file.Path +import sbt.util.Logger +import sjsonnew.JsonFormat +import sjsonnew.support.scalajson.unsafe.{ Converter, Parser } +import sjsonnew.shaded.scalajson.ast.unsafe.JValue + +import scala.annotation.nowarn +import scala.concurrent.*, duration.* + +class Sona(client: SonaClient) extends AutoCloseable { + def uploadBundle( + bundleZipPath: Path, + deploymentName: String, + pt: PublishingType, + log: Logger, + ): Unit = { + val deploymentId = client.uploadBundle(bundleZipPath, deploymentName, pt, log) + client.waitForDeploy(deploymentId, deploymentName, pt, 1, log) + } + def close(): Unit = client.close() +} + +class SonaClient(reqTransform: Request => Request) extends AutoCloseable { + import SonaClient.baseUrl + + val gigahorseConfig = Gigahorse.config + .withRequestTimeout(2.minute) + .withReadTimeout(2.minute) + val http = Gigahorse.http(gigahorseConfig) + def uploadBundle( + bundleZipPath: Path, + deploymentName: String, + publishingType: PublishingType, + log: Logger, + ): String = { + val res = retryF(maxAttempt = 2) { (attempt: Int) => + log.info(s"uploading bundle to the Central Portal (attempt: $attempt)") + // addQuery string doesn't work for post + val q = queryString( + "name" -> deploymentName, + "publishingType" -> (publishingType match { + case PublishingType.Automatic => "AUTOMATIC" + case PublishingType.UserManaged => "USER_MANAGED" + }) + ) + val req = Gigahorse + .url(s"${baseUrl}/publisher/upload?$q") + .post( + MultipartFormBody( + FormPart("bundle", bundleZipPath.toFile()) + ) + ) + .withRequestTimeout(600.second) + http.run(reqTransform(req), Gigahorse.asString) + } + awaitWithMessage(res, "uploading...", log) + } + + def queryString(kv: (String, String)*): String = + kv.map { + case (k, v) => + val encodedV = URLEncoder.encode(v, "UTF-8") + s"$k=$encodedV" + } + .mkString("&") + + def waitForDeploy( + deploymentId: String, + deploymentName: String, + publishingType: PublishingType, + attempt: Int, + log: Logger, + ): Unit = { + val status = deploymentStatus(deploymentId) + log.info(s"deployment $deploymentName ${status.deploymentState} ${attempt}/n") + val sleepSec = + if (attempt <= 3) List(5, 5, 10, 15)(attempt) + else 30 + status.deploymentState match { + case DeploymentState.FAILED => sys.error(s"deployment $deploymentId failed") + case DeploymentState.PENDING | DeploymentState.PUBLISHING | DeploymentState.VALIDATING => + Thread.sleep(sleepSec * 1000L) + waitForDeploy(deploymentId, deploymentName, publishingType, attempt + 1, log) + case DeploymentState.PUBLISHED if publishingType == PublishingType.Automatic => () + case DeploymentState.VALIDATED if publishingType == PublishingType.UserManaged => () + case DeploymentState.VALIDATED => + Thread.sleep(sleepSec * 1000L) + waitForDeploy(deploymentId, deploymentName, publishingType, attempt + 1, log) + case _ => + Thread.sleep(sleepSec * 1000L) + waitForDeploy(deploymentId, deploymentName, publishingType, attempt + 1, log) + } + } + + def deploymentStatus(deploymentId: String): PublisherStatus = { + val res = retryF(maxAttempt = 5) { (attempt: Int) => + deploymentStatusF(deploymentId) + } + Await.result(res, 600.seconds) + } + + /** https://central.sonatype.org/publish/publish-portal-api/#verify-status-of-the-deployment + */ + def deploymentStatusF(deploymentId: String): Future[PublisherStatus] = { + val req = Gigahorse + .url(s"${baseUrl}/publisher/status") + .addQueryString("id" -> deploymentId) + .post("", StandardCharsets.UTF_8) + http.run(reqTransform(req), SonaClient.asPublisherStatus) + } + + /** Retry future function on any error. + */ + @nowarn + def retryF[A1](maxAttempt: Int)(f: Int => Future[A1]): Future[A1] = { + import scala.concurrent.ExecutionContext.Implicits.* + def impl(retry: Int): Future[A1] = { + val res = f(retry + 1) + res.recoverWith { + case _ if retry < maxAttempt => + Thread.sleep(5000) + impl(retry + 1) + } + } + impl(0) + } + + def awaitWithMessage[A1](f: Future[A1], msg: String, log: Logger): A1 = { + import scala.concurrent.ExecutionContext.Implicits.* + def loop(attempt: Int): Unit = + if (!f.isCompleted) { + if (attempt > 0) { + log.info(msg) + } + Future { + blocking { + Thread.sleep(30.second.toMillis) + } + }.foreach(_ => loop(attempt + 1)) + } else () + loop(0) + Await.result(f, 600.seconds) + } + + def close(): Unit = http.close() +} + +object Sona { + def host: String = SonaClient.host + def oauthClient(userName: String, userToken: String): Sona = + new Sona(SonaClient.oauthClient(userName, userToken)) +} + +object SonaClient { + import sbt.internal.sona.codec.JsonProtocol.{ *, given } + val host: String = "central.sonatype.com" + val baseUrl: String = s"https://$host/api/v1" + val asJson: FullResponse => JValue = (r: FullResponse) => + Parser.parseFromByteBuffer(r.bodyAsByteBuffer).get + def as[A1: JsonFormat]: FullResponse => A1 = asJson.andThen(Converter.fromJsonUnsafe[A1]) + val asPublisherStatus: FullResponse => PublisherStatus = as[PublisherStatus] + def oauthClient(userName: String, userToken: String): SonaClient = + new SonaClient(OAuthClient(userName, userToken)) +} + +private case class OAuthClient(userName: String, userToken: String) + extends Function1[Request, Request] { + val base64Credentials = + Base64.getEncoder.encodeToString(s"${userName}:${userToken}".getBytes(StandardCharsets.UTF_8)) + def apply(request: Request): Request = + request.addHeaders("Authorization" -> s"Bearer $base64Credentials") + override def toString: String = "OAuthClient(****)" +} + +sealed trait PublishingType +object PublishingType { + case object Automatic extends PublishingType + case object UserManaged extends PublishingType +} diff --git a/main-command/src/main/scala/sbt/BasicKeys.scala b/main-command/src/main/scala/sbt/BasicKeys.scala index f01b1f6a40..7d6693e199 100644 --- a/main-command/src/main/scala/sbt/BasicKeys.scala +++ b/main-command/src/main/scala/sbt/BasicKeys.scala @@ -93,7 +93,7 @@ object BasicKeys { val serverIdleTimeout = AttributeKey[Option[FiniteDuration]]( - "serverIdleTimeOut", + "serverIdleTimeout", "If set to a defined value, sbt server will exit if it goes at least the specified duration without receiving any commands.", 10000 ) diff --git a/main-command/src/main/scala/sbt/Command.scala b/main-command/src/main/scala/sbt/Command.scala index f4a1ebc6f1..15e910bb16 100644 --- a/main-command/src/main/scala/sbt/Command.scala +++ b/main-command/src/main/scala/sbt/Command.scala @@ -184,12 +184,17 @@ object Command { } ) - def process(command: String, state: State): State = { + // overload instead of default parameter to keep binary compatibility + @deprecated("Use overload that takes the onParseError callback", since = "1.9.4") + def process(command: String, state: State): State = process(command, state, _ => ()) + + def process(command: String, state: State, onParseError: String => Unit): State = { (if (command.contains(";")) parse(command, state.combinedParser) else parse(command, state.nonMultiParser)) match { case Right(s) => s() // apply command. command side effects happen here case Left(errMsg) => state.log error errMsg + onParseError(errMsg) state.fail } } diff --git a/main-command/src/main/scala/sbt/internal/CommandChannel.scala b/main-command/src/main/scala/sbt/internal/CommandChannel.scala index db53b95580..3e0d7a518d 100644 --- a/main-command/src/main/scala/sbt/internal/CommandChannel.scala +++ b/main-command/src/main/scala/sbt/internal/CommandChannel.scala @@ -63,6 +63,8 @@ abstract class CommandChannel { } } } + protected def appendExec(commandLine: String, execId: Option[String]): Boolean = + append(Exec(commandLine, execId.orElse(Some(Exec.newExecId)), Some(CommandSource(name)))) def poll: Option[Exec] = Option(commandQueue.poll) def prompt(e: ConsolePromptEvent): Unit = userThread.onConsolePromptEvent(e) @@ -81,20 +83,21 @@ abstract class CommandChannel { private[sbt] final def logLevel: Level.Value = level.get private[this] def setLevel(value: Level.Value, cmd: String): Boolean = { level.set(value) - append(Exec(cmd, Some(Exec.newExecId), Some(CommandSource(name)))) + appendExec(cmd, None) } - private[sbt] def onCommand: String => Boolean = { - case "error" => setLevel(Level.Error, "error") - case "debug" => setLevel(Level.Debug, "debug") - case "info" => setLevel(Level.Info, "info") - case "warn" => setLevel(Level.Warn, "warn") - case cmd => - if (cmd.nonEmpty) append(Exec(cmd, Some(Exec.newExecId), Some(CommandSource(name)))) - else false - } - private[sbt] def onFastTrackTask: String => Boolean = { s: String => + private[sbt] def onCommandLine(cmd: String): Boolean = + cmd match { + case "error" => setLevel(Level.Error, "error") + case "debug" => setLevel(Level.Debug, "debug") + case "info" => setLevel(Level.Info, "info") + case "warn" => setLevel(Level.Warn, "warn") + case cmd => + if (cmd.nonEmpty) appendExec(cmd, None) + else false + } + private[sbt] def onFastTrackTask(cmd: String): Boolean = { fastTrack.synchronized(fastTrack.forEach { q => - q.add(new FastTrackTask(this, s)) + q.add(new FastTrackTask(this, cmd)) () }) true 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 34c544011c..6bc13337fb 100644 --- a/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala +++ b/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala @@ -15,14 +15,24 @@ import java.lang.ProcessBuilder.Redirect import java.net.{ Socket, SocketException } import java.nio.file.Files import java.util.UUID +import java.util.Date import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference } import java.util.concurrent.{ ConcurrentHashMap, LinkedBlockingQueue, TimeUnit } +import java.text.DateFormat import sbt.BasicCommandStrings.{ DashDashDetachStdio, DashDashServer, Shutdown, TerminateAction } import sbt.internal.client.NetworkClient.Arguments import sbt.internal.langserver.{ LogMessageParams, MessageType, PublishDiagnosticsParams } +import sbt.internal.worker.{ ClientJobParams, JvmRunInfo, NativeRunInfo, RunInfo } import sbt.internal.protocol._ -import sbt.internal.util.{ ConsoleAppender, ConsoleOut, Signals, Terminal, Util } +import sbt.internal.util.{ + ConsoleAppender, + ConsoleOut, + MessageOnlyException, + Signals, + Terminal, + Util +} import sbt.io.IO import sbt.io.syntax._ import sbt.protocol._ @@ -41,6 +51,7 @@ import Serialization.{ attach, cancelReadSystemIn, cancelRequest, + clientJob, promptChannel, readSystemIn, systemIn, @@ -61,6 +72,7 @@ import Serialization.{ } import NetworkClient.Arguments import java.util.concurrent.TimeoutException +import sbt.util.Logger trait ConsoleInterface { def appendLog(level: Level.Value, message: => String): Unit @@ -141,6 +153,7 @@ class NetworkClient( private lazy val noTab = arguments.completionArguments.contains("--no-tab") private lazy val noStdErr = arguments.completionArguments.contains("--no-stderr") && !sys.env.contains("SBTN_AUTO_COMPLETE") && !sys.env.contains("SBTC_AUTO_COMPLETE") + private def shutdownOnly = arguments.commandArguments == Seq(Shutdown) private def mkSocket(file: File): (Socket, Option[String]) = ClientSocket.socket(file, useJNI) @@ -164,6 +177,11 @@ class NetworkClient( case null => inputThread.set(new RawInputThread) case _ => } + private lazy val log: Logger = new Logger { + def trace(t: => Throwable): Unit = () + def success(message: => String): Unit = () + def log(level: Level.Value, message: => String): Unit = console.appendLog(level, message) + } private[sbt] def connectOrStartServerAndConnect( promptCompleteUsers: Boolean, @@ -171,7 +189,10 @@ class NetworkClient( ): (Socket, Option[String]) = try { if (!portfile.exists) { - if (promptCompleteUsers) { + if (shutdownOnly) { + console.appendLog(Level.Info, "no sbt server is running. ciao") + System.exit(0) + } else if (promptCompleteUsers) { val msg = if (noTab) "" else "No sbt server is running. Press to start one..." errorStream.print(s"\n$msg") if (noStdErr) System.exit(0) @@ -293,7 +314,18 @@ class NetworkClient( } // initiate handshake val execId = UUID.randomUUID.toString - val initCommand = InitCommand(tkn, Option(execId), Some(true)) + val skipAnalysis = true + val opts = InitializeOption( + token = tkn, + skipAnalysis = Some(skipAnalysis), + canWork = Some(true), + ) + val initCommand = InitCommand( + token = tkn, // duplicated with opts for compatibility + execId = Option(execId), + skipAnalysis = Some(skipAnalysis), // duplicated with opts for compatibility + initializationOptions = Some(opts), + ) conn.sendString(Serialization.serializeCommandAsJsonMessage(initCommand)) connectionHolder.set(conn) conn @@ -527,61 +559,77 @@ class NetworkClient( .getOrElse(1) case _ => 1 } - private def completeExec(execId: String, exitCode: => Int): Unit = + + private val onAttachResponse: PartialFunction[JsonRpcResponseMessage, Unit] = { + case msg if attachUUID.get == msg.id => + attachUUID.set(null) + attached.set(true) + Option(inputThread.get).foreach(_.drain()) + () + } + def completeExec(execId: String, exitCode: Int) = { pendingResults.remove(execId) match { - case null => + case null => () case (q, startTime, name) => val now = System.currentTimeMillis - val message = timing(startTime, now) - val ec = exitCode + val message = NetworkClient.timing(startTime, now) if (batchMode.get || !attached.get) { - if (ec == 0) console.success(message) + if (exitCode == 0) console.success(message) else console.appendLog(Level.Error, message) } - Util.ignoreResult(q.offer(ec)) - } - def onResponse(msg: JsonRpcResponseMessage): Unit = { - completeExec(msg.id, getExitCode(msg.result)) - pendingCancellations.remove(msg.id) match { - case null => - case q => q.offer(msg.toString.contains("Task cancelled")) - } - msg.id match { - case execId => - if (attachUUID.get == msg.id) { - attachUUID.set(null) - attached.set(true) - Option(inputThread.get).foreach(_.drain()) - } - pendingCompletions.remove(execId) match { - case null => - case completions => - completions(msg.result match { - case Some(o: JObject) => - o.value - .foldLeft(CompletionResponse(Vector.empty[String])) { - case (resp, i) => - if (i.field == "items") - resp.withItems( - Converter - .fromJson[Vector[String]](i.value) - .getOrElse(Vector.empty[String]) - ) - else if (i.field == "cachedTestNames") - resp.withCachedTestNames( - Converter.fromJson[Boolean](i.value).getOrElse(true) - ) - else if (i.field == "cachedMainClassNames") - resp.withCachedMainClassNames( - Converter.fromJson[Boolean](i.value).getOrElse(true) - ) - else resp - } - case _ => CompletionResponse(Vector.empty[String]) - }) - } + Util.ignoreResult(q.offer(exitCode)) } } + private val onExecResponse: PartialFunction[JsonRpcResponseMessage, Unit] = { + case msg if pendingResults.containsKey(msg.id) => + completeExec(msg.id, getExitCode(msg.result)) + } + private val onCancellationResponse: PartialFunction[JsonRpcResponseMessage, Unit] = { + case msg if pendingCancellations.containsKey(msg.id) => + pendingCancellations.remove(msg.id) match { + case null => () + case q => Util.ignoreResult(q.offer(msg.toString.contains("Task cancelled"))) + } + } + private val onCompletionResponse: PartialFunction[JsonRpcResponseMessage, Unit] = { + case msg if pendingCompletions.containsKey(msg.id) => + pendingCompletions.remove(msg.id) match { + case null => () + case completions => + completions(msg.result match { + case Some(o: JObject) => + o.value + .foldLeft(CompletionResponse(Vector.empty[String])) { + case (resp, i) => + if (i.field == "items") + resp.withItems( + Converter + .fromJson[Vector[String]](i.value) + .getOrElse(Vector.empty[String]) + ) + else if (i.field == "cachedTestNames") + resp.withCachedTestNames( + Converter.fromJson[Boolean](i.value).getOrElse(true) + ) + else if (i.field == "cachedMainClassNames") + resp.withCachedMainClassNames( + Converter.fromJson[Boolean](i.value).getOrElse(true) + ) + else resp + } + case _ => CompletionResponse(Vector.empty[String]) + }) + } + } + // cache the composed plan + private val responsePlan = Util.reduceIntents[JsonRpcResponseMessage, Unit]( + onExecResponse, + onCancellationResponse, + onAttachResponse, + onCompletionResponse, + { case _ => () }, + ) + def onResponse(msg: JsonRpcResponseMessage): Unit = responsePlan(msg) def onNotification(msg: JsonRpcNotificationMessage): Unit = { def splitToMessage: Vector[(Level.Value, String)] = @@ -623,6 +671,12 @@ class NetworkClient( case Success(params) => splitDiagnostics(params); Vector() case Failure(_) => Vector() } + case (`clientJob`, Some(json)) => + import sbt.internal.worker.codec.JsonProtocol._ + Converter.fromJson[ClientJobParams](json) match { + case Success(params) => clientSideRun(params).get; Vector.empty + case Failure(_) => Vector.empty + } case (`Shutdown`, Some(_)) => Vector.empty case (msg, _) if msg.startsWith("build/") => Vector.empty case _ => @@ -669,6 +723,58 @@ class NetworkClient( } } + private def clientSideRun(params: ClientJobParams): Try[Unit] = + params.runInfo match { + case Some(info) => clientSideRun(info) + case _ => Failure(new MessageOnlyException(s"runInfo is not specified in $params")) + } + + private def clientSideRun(runInfo: RunInfo): Try[Unit] = { + def jvmRun(info: JvmRunInfo): Try[Unit] = { + val option = ForkOptions( + javaHome = info.javaHome.map(new File(_)), + outputStrategy = None, // TODO: Handle buffered output etc + bootJars = Vector.empty, + workingDirectory = info.workingDirectory.map(new File(_)), + runJVMOptions = info.jvmOptions, + connectInput = info.connectInput, + envVars = info.environmentVariables, + ) + // ForkRun handles exit code handling and cancellation + val runner = new ForkRun(option) + runner + .run( + mainClass = info.mainClass, + classpath = info.classpath.map(_.path).map(new File(_)), + options = info.args, + log = log + ) + } + def nativeRun(info: NativeRunInfo): Try[Unit] = { + import java.lang.{ ProcessBuilder => JProcessBuilder } + val option = ForkOptions( + javaHome = None, + outputStrategy = None, // TODO: Handle buffered output etc + bootJars = Vector.empty, + workingDirectory = info.workingDirectory.map(new File(_)), + runJVMOptions = Vector.empty, + connectInput = info.connectInput, + envVars = info.environmentVariables, + ) + val command = info.cmd :: info.args.toList + val jpb = new JProcessBuilder(command: _*) + val exitCode = try Fork.blockForExitCode(Fork.forkInternal(option, Nil, jpb)) + catch { + case _: InterruptedException => + log.warn("run canceled") + 1 + } + Run.processExitCode(exitCode, "runner") + } + if (runInfo.jvm) jvmRun(runInfo.jvmRunInfo.getOrElse(sys.error("missing jvmRunInfo"))) + else nativeRun(runInfo.nativeRunInfo.getOrElse(sys.error("missing nativeRunInfo"))) + } + def onRequest(msg: JsonRpcRequestMessage): Unit = { import sbt.protocol.codec.JsonProtocol._ (msg.method, msg.params) match { @@ -1006,26 +1112,6 @@ class NetworkClient( RawInputThread.this.interrupt() } } - - // copied from Aggregation - private def timing(startTime: Long, endTime: Long): String = { - import java.text.DateFormat - val format = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM) - val nowString = format.format(new java.util.Date(endTime)) - val total = math.max(0, (endTime - startTime + 500) / 1000) - val totalString = s"$total s" + - (if (total <= 60) "" - else { - val maybeHours = total / 3600 match { - case 0 => "" - case h => f"$h%02d:" - } - val mins = f"${total % 3600 / 60}%02d" - val secs = f"${total % 60}%02d" - s" ($maybeHours$mins:$secs)" - }) - s"Total time: $totalString, completed $nowString" - } } object NetworkClient { @@ -1138,6 +1224,32 @@ object NetworkClient { ) } + private[sbt] def timing(format: DateFormat, startTime: Long, endTime: Long): String = { + // sbt#7558 + // JDK 20+ emits special space (NNBSP) as part of formatted date + // Which sometimes becomes garbled in standard output + // Therefore we replace NNBSP (u202f) with standard space (u0020) + val nowString = format.format(new Date(endTime)).replace("\u202F", "\u0020") + val total = (endTime - startTime + 500) / 1000 + val totalString = s"$total s" + + (if (total <= 60) "" + else { + val hours = total / 3600 match { + case 0 => "0" + case h => f"$h%02d" + } + val mins = f"${total % 3600 / 60}%02d" + val secs = f"${total % 60}%02d" + s" ($hours:$mins:$secs.0)" + }) + s"Total time: $totalString, completed $nowString" + } + + private[sbt] def timing(startTime: Long, endTime: Long): String = { + val format = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM) + timing(format, startTime, endTime) + } + def client( baseDirectory: File, args: Array[String], @@ -1236,7 +1348,6 @@ object NetworkClient { System.out.flush() }) Runtime.getRuntime.addShutdownHook(hook) - if (Util.isNonCygwinWindows) sbt.internal.util.JLine3.forceWindowsJansi() val parsed = parseArgs(restOfArgs) System.exit(Terminal.withStreams(isServer = false, isSubProcess = false) { val term = Terminal.console diff --git a/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala b/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala index b503e503c0..9a20f82532 100644 --- a/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala +++ b/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala @@ -79,6 +79,7 @@ trait ServerCallback { private[sbt] def authOptions: Set[ServerAuthentication] private[sbt] def authenticate(token: String): Boolean private[sbt] def setInitialized(value: Boolean): Unit + private[sbt] def setInitializeOption(opts: InitializeOption): Unit private[sbt] def onSettingQuery(execId: Option[String], req: Q): Unit private[sbt] def onCompletionRequest(execId: Option[String], cp: CP): Unit private[sbt] def onCancellationRequest(execId: Option[String], crp: CRP): Unit 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 e3540c570b..b4ceb047dd 100644 --- a/main-command/src/main/scala/sbt/internal/ui/UITask.scala +++ b/main-command/src/main/scala/sbt/internal/ui/UITask.scala @@ -17,6 +17,7 @@ import sbt.BasicKeys.{ historyPath, colorShellPrompt } import sbt.State import sbt.internal.CommandChannel import sbt.internal.util.ConsoleAppender.{ ClearPromptLine, ClearScreenAfterCursor, DeleteLine } +import sbt.internal.util.Terminal.hasConsole import sbt.internal.util._ import sbt.internal.util.complete.{ Parser } @@ -27,7 +28,7 @@ private[sbt] trait UITask extends Runnable with AutoCloseable { private[sbt] val reader: UITask.Reader private[this] final def handleInput(s: Either[String, String]): Boolean = s match { case Left(m) => channel.onFastTrackTask(m) - case Right(cmd) => channel.onCommand(cmd) + case Right(cmd) => channel.onCommandLine(cmd) } private[this] val isStopped = new AtomicBoolean(false) override def run(): Unit = { @@ -55,6 +56,20 @@ private[sbt] object UITask { object Reader { // Avoid filling the stack trace since it isn't helpful here object interrupted extends InterruptedException + + /** + * Return Left for fast track commands, otherwise return Right(...). + */ + def splitCommand(cmd: String): Either[String, String] = + // We need to put the empty string on the fast track queue so that we can + // reprompt the user if another command is running on the server. + if (cmd.isEmpty()) Left("") + else + cmd match { + case Shutdown | TerminateAction | Cancel => Left(cmd) + case cmd => Right(cmd) + } + def terminalReader(parser: Parser[_])( terminal: Terminal, state: State @@ -70,22 +85,15 @@ private[sbt] object UITask { if (thread.isInterrupted || closed.get) throw interrupted (try reader.readLine(clear + terminal.prompt.mkPrompt()) finally reader.close) match { - case None if terminal == Terminal.console && System.console == null => + case None if terminal == Terminal.console && !hasConsole => // No stdin is attached to the process so just ignore the result and // block until the thread is interrupted. 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 - case None => Left(TerminateAction) - case Some(s: String) => - s.trim() match { - // We need to put the empty string on the fast track queue so that we can - // reprompt the user if another command is running on the server. - case "" => Left("") - case cmd @ (`Shutdown` | `TerminateAction` | `Cancel`) => Left(cmd) - case cmd => Right(cmd) - } + case None => Left(TerminateAction) + case Some(s: String) => splitCommand(s.trim()) } } terminal.setPrompt(Prompt.Pending) diff --git a/main/src/main/java/sbt/internal/MetaBuildLoader.java b/main/src/main/java/sbt/internal/MetaBuildLoader.java index c1980117f6..7bc9beac9c 100644 --- a/main/src/main/java/sbt/internal/MetaBuildLoader.java +++ b/main/src/main/java/sbt/internal/MetaBuildLoader.java @@ -14,6 +14,7 @@ import java.net.URLClassLoader; import java.util.LinkedHashSet; import java.util.Set; +import java.util.Stack; import java.util.regex.Pattern; import xsbti.AppProvider; import xsbti.ScalaProvider; @@ -65,32 +66,26 @@ public void close() throws IOException { * library. */ public static MetaBuildLoader makeLoader(final AppProvider appProvider) throws IOException { - final String jlineJars = "jline-?[0-9.]+-sbt-.*|jline-terminal(-(jna|jansi))?-[0-9.]+"; + final String jlineJars = + "jline-?[0-9.]+-sbt-.*|jline-terminal(-(jni))?-[0-9.]+|jline-native-[0-9.]+"; final String testInterfaceJars = "test-interface(-.*)?"; final String compilerInterfaceJars = "compiler-interface(-.*)?"; final String utilInterfaceJars = "util-interface(-.*)?"; final String jansiJars = "jansi-[0-9.]+"; - final String jnaJars = "jna-(platform-)?[0-9.]+"; final String fullPattern = String.format( - "^(%s|%s|%s|%s|%s|%s)\\.jar", - jlineJars, - testInterfaceJars, - compilerInterfaceJars, - utilInterfaceJars, - jansiJars, - jnaJars); + "^(%s|%s|%s|%s|%s)\\.jar", + jlineJars, testInterfaceJars, compilerInterfaceJars, utilInterfaceJars, jansiJars); final Pattern pattern = Pattern.compile(fullPattern); final File[] cp = appProvider.mainClasspath(); final URL[] interfaceURLs = new URL[3]; - final URL[] jlineURLs = new URL[7]; + final Stack jlineURLs = new Stack<>(); final File[] extra = appProvider.id().classpathExtra() == null ? new File[0] : appProvider.id().classpathExtra(); final Set bottomClasspath = new LinkedHashSet<>(); { int interfaceIndex = 0; - int jlineIndex = 0; for (final File file : cp) { final String name = file.getName(); if ((name.contains("test-interface") @@ -100,8 +95,7 @@ public static MetaBuildLoader makeLoader(final AppProvider appProvider) throws I interfaceURLs[interfaceIndex] = file.toURI().toURL(); interfaceIndex += 1; } else if (pattern.matcher(name).find()) { - jlineURLs[jlineIndex] = file.toURI().toURL(); - jlineIndex += 1; + jlineURLs.push(file.toURI().toURL()); } else { bottomClasspath.add(file); } @@ -150,7 +144,7 @@ public String toString() { }; final SbtInterfaceLoader interfaceLoader = new SbtInterfaceLoader(interfaceURLs, topLoader); - final JLineLoader jlineLoader = new JLineLoader(jlineURLs, interfaceLoader); + final JLineLoader jlineLoader = new JLineLoader(jlineURLs.toArray(new URL[0]), interfaceLoader); final File[] siJars = scalaProvider.jars(); final URL[] lib = new URL[1]; int scalaRestCount = siJars.length - 1; diff --git a/main/src/main/scala/sbt/BackgroundJobService.scala b/main/src/main/scala/sbt/BackgroundJobService.scala index 6dc8deb810..2b113c1c1f 100644 --- a/main/src/main/scala/sbt/BackgroundJobService.scala +++ b/main/src/main/scala/sbt/BackgroundJobService.scala @@ -70,6 +70,8 @@ abstract class BackgroundJobService extends Closeable { def waitFor(job: JobHandle): Unit + private[sbt] def createWorkingDirectory: File + /** Copies classpath to temporary directories. */ def copyClasspath(products: Classpath, full: Classpath, workingDirectory: File): Classpath @@ -100,4 +102,5 @@ abstract class JobHandle { def id: Long def humanReadableName: String def spawningTask: ScopedKey[_] + def isAutoCancel: Boolean } diff --git a/main/src/main/scala/sbt/Cross.scala b/main/src/main/scala/sbt/Cross.scala index 8494ebb763..5fb67a6677 100644 --- a/main/src/main/scala/sbt/Cross.scala +++ b/main/src/main/scala/sbt/Cross.scala @@ -118,12 +118,14 @@ object Cross { )(command: String): (Seq[ProjectRef], String) = { import extracted._ import DefaultParsers._ - val parser = (OpOrID <~ charClass(_ == '/', "/")) ~ any.* map { - case seg1 ~ cmd => (seg1, cmd.mkString) - } + val parser = ((('{' ~> URIClass <~ '}').? ~ OpOrID <~ '/') ~ any.*.string) + .map { case uri ~ seg1 ~ cmd => (uri, seg1, cmd) } Parser.parse(command, parser) match { - case Right((seg1, cmd)) => - structure.allProjectRefs.find(_.project == seg1) match { + case Right((uri, seg1, cmd)) => + structure.allProjectRefs.find { + case p if uri.isDefined => seg1 == p.project && uri.contains(p.build.toString) + case p => seg1 == p.project + } match { case Some(proj) => (Seq(proj), cmd) case _ => (resolveAggregates(extracted), command) } @@ -187,9 +189,10 @@ object Cross { keys.toSeq.flatMap { k => project(k).withFilter(projects.contains).flatMap { p => if (p == extracted.currentRef || !projects.contains(extracted.currentRef)) { - val parts = project(k).map(_.project) ++ k.scope.config.toOption.map { - case ConfigKey(n) => n.head.toUpper + n.tail - } ++ k.scope.task.toOption.map(_.label) ++ Some(k.key.label) + val parts = project(k).map(p => s"{${p.build}}${p.project}") ++ + k.scope.config.toOption.map(c => c.name.capitalize) ++ + k.scope.task.toOption.map(_.label) ++ + Some(k.key.label) Some(v -> parts.mkString("", "/", fullArgs)) } else None } @@ -257,7 +260,7 @@ object Cross { val (aggs, aggCommand) = parseSlashCommand(Project.extract(state))(rawCmd) aggs .intersect(affectedRefs) - .map({ case ProjectRef(_, proj) => s"$proj/$aggCommand" }) + .map(p => s"{${p.build}}${p.project}/$aggCommand") .mkString("all ", " ", "") } } diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 8a87288924..04f555768f 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -9,9 +9,8 @@ package sbt import java.io.{ File, PrintWriter } -import java.net.{ URI, URL } -import java.nio.file.{ Paths, Path => NioPath } -import java.util.Optional +import java.nio.file.{ Path => NioPath } +import java.util.{ Optional, UUID } import java.util.concurrent.TimeUnit import lmcoursier.CoursierDependencyResolution import lmcoursier.definitions.{ Configuration => CConfiguration } @@ -51,11 +50,13 @@ import sbt.internal.server.{ BspCompileTask, BuildServerProtocol, BuildServerReporter, + ClientJob, Definition, LanguageServerProtocol, ServerHandler, VirtualTerminal } +import sbt.internal.sona.Sona import sbt.internal.testing.TestLogger import sbt.internal.util.Attributed.data import sbt.internal.util.Types._ @@ -100,13 +101,7 @@ import scala.xml.NodeSeq // incremental compiler import sbt.SlashSyntax0._ -import sbt.internal.inc.{ - Analysis, - AnalyzingCompiler, - ManagedLoggedReporter, - MixedAnalyzingCompiler, - ScalaInstance -} +import sbt.internal.inc.{ Analysis, AnalyzingCompiler, ManagedLoggedReporter, ScalaInstance } import xsbti.{ CrossValue, VirtualFile, VirtualFileRef } import xsbti.compile.{ AnalysisContents, @@ -186,6 +181,7 @@ object Defaults extends BuildCommon { apiMappings := Map.empty, autoScalaLibrary :== true, managedScalaInstance :== true, + allowUnsafeScalaLibUpgrade :== false, classpathEntryDefinesClass := { (file: File) => sys.error("use classpathEntryDefinesClassVF instead") }, @@ -227,12 +223,12 @@ object Defaults extends BuildCommon { closeClassLoaders :== SysProp.closeClassLoaders, allowZombieClassLoaders :== true, packageTimestamp :== Package.defaultTimestamp, - ) ++ BuildServerProtocol.globalSettings + ) ++ BuildServerProtocol.globalSettings ++ ClientJob.globalSettings private[sbt] lazy val globalIvyCore: Seq[Setting[_]] = Seq( internalConfigurationMap :== Configurations.internalMap _, - credentials :== SysProp.sbtCredentialsEnv.toList, + credentials := SysProp.sbtCredentialsEnv.toList, exportJars :== false, trackInternalDependencies :== TrackLevel.TrackAlways, exportToInternal :== TrackLevel.TrackAlways, @@ -271,6 +267,20 @@ object Defaults extends BuildCommon { csrLogger := LMCoursier.coursierLoggerTask.value, csrMavenProfiles :== Set.empty, csrReconciliations :== LMCoursier.relaxedForAllModules, + csrMavenDependencyOverride :== false, + csrSameVersions := Seq( + ScalaArtifacts.Artifacts.map(a => InclExclRule(scalaOrganization.value, a)).toSet + ), + stagingDirectory := (ThisBuild / baseDirectory).value / "target" / "sona-staging", + localStaging := Some(Resolver.file("local-staging", stagingDirectory.value)), + sonaBundle := Publishing + .makeBundle( + stagingDirectory.value.toPath(), + ((ThisBuild / baseDirectory).value / "target" / "sona-bundle" / "bundle.zip").toPath() + ) + .toFile(), + sonaBundle / aggregate :== false, + commands ++= Seq(Publishing.sonaRelease, Publishing.sonaUpload), ) /** Core non-plugin settings for sbt builds. These *must* be on every build or the sbt engine will fail to run at all. */ @@ -341,6 +351,7 @@ object Defaults extends BuildCommon { val rs = EvaluateTask.taskTimingProgress.toVector ++ EvaluateTask.taskTraceEvent.toVector rs map { Keys.TaskProgress(_) } }, + commandProgress := Seq(), // progressState is deprecated SettingKey[Option[ProgressState]]("progressState") := None, Previous.cache := new Previous( @@ -407,13 +418,12 @@ object Defaults extends BuildCommon { val boot = app.provider.scalaProvider.launcher.bootDirectory val ih = app.provider.scalaProvider.launcher.ivyHome val coursierCache = csrCacheDirectory.value - val javaHome = Paths.get(sys.props("java.home")) Map( "BASE" -> base.toPath, "SBT_BOOT" -> boot.toPath, "CSR_CACHE" -> coursierCache.toPath, "IVY_HOME" -> ih.toPath, - "JAVA_HOME" -> javaHome, + "JAVA_HOME" -> Util.javaHome, ) }, fileConverter := MappedFileConverter(rootPaths.value, allowMachinePath.value), @@ -712,7 +722,20 @@ object Defaults extends BuildCommon { crossPaths.value ), cleanIvy := IvyActions.cleanCachedResolutionCache(ivyModule.value, streams.value.log), - clean := clean.dependsOn(cleanIvy).value, + clean := { + val _ = cleanIvy.value + try { + val store = AnalysisUtil.staticCachedStore( + analysisFile = (Compile / compileAnalysisFile).value.toPath, + useTextAnalysis = !(Compile / enableBinaryCompileAnalysis).value, + useConsistent = (Compile / enableConsistentCompileAnalysis).value, + ) + store.clearCache() + } catch { + case NonFatal(_) => () + } + clean.value + }, scalaCompilerBridgeBinaryJar := Def.settingDyn { val sv = scalaVersion.value if (ScalaArtifacts.isScala3(sv) || VersionNumber(sv) @@ -729,13 +752,17 @@ object Defaults extends BuildCommon { consoleProject / scalaCompilerBridgeSource := ZincLmUtil.getDefaultBridgeSourceModule( appConfiguration.value.provider.scalaProvider.version ), + classpathOptions := ClasspathOptionsUtil.noboot(scalaVersion.value), + console / classpathOptions := ClasspathOptionsUtil.replNoboot(scalaVersion.value), ) // must be a val: duplication detected by object identity private[this] lazy val compileBaseGlobal: Seq[Setting[_]] = globalDefaults( Seq( auxiliaryClassFiles :== Nil, incOptions := IncOptions.of(), + // TODO: Kept for old Dotty plugin. Remove on sbt 2.x classpathOptions :== ClasspathOptionsUtil.boot, + // TODO: Kept for old Dotty plugin. Remove on sbt 2.x console / classpathOptions :== ClasspathOptionsUtil.repl, compileOrder :== CompileOrder.Mixed, javacOptions :== Nil, @@ -871,7 +898,12 @@ object Defaults extends BuildCommon { } def defaultCompileSettings: Seq[Setting[_]] = - globalDefaults(enableBinaryCompileAnalysis := true) + globalDefaults( + Seq( + enableBinaryCompileAnalysis :== true, + enableConsistentCompileAnalysis :== SysProp.analysis2024, + ) + ) lazy val configTasks: Seq[Setting[_]] = docTaskSettings(doc) ++ inTask(compile)(compileInputsSettings) ++ @@ -907,7 +939,20 @@ object Defaults extends BuildCommon { tastyFiles.map(_.getAbsoluteFile) } else Nil }.value, - clean := (compileOutputs / clean).value, + clean := { + val _ = (compileOutputs / clean).value + val analysisFile = compileAnalysisFile.value + try { + val store = AnalysisUtil.staticCachedStore( + analysisFile = analysisFile.toPath, + useTextAnalysis = !enableBinaryCompileAnalysis.value, + useConsistent = enableConsistentCompileAnalysis.value, + ) + store.clearCache() + } catch { + case NonFatal(_) => () + } + }, earlyOutputPing := Def.promise[Boolean], compileProgress := { val s = streams.value @@ -1144,11 +1189,68 @@ object Defaults extends BuildCommon { def scalaInstanceFromUpdate: Initialize[Task[ScalaInstance]] = Def.task { val sv = scalaVersion.value val fullReport = update.value + val s = streams.value + + // For Scala 3, update scala-library.jar in `scala-tool` and `scala-doc-tool` in case a newer version + // is present in the `compile` configuration. This is needed once forwards binary compatibility is dropped + // to avoid NoSuchMethod exceptions when expanding macros. + def updateLibraryToCompileConfiguration(report: ConfigurationReport) = + if (!ScalaArtifacts.isScala3(sv)) report + else + (for { + compileConf <- fullReport.configuration(Configurations.Compile) + compileLibMod <- compileConf.modules.find(_.module.name == ScalaArtifacts.LibraryID) + reportLibMod <- report.modules.find(_.module.name == ScalaArtifacts.LibraryID) + if VersionNumber(reportLibMod.module.revision) + .matchesSemVer(SemanticSelector(s"<${compileLibMod.module.revision}")) + } yield { + val newMods = report.modules + .filterNot(_.module.name == ScalaArtifacts.LibraryID) :+ compileLibMod + report.withModules(newMods) + }).getOrElse(report) - val toolReport = fullReport - .configuration(Configurations.ScalaTool) - .getOrElse(sys.error(noToolConfiguration(managedScalaInstance.value))) + val toolReport = updateLibraryToCompileConfiguration( + fullReport + .configuration(Configurations.ScalaTool) + .getOrElse(sys.error(noToolConfiguration(managedScalaInstance.value))) + ) + if (Classpaths.isScala213(sv)) { + val scalaDeps = for { + compileReport <- fullReport.configuration(Configurations.Compile).iterator + libName <- ScalaArtifacts.Artifacts.iterator + lib <- compileReport.modules.find(_.module.name == libName) + } yield lib + for (lib <- scalaDeps.take(1)) { + val libVer = lib.module.revision + val libName = lib.module.name + val proj = Def.displayBuildRelative(thisProjectRef.value.build, thisProjectRef.value) + if (VersionNumber(sv).matchesSemVer(SemanticSelector(s"<$libVer"))) { + val err = !allowUnsafeScalaLibUpgrade.value + val fix = + if (err) + """Upgrade the `scalaVersion` to fix the build. If upgrading the Scala compiler version is + |not possible (for example due to a regression in the compiler or a missing dependency), + |this error can be demoted by setting `allowUnsafeScalaLibUpgrade := true`.""".stripMargin + else + s"""Note that the dependency classpath and the runtime classpath of your project + |contain the newer $libName $libVer, even if the scalaVersion is $sv. + |Compilation (macro expansion) or using the Scala REPL in sbt may fail with a LinkageError.""".stripMargin + + val msg = + s"""Expected `$proj scalaVersion` to be $libVer or later, but found $sv. + |To support backwards-only binary compatibility (SIP-51), the Scala 2.13 compiler + |should not be older than $libName on the dependency classpath. + | + |$fix + | + |See `$proj evicted` to know why $libName $libVer is getting pulled in. + |""".stripMargin + if (err) sys.error(msg) + else s.log.warn(msg) + } + } + } def file(id: String): File = { val files = for { m <- toolReport.modules if m.module.name.startsWith(id) @@ -1161,6 +1263,7 @@ object Defaults extends BuildCommon { val allDocJars = fullReport .configuration(Configurations.ScalaDocTool) + .map(updateLibraryToCompileConfiguration) .toSeq .flatMap(_.modules) .flatMap(_.artifacts.map(_._2)) @@ -1393,26 +1496,34 @@ object Defaults extends BuildCommon { if (stamps.isEmpty) Long.MinValue else stamps.max } - def intlStamp(c: String, analysis: Analysis, s: Set[String]): Long = { - if (s contains c) Long.MinValue + def intlStamp0(javaClassName: String, analysis: Analysis, alreadySeen: Set[String])( + className: String + ): Set[Long] = { + import analysis.{ apis, relations } + relations + .internalClassDeps(className) + .map(intlStamp(_, analysis, alreadySeen + javaClassName)) ++ + relations.externalDeps(className).map(stamp) ++ + apis.internal.get(javaClassName).toSeq.map(_.compilationTimestamp) ++ + apis.internal.get(className).toSeq.map(_.compilationTimestamp) + } + def intlStamp(javaClassName: String, analysis: Analysis, alreadySeen: Set[String]): Long = + if (alreadySeen contains javaClassName) Long.MinValue else stamps.getOrElse( - c, { - val x = { - import analysis.{ apis, relations => rel } - rel.internalClassDeps(c).map(intlStamp(_, analysis, s + c)) ++ - rel.externalDeps(c).map(stamp) ++ - rel.productClassName.reverse(c).flatMap { pc => - apis.internal.get(pc).map(_.compilationTimestamp) - } + Long.MinValue + javaClassName, { + val x: Long = { + val classNames = analysis.relations.productClassName.reverse(javaClassName).toSeq + classNames.flatMap(intlStamp0(javaClassName, analysis, alreadySeen)) ++ Seq( + Long.MinValue + ) }.max if (x != Long.MinValue) { - stamps(c) = x + stamps(javaClassName) = x } x } ) - } def noSuccessYet(test: String) = succeeded.get(test) match { case None => true case Some(ts) => stamps.synchronized(stamp(test)) > ts @@ -1611,7 +1722,7 @@ object Defaults extends BuildCommon { + "These issues, along with others that were not enumerated above, may be" + " resolved by changing the class loader layering strategy.\n" + "The Flat and ScalaLibrary strategies bundle the full project classpath in" - + " the same class loader.\nTo use one of these strategies, set the " + + " the same class loader.\nTo use one of these strategies, set the" + " ClassLoaderLayeringStrategy key\nin your configuration, for example:\n\n" + s"set ${projectId}Test / classLoaderLayeringStrategy :=" + " ClassLoaderLayeringStrategy.ScalaLibrary\n" @@ -1718,17 +1829,17 @@ object Defaults extends BuildCommon { // drop base directories, since there are no valid mappings for these def sourceMappings: Initialize[Task[Seq[(File, String)]]] = Def.task { - val sdirs = unmanagedSourceDirectories.value + val sdirs = sourceDirectories.value val base = baseDirectory.value val relative = (f: File) => relativeTo(sdirs)(f).orElse(relativeTo(base)(f)).orElse(flat(f)) val exclude = Set(sdirs, base) - unmanagedSources.value.flatMap { + sources.value.flatMap { case s if !exclude(s) => relative(s).map(s -> _) case _ => None } } - def resourceMappings = relativeMappings(unmanagedResources, unmanagedResourceDirectories) + def resourceMappings = relativeMappings(resources, resourceDirectories) def relativeMappings( files: Taskable[Seq[File]], @@ -1768,16 +1879,8 @@ object Defaults extends BuildCommon { extraPrefix: String ): Initialize[File] = Def.setting { - val f = artifactName.value crossTarget.value / - (prefix(configuration.value.name) + extraPrefix) / f( - ScalaVersion( - (artifactName / scalaVersion).value, - (artifactName / scalaBinaryVersion).value - ), - projectID.value, - art.value - ) + (prefix(configuration.value.name) + "early") / "early.jar" } private[sbt] def prefixArtifactPathSetting( @@ -1997,6 +2100,11 @@ object Defaults extends BuildCommon { def foregroundRunMainTask: Initialize[InputTask[Unit]] = Def.inputTask { val handle = bgRunMain.evaluated + handle match { + case threadJobHandle: AbstractBackgroundJobService#ThreadJobHandle => + threadJobHandle.isAutoCancel = true + case _ => + } val service = bgJobService.value service.waitForTry(handle).get } @@ -2005,6 +2113,11 @@ object Defaults extends BuildCommon { def foregroundRunTask: Initialize[InputTask[Unit]] = Def.inputTask { val handle = bgRun.evaluated + handle match { + case threadJobHandle: AbstractBackgroundJobService#ThreadJobHandle => + threadJobHandle.isAutoCancel = true + case _ => + } val service = bgJobService.value service.waitForTry(handle).get } @@ -2252,13 +2365,15 @@ object Defaults extends BuildCommon { */ private[sbt] def compileScalaBackendTask: Initialize[Task[CompileResult]] = Def.task { val setup: Setup = compileIncSetup.value - val useBinary: Boolean = enableBinaryCompileAnalysis.value val analysisResult: CompileResult = compileIncremental.value val exportP = exportPipelining.value // Save analysis midway if pipelining is enabled if (analysisResult.hasModified && exportP) { - val store = - MixedAnalyzingCompiler.staticCachedStore(setup.cacheFile.toPath, !useBinary) + val store = AnalysisUtil.staticCachedStore( + analysisFile = setup.cacheFile.toPath, + useTextAnalysis = !enableBinaryCompileAnalysis.value, + useConsistent = enableConsistentCompileAnalysis.value, + ) val contents = AnalysisContents.create(analysisResult.analysis(), analysisResult.setup()) store.set(contents) // this stores the eary analysis (again) in case the subproject contains a macro @@ -2278,9 +2393,11 @@ object Defaults extends BuildCommon { .debug(s"${name.value}: compileEarly: blocking on earlyOutputPing") earlyOutputPing.await.value }) { - val useBinary: Boolean = enableBinaryCompileAnalysis.value - val store = - MixedAnalyzingCompiler.staticCachedStore(earlyCompileAnalysisFile.value.toPath, !useBinary) + val store = AnalysisUtil.staticCachedStore( + analysisFile = earlyCompileAnalysisFile.value.toPath, + useTextAnalysis = !enableBinaryCompileAnalysis.value, + useConsistent = enableConsistentCompileAnalysis.value, + ) store.get.toOption match { case Some(contents) => contents.getAnalysis case _ => Analysis.empty @@ -2291,13 +2408,15 @@ object Defaults extends BuildCommon { } def compileTask: Initialize[Task[CompileAnalysis]] = Def.task { val setup: Setup = compileIncSetup.value - val useBinary: Boolean = enableBinaryCompileAnalysis.value val c = fileConverter.value // TODO - expose bytecode manipulation phase. val analysisResult: CompileResult = manipulateBytecode.value if (analysisResult.hasModified) { - val store = - MixedAnalyzingCompiler.staticCachedStore(setup.cacheFile.toPath, !useBinary) + val store = AnalysisUtil.staticCachedStore( + analysisFile = setup.cacheFile.toPath, + useTextAnalysis = !enableBinaryCompileAnalysis.value, + useConsistent = enableConsistentCompileAnalysis.value, + ) val contents = AnalysisContents.create(analysisResult.analysis(), analysisResult.setup()) store.set(contents) } @@ -2315,11 +2434,11 @@ object Defaults extends BuildCommon { val ci = (compile / compileInputs).value val ping = earlyOutputPing.value val reporter = (compile / bspReporter).value - BspCompileTask.compute(bspTargetIdentifier.value, thisProjectRef.value, configuration.value) { - task => + BspCompileTask + .compute(bspTargetIdentifier.value, thisProjectRef.value, configuration.value, ci) { task => // TODO - Should readAnalysis + saveAnalysis be scoped by the compile task too? compileIncrementalTaskImpl(task, s, ci, ping, reporter) - } + } } private val incCompiler = ZincUtil.defaultIncrementalCompiler private[sbt] def compileJavaTask: Initialize[Task[CompileResult]] = Def.task { @@ -2397,11 +2516,16 @@ object Defaults extends BuildCommon { cachedPerEntryDefinesClassLookup(classpathEntry) } val extra = extraIncOptions.value.map(t2) - val useBinary: Boolean = enableBinaryCompileAnalysis.value val eapath = earlyCompileAnalysisFile.value.toPath val eaOpt = - if (exportPipelining.value) Some(MixedAnalyzingCompiler.staticCachedStore(eapath, !useBinary)) - else None + if (exportPipelining.value) { + val store = AnalysisUtil.staticCachedStore( + analysisFile = eapath, + useTextAnalysis = !enableBinaryCompileAnalysis.value, + useConsistent = enableConsistentCompileAnalysis.value, + ) + Some(store) + } else None Setup.of( lookup, (compile / skip).value, @@ -2490,9 +2614,13 @@ object Defaults extends BuildCommon { private[sbt] def jnone[A]: Optional[A] = none[A].toOptional def compileAnalysisSettings: Seq[Setting[_]] = Seq( previousCompile := { - val setup = compileIncSetup.value - val useBinary: Boolean = enableBinaryCompileAnalysis.value - val store = MixedAnalyzingCompiler.staticCachedStore(setup.cacheFile.toPath, !useBinary) + // Avoid compileIncSetup since it would trigger upstream compilation + val analysisFile = compileAnalysisFile.value + val store = AnalysisUtil.staticCachedStore( + analysisFile = analysisFile.toPath, + useTextAnalysis = !enableBinaryCompileAnalysis.value, + useConsistent = enableConsistentCompileAnalysis.value, + ) val prev = store.get().toOption match { case Some(contents) => val analysis = Option(contents.getAnalysis).toOptional @@ -2521,11 +2649,20 @@ object Defaults extends BuildCommon { } def sbtPluginExtra(m: ModuleID, sbtV: String, scalaV: String): ModuleID = - m.extra( - PomExtraDependencyAttributes.SbtVersionKey -> sbtV, - PomExtraDependencyAttributes.ScalaVersionKey -> scalaV - ) - .withCrossVersion(Disabled()) + partialVersion(sbtV) match { + case Some((0, _)) | Some((1, _)) => + m.extra( + PomExtraDependencyAttributes.SbtVersionKey -> sbtV, + PomExtraDependencyAttributes.ScalaVersionKey -> scalaV + ) + .withCrossVersion(Disabled()) + case Some(_) => + // this produces a normal suffix like _sjs1_2.13 + val prefix = s"sbt${binarySbtVersion(sbtV)}_" + m.cross(CrossVersion.binaryWith(prefix, "")) + case None => + sys.error(s"unknown sbt version $sbtV") + } def discoverSbtPluginNames: Initialize[Task[PluginDiscovery.DiscoveredNames]] = Def.taskDyn { if (sbtPlugin.value) Def.task(PluginDiscovery.discoverSourceAll(compile.value)) @@ -2542,7 +2679,8 @@ object Defaults extends BuildCommon { val cacheStore = factory.make("copy-resource") val converter = fileConverter.value val flt: File => Option[File] = flat(t) - val transform: File => Option[File] = (f: File) => rebase(dirs, t)(f).orElse(flt(f)) + val transform: File => Option[File] = + (f: File) => rebase(resourceDirectories.value.sorted, t)(f).orElse(flt(f)) val mappings: Seq[(File, File)] = resources.value.flatMap { case r if !dirs(r) => transform(r).map(r -> _) case _ => None @@ -2597,7 +2735,7 @@ object Defaults extends BuildCommon { lazy val configSettings: Seq[Setting[_]] = Classpaths.configSettings ++ configTasks ++ configPaths ++ packageConfig ++ Classpaths.compilerPluginConfig ++ deprecationSettings ++ - BuildServerProtocol.configSettings + BuildServerProtocol.configSettings ++ ClientJob.configSettings lazy val compileSettings: Seq[Setting[_]] = configSettings ++ (mainBgRunMainTask +: mainBgRunTask) ++ Classpaths.addUnmanagedLibrary @@ -2848,7 +2986,7 @@ object Classpaths { Defaults.globalDefaults( Seq( publishMavenStyle :== true, - sbtPluginPublishLegacyMavenStyle := true, + sbtPluginPublishLegacyMavenStyle :== false, publishArtifact :== true, (Test / publishArtifact) :== false ) @@ -2856,8 +2994,10 @@ object Classpaths { private lazy val publishSbtPluginMavenStyle = Def.task(sbtPlugin.value && publishMavenStyle.value) private lazy val packagedDefaultArtifacts = packaged(defaultArtifactTasks) - private lazy val emptyArtifacts = Def.task(Map.empty[Artifact, File]) - + private lazy val sbt2Plus: Def.Initialize[Boolean] = Def.setting { + val sbtV = (pluginCrossBuild / sbtBinaryVersion).value + sbtV != "1.0" && !sbtV.startsWith("0.") + } val jvmPublishSettings: Seq[Setting[_]] = Seq( artifacts := artifactDefs(defaultArtifactTasks).value, packagedArtifacts := Def @@ -2877,13 +3017,31 @@ object Classpaths { * valid POM file, that is a POM file that Maven can resolve. */ private def mavenArtifactsOfSbtPlugin: Def.Initialize[Task[Map[Artifact, File]]] = + Def.task { + // This is a conditional task. The top-level must be an if expression. + if (sbt2Plus.value) { + // Both POMs and JARs are Maven-compatible in sbt 2.x, so ignore the workarounds + packagedDefaultArtifacts.value + } else { + val crossVersion = sbtCrossVersion.value + val legacyPomArtifact = (makePom / artifact).value + def addSuffix(a: Artifact): Artifact = a.withName(crossVersion(a.name)) + Map(addSuffix(legacyPomArtifact) -> makeMavenPomOfSbtPlugin.value) ++ + pomConsistentArtifactsForLegacySbt.value ++ + legacyPackagedArtifacts.value + } + } + + private def legacyPackagedArtifacts: Def.Initialize[Task[Map[Artifact, File]]] = Def.task { + // This is a conditional task. The top-level must be an if expression. + if (sbtPluginPublishLegacyMavenStyle.value) packagedDefaultArtifacts.value + else Map.empty[Artifact, File] + } + + private def pomConsistentArtifactsForLegacySbt: Def.Initialize[Task[Map[Artifact, File]]] = Def.task { val crossVersion = sbtCrossVersion.value - val legacyArtifact = (makePom / artifact).value - val pom = makeMavenPomOfSbtPlugin.value val legacyPackages = packaged(defaultPackages).value - - def addSuffix(a: Artifact): Artifact = a.withName(crossVersion(a.name)) def copyArtifact(artifact: Artifact, file: File): (Artifact, File) = { val nameWithSuffix = crossVersion(artifact.name) val targetFile = @@ -2891,11 +3049,9 @@ object Classpaths { IO.copyFile(file, targetFile) artifact.withName(nameWithSuffix) -> targetFile } - val packages = legacyPackages.map { case (artifact, file) => copyArtifact(artifact, file) } - val legacyPackagedArtifacts = Def - .ifS(sbtPluginPublishLegacyMavenStyle.toTask)(packagedDefaultArtifacts)(emptyArtifacts) - .value - packages + (addSuffix(legacyArtifact) -> pom) ++ legacyPackagedArtifacts + legacyPackages.map { + case (artifact, file) => copyArtifact(artifact, file); + } } private def sbtCrossVersion: Def.Initialize[String => String] = Def.setting { @@ -2937,9 +3093,24 @@ object Classpaths { makeIvyXml := deliverTask(makeIvyXmlConfiguration).value, publish := publishOrSkip(publishConfiguration, publish / skip).value, publishLocal := publishOrSkip(publishLocalConfiguration, publishLocal / skip).value, - publishM2 := publishOrSkip(publishM2Configuration, publishM2 / skip).value + publishM2 := publishOrSkip(publishM2Configuration, publishM2 / skip).value, + credentials ++= { + val alreadyContainsCentralCredentials: Boolean = credentials.value.exists { + case d: DirectCredentials => d.host == Sona.host + case _ => false + } + if (!alreadyContainsCentralCredentials) SysProp.sonatypeCredentalsEnv.toSeq + else Nil + }, + sonaDeploymentName := { + val o = organization.value + val v = version.value + val uuid = UUID.randomUUID().toString().take(8) + s"$o:$v:$uuid" + }, ) + @nowarn("cat=deprecation") private[this] def baseGlobalDefaults = Defaults.globalDefaults( Seq( @@ -3010,6 +3181,7 @@ object Classpaths { ) ) + @nowarn("cat=deprecation") val ivyBaseSettings: Seq[Setting[_]] = baseGlobalDefaults ++ sbtClassifiersTasks ++ Seq( conflictWarning := conflictWarning.value.copy(label = Reference.display(thisProjectRef.value)), unmanagedBase := baseDirectory.value / "lib", @@ -3663,7 +3835,8 @@ object Classpaths { val conf = config.value val log = streams.value.log val module = ivyModule.value - IvyActions.publish(module, conf, log) + val publisherInterface = publisher.value + publisherInterface.publish(module, conf, log) } } .tag(Tags.Publish, Tags.Network) @@ -3731,7 +3904,7 @@ object Classpaths { forceUpdatePeriod.value match { case None => false case Some(period) => - val fullUpdateOutput = cacheDirectory / "out" + val fullUpdateOutput = cacheDirectory / "output" val now = System.currentTimeMillis val diff = now - IO.getModifiedTimeOrZero(fullUpdateOutput) val elapsedDuration = new FiniteDuration(diff, TimeUnit.MILLISECONDS) @@ -3931,6 +4104,8 @@ object Classpaths { def deliverPattern(outputPath: File): String = (outputPath / "[artifact]-[revision](-[classifier]).[ext]").absolutePath + private[sbt] def isScala213(sv: String) = sv.startsWith("2.13.") + private[sbt] def isScala2Scala3Sandwich(sbv1: String, sbv2: String): Boolean = { def compare(a: String, b: String): Boolean = a == "2.13" && (b.startsWith("0.") || b.startsWith("3")) diff --git a/main/src/main/scala/sbt/EvaluateTask.scala b/main/src/main/scala/sbt/EvaluateTask.scala index 29749cfdea..ec425eb23b 100644 --- a/main/src/main/scala/sbt/EvaluateTask.scala +++ b/main/src/main/scala/sbt/EvaluateTask.scala @@ -148,18 +148,20 @@ final case class PluginData( resolvers: Option[Vector[Resolver]], report: Option[UpdateReport], scalacOptions: Seq[String], + javacOptions: Seq[String], unmanagedSourceDirectories: Seq[File], unmanagedSources: Seq[File], managedSourceDirectories: Seq[File], managedSources: Seq[File], + classDirectory: Option[File], buildTarget: Option[BuildTargetIdentifier] ) { val classpath: Seq[Attributed[File]] = definitionClasspath ++ dependencyClasspath } object PluginData { - private[sbt] def apply(dependencyClasspath: Def.Classpath): PluginData = - PluginData(dependencyClasspath, Nil, None, None, Nil, Nil, Nil, Nil, Nil, None) + private[sbt] def apply(depClasspath: Def.Classpath): PluginData = + PluginData(depClasspath, Nil, None, None, Nil, Nil, Nil, Nil, Nil, Nil, None, None) } object EvaluateTask { @@ -258,12 +260,15 @@ object EvaluateTask { extracted: Extracted, structure: BuildStructure, state: State - ): ExecuteProgress[Task] = { + ): ExecuteProgress2 = { state - .get(currentTaskProgress) - .map { tp => - new ExecuteProgress[Task] { - val progress = tp.progress + .get(currentCommandProgress) + .map { progress => + new ExecuteProgress2 { + override def beforeCommand(cmd: String, state: State): Unit = + progress.beforeCommand(cmd, state) + override def afterCommand(cmd: String, result: Either[Throwable, State]): Unit = + progress.afterCommand(cmd, result) override def initial(): Unit = progress.initial() override def afterRegistered( task: Task[_], @@ -279,7 +284,9 @@ object EvaluateTask { progress.afterCompleted(task, result) override def afterAllCompleted(results: RMap[Task, Result]): Unit = progress.afterAllCompleted(results) - override def stop(): Unit = {} + override def stop(): Unit = { + // TODO: this is not a typo, but a questionable decision in 6559c3a0 that is probably obsolete + } } } .getOrElse { @@ -293,11 +300,12 @@ object EvaluateTask { (if (SysProp.taskTimings) new TaskTimings(reportOnShutdown = false, state.globalLogging.full) :: Nil else Nil) - reporters match { - case xs if xs.isEmpty => ExecuteProgress.empty[Task] - case xs if xs.size == 1 => xs.head - case xs => ExecuteProgress.aggregate[Task](xs) - } + val cmdProgress = getSetting(Keys.commandProgress, Seq(), extracted, structure) + ExecuteProgress2.aggregate(reporters match { + case xs if xs.isEmpty => cmdProgress + case xs if xs.size == 1 => cmdProgress :+ new ExecuteProgressAdapter(xs.head) + case xs => cmdProgress :+ new ExecuteProgressAdapter(ExecuteProgress.aggregate[Task](xs)) + }) } } // TODO - Should this pull from Global or from the project itself? @@ -523,6 +531,7 @@ object EvaluateTask { log.warn("Canceling execution...") RunningProcesses.killAll() ConcurrentRestrictions.cancelAll() + DefaultBackgroundJobService.stop() shutdownImpl(true) } } diff --git a/main/src/main/scala/sbt/ExecuteProgress2.scala b/main/src/main/scala/sbt/ExecuteProgress2.scala new file mode 100644 index 0000000000..006b8f8299 --- /dev/null +++ b/main/src/main/scala/sbt/ExecuteProgress2.scala @@ -0,0 +1,85 @@ +/* + * sbt + * Copyright 2023, Scala center + * Copyright 2011 - 2022, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt +import sbt.internal.util.RMap + +/** + * Tracks command execution progress. In addition to ExecuteProgress, this interface + * adds command start and end events, and gives access to the sbt.State at the beginning + * and end of each command. + * + * Command progress callbacks are wrapping task progress callbacks. That is, the `beforeCommand` + * callback will be called before the `initial` callback from ExecuteProgress, and the + * `afterCommand` callback will be called after the `stop` callback from ExecuteProgress. + */ +trait ExecuteProgress2 extends ExecuteProgress[Task] { + + /** + * Called before a command starts processing. The command has not yet been parsed. + * + * @param cmd The command string + * @param state The sbt.State before the command starts executing. + */ + def beforeCommand(cmd: String, state: State): Unit + + /** + * Called after a command finished execution. + * + * @param cmd The command string. + * @param result Left in case of an error. If the command cannot be parsed, it will be + * signalled as a ParseException with a detailed message. If the command + * was cancelled by the user, as sbt.Cancelled. If the command succeeded, + * Right with the new state after command execution. + * + */ + def afterCommand(cmd: String, result: Either[Throwable, State]): Unit +} + +class ExecuteProgressAdapter(ep: ExecuteProgress[Task]) extends ExecuteProgress2 { + override def beforeCommand(cmd: String, state: State): Unit = {} + override def afterCommand(cmd: String, result: Either[Throwable, State]): Unit = {} + override def initial(): Unit = ep.initial() + override def afterRegistered( + task: Task[_], + allDeps: Iterable[Task[_]], + pendingDeps: Iterable[Task[_]] + ): Unit = ep.afterRegistered(task, allDeps, pendingDeps) + override def afterReady(task: Task[_]): Unit = ep.afterReady(task) + override def beforeWork(task: Task[_]): Unit = ep.beforeWork(task) + override def afterWork[A](task: Task[A], result: Either[Task[A], Result[A]]): Unit = + ep.afterWork(task, result) + override def afterCompleted[A](task: Task[A], result: Result[A]): Unit = + ep.afterCompleted(task, result) + override def afterAllCompleted(results: RMap[Task, Result]): Unit = ep.afterAllCompleted(results) + override def stop(): Unit = ep.stop() +} + +object ExecuteProgress2 { + def aggregate(xs: Seq[ExecuteProgress2]): ExecuteProgress2 = new ExecuteProgress2 { + override def beforeCommand(cmd: String, state: State): Unit = + xs.foreach(_.beforeCommand(cmd, state)) + override def afterCommand(cmd: String, result: Either[Throwable, State]): Unit = + xs.foreach(_.afterCommand(cmd, result)) + override def initial(): Unit = xs.foreach(_.initial()) + override def afterRegistered( + task: Task[_], + allDeps: Iterable[Task[_]], + pendingDeps: Iterable[Task[_]] + ): Unit = xs.foreach(_.afterRegistered(task, allDeps, pendingDeps)) + override def afterReady(task: Task[_]): Unit = xs.foreach(_.afterReady(task)) + override def beforeWork(task: Task[_]): Unit = xs.foreach(_.beforeWork(task)) + override def afterWork[A](task: Task[A], result: Either[Task[A], Result[A]]): Unit = + xs.foreach(_.afterWork(task, result)) + override def afterCompleted[A](task: Task[A], result: Result[A]): Unit = + xs.foreach(_.afterCompleted(task, result)) + override def afterAllCompleted(results: RMap[Task, Result]): Unit = + xs.foreach(_.afterAllCompleted(results)) + override def stop(): Unit = xs.foreach(_.stop()) + } +} diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 0fb3c3bdc3..661199c844 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -29,6 +29,7 @@ import sbt.internal.remotecache.RemoteCacheArtifact import sbt.internal.server.BuildServerProtocol.BspFullWorkspace import sbt.internal.server.{ BuildServerReporter, ServerHandler } import sbt.internal.util.{ AttributeKey, ProgressState, SourcePosition } +import sbt.internal.worker.ClientJobParams import sbt.io._ import sbt.librarymanagement.Configurations.CompilerPlugin import sbt.librarymanagement.LibraryManagementCodec._ @@ -206,7 +207,8 @@ object Keys { val scalaCompilerBridgeSource = settingKey[ModuleID]("Configures the module ID of the sources of the compiler bridge when scalaCompilerBridgeBinaryJar is None").withRank(CSetting) val scalaCompilerBridgeScope = taskKey[Unit]("The compiler bridge scope.").withRank(DTask) val scalaArtifacts = settingKey[Seq[String]]("Configures the list of artifacts which should match the Scala binary version").withRank(CSetting) - val enableBinaryCompileAnalysis = settingKey[Boolean]("Writes the analysis file in binary format") + val enableBinaryCompileAnalysis = settingKey[Boolean]("Writes the analysis file in binary format").withRank(DSetting) + val enableConsistentCompileAnalysis = settingKey[Boolean]("Writes the analysis file in consistent binary format").withRank(DSetting) val crossJavaVersions = settingKey[Seq[String]]("The java versions used during JDK cross testing").withRank(BPlusSetting) val semanticdbEnabled = settingKey[Boolean]("Enables SemanticDB Scalac plugin").withRank(CSetting) val semanticdbCompilerPlugin = settingKey[ModuleID]("SemanticDB Scalac plugin").withRank(CSetting) @@ -419,21 +421,25 @@ object Keys { val bspBuildTargetOutputPathsItem = taskKey[OutputPathsItem]("").withRank(DTask) val bspBuildTargetCompile = inputKey[Unit]("").withRank(DTask) val bspBuildTargetCompileItem = taskKey[Int]("").withRank(DTask) - val bspBuildTargetTest = inputKey[Unit]("Corresponds to buildTarget/test request").withRank(DTask) - val bspBuildTargetRun = inputKey[Unit]("Corresponds to buildTarget/run request").withRank(DTask) - val bspBuildTargetCleanCache = inputKey[Unit]("Corresponds to buildTarget/cleanCache request").withRank(DTask) + val bspBuildTargetTest = inputKey[Unit]("Implementation of buildTarget/test").withRank(DTask) + val bspBuildTargetRun = inputKey[Unit]("Implementation of buildTarget/run").withRank(DTask) + val bspBuildTargetCleanCache = inputKey[Unit]("Implementation of buildTarget/cleanCache").withRank(DTask) val bspBuildTargetScalacOptions = inputKey[Unit]("").withRank(DTask) val bspBuildTargetScalacOptionsItem = taskKey[ScalacOptionsItem]("").withRank(DTask) + val bspBuildTargetJavacOptions = inputKey[Unit]("Implementation of buildTarget/javacOptions").withRank(DTask) + val bspBuildTargetJavacOptionsItem = taskKey[JavacOptionsItem]("Item of buildTarget/javacOptions").withRank(DTask) - val bspBuildTargetJVMRunEnvironment = inputKey[Unit]("Corresponds to the buildTarget/jvmRunEnvironment request").withRank(DTask) - val bspBuildTargetJVMTestEnvironment = inputKey[Unit]("Corresponds to the buildTarget/jvmTestEnvironment request").withRank(DTask) + val bspBuildTargetJVMRunEnvironment = inputKey[Unit]("Implementation of buildTarget/jvmRunEnvironment").withRank(DTask) + val bspBuildTargetJVMTestEnvironment = inputKey[Unit]("Implementation of buildTarget/jvmTestEnvironment").withRank(DTask) val bspBuildTargetJvmEnvironmentItem = taskKey[JvmEnvironmentItem]("Computes JVM environment item").withRank(DTask) - val bspScalaTestClasses = inputKey[Unit]("Corresponds to buildTarget/scalaTestClasses request").withRank(DTask) + val bspScalaTestClasses = inputKey[Unit]("Implementation of buildTarget/scalaTestClasses").withRank(DTask) val bspScalaTestClassesItem = taskKey[Seq[ScalaTestClassesItem]]("").withRank(DTask) - val bspScalaMainClasses = inputKey[Unit]("Corresponds to buildTarget/scalaMainClasses request").withRank(DTask) + val bspScalaMainClasses = inputKey[Unit]("Implementation of buildTarget/scalaMainClasses").withRank(DTask) val bspScalaMainClassesItem = taskKey[ScalaMainClassesItem]("").withRank(DTask) val bspReporter = taskKey[BuildServerReporter]("").withRank(DTask) + val clientJob = inputKey[ClientJobParams]("Translates a task into a job specification").withRank(Invisible) + val clientJobRunInfo = inputKey[ClientJobParams]("Translates the run task into a job specification").withRank(Invisible) val useCoursier = settingKey[Boolean]("Use Coursier for dependency resolution.").withRank(BSetting) val csrCacheDirectory = settingKey[File]("Coursier cache directory. Uses -Dsbt.coursier.home or Coursier's default.").withRank(CSetting) @@ -450,6 +456,8 @@ object Keys { val csrExtraCredentials = taskKey[Seq[lmcoursier.credentials.Credentials]]("") val csrPublications = taskKey[Seq[(lmcoursier.definitions.Configuration, lmcoursier.definitions.Publication)]]("") val csrReconciliations = settingKey[Seq[(ModuleMatchers, Reconciliation)]]("Strategy to reconcile version conflicts.") + val csrSameVersions = settingKey[Seq[Set[InclExclRule]]]("Modules to keep at the same version.") + val csrMavenDependencyOverride = settingKey[Boolean]("Enables Maven dependency override (bill of materials) support") val internalConfigurationMap = settingKey[Configuration => Configuration]("Maps configurations to the actual configuration used to define the classpath.").withRank(CSetting) val classpathConfiguration = taskKey[Configuration]("The configuration used to define the classpath.").withRank(CTask) @@ -526,6 +534,7 @@ object Keys { val otherResolvers = taskKey[Seq[Resolver]]("Resolvers not included in the main resolver chain, such as those in module configurations.").withRank(CSetting) val scalaCompilerBridgeResolvers = taskKey[Seq[Resolver]]("Resolvers used to resolve compiler bridges.").withRank(CSetting) val includePluginResolvers = settingKey[Boolean]("Include the resolvers from the metabuild.").withRank(CSetting) + @deprecated("JCenter has sunset", "1.10.4") val useJCenter = settingKey[Boolean]("Use JCenter as the default repository.").withRank(CSetting) val moduleConfigurations = settingKey[Seq[ModuleConfiguration]]("Defines module configurations, which override resolvers on a per-module basis.").withRank(BMinusSetting) val retrievePattern = settingKey[String]("Pattern used to retrieve managed dependencies to the current build.").withRank(DSetting) @@ -558,6 +567,10 @@ object Keys { val forceUpdatePeriod = settingKey[Option[FiniteDuration]]("Duration after which to force a full update to occur").withRank(CSetting) val versionScheme = settingKey[Option[String]]("""Version scheme used for the subproject: Supported values are Some("early-semver"), Some("pvp"), and Some("semver-spec")""").withRank(BSetting) val libraryDependencySchemes = settingKey[Seq[ModuleID]]("""Version scheme to use for specific modules set as "org" %% "name" % "": Supported values are "early-semver", "pvp", "semver-spec", "always", and "strict".""").withRank(BSetting) + val stagingDirectory = settingKey[File]("Local staging directory for Sonatype publishing").withRank(CSetting) + val sonaBundle = taskKey[File]("Local bundle for Sonatype publishing").withRank(DTask) + val localStaging = settingKey[Option[Resolver]]("Local staging resolver for Sonatype publishing").withRank(CSetting) + val sonaDeploymentName = settingKey[String]("The name used for deployment").withRank(DSetting) val classifiersModule = taskKey[GetClassifiersModule]("classifiers-module").withRank(CTask) val compatibilityWarningOptions = settingKey[CompatibilityWarningOptions]("Configures warnings around Maven incompatibility.").withRank(CSetting) @@ -565,6 +578,7 @@ object Keys { val conflictManager = settingKey[ConflictManager]("Selects the conflict manager to use for dependency management.").withRank(CSetting) val autoScalaLibrary = settingKey[Boolean]("Adds a dependency on scala-library if true.").withRank(ASetting) val managedScalaInstance = settingKey[Boolean]("Automatically obtains Scala tools as managed dependencies if true.").withRank(BSetting) + val allowUnsafeScalaLibUpgrade = settingKey[Boolean]("Allow the Scala library on the compilation classpath to be newer than the scalaVersion (see Scala SIP-51).").withRank(CSetting) val sbtResolver = settingKey[Resolver]("Provides a resolver for obtaining sbt as a dependency.").withRank(BMinusSetting) val sbtResolvers = settingKey[Seq[Resolver]]("The external resolvers for sbt and plugin dependencies.").withRank(BMinusSetting) val sbtDependency = settingKey[ModuleID]("Provides a definition for declaring the current version of sbt.").withRank(BMinusSetting) @@ -599,7 +613,7 @@ object Keys { object TaskProgress { def apply(progress: ExecuteProgress[Task]): TaskProgress = new TaskProgress(progress) } - private[sbt] val currentTaskProgress = AttributeKey[TaskProgress]("current-task-progress") + private[sbt] val currentCommandProgress = AttributeKey[ExecuteProgress2]("current-command-progress") private[sbt] val taskProgress = AttributeKey[sbt.internal.TaskProgress]("active-task-progress") val useSuperShell = settingKey[Boolean]("Enables (true) or disables the super shell.") val superShellMaxTasks = settingKey[Int]("The max number of tasks to display in the supershell progress report") @@ -613,6 +627,7 @@ object Keys { private[sbt] val postProgressReports = settingKey[Unit]("Internally used to modify logger.").withRank(DTask) @deprecated("No longer used", "1.3.0") private[sbt] val executeProgress = settingKey[State => TaskProgress]("Experimental task execution listener.").withRank(DTask) + val commandProgress = settingKey[Seq[ExecuteProgress2]]("Command progress listeners receive events when commands start and end, in addition to task progress events.") val lintUnused = inputKey[Unit]("Check for keys unused by other settings and tasks.") val lintIncludeFilter = settingKey[String => Boolean]("Filters key names that should be included in the lint check.") val lintExcludeFilter = settingKey[String => Boolean]("Filters key names that should be excluded in the lint check.") diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 48d24b3e59..cae9826808 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -27,6 +27,7 @@ import sbt.internal.inc.ScalaInstance import sbt.internal.io.Retry import sbt.internal.nio.{ CheckBuildSources, FileTreeRepository } import sbt.internal.server.{ BuildServerProtocol, NetworkChannel } +import sbt.internal.util.Terminal.hasConsole import sbt.internal.util.Types.{ const, idFun } import sbt.internal.util.complete.{ Parser, SizeParser } import sbt.internal.util.{ Terminal => ITerminal, _ } @@ -151,8 +152,7 @@ private[sbt] object xMain { try Some(new BootServerSocket(configuration)) -> None catch { - case e: ServerAlreadyBootingException - if System.console != null && !ITerminal.startedByRemoteClient => + case e: ServerAlreadyBootingException if hasConsole && !ITerminal.startedByRemoteClient => printThrowable(e) println("Create a new server? y/n (default y)") val exit = diff --git a/main/src/main/scala/sbt/MainLoop.scala b/main/src/main/scala/sbt/MainLoop.scala index 3c7a800a6a..aaa1d98eca 100644 --- a/main/src/main/scala/sbt/MainLoop.scala +++ b/main/src/main/scala/sbt/MainLoop.scala @@ -11,13 +11,18 @@ 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.util.{ ErrorHandling, GlobalLogBacking, Prompt, Terminal => ITerminal } +import sbt.internal.util.{ + AttributeKey, + ErrorHandling, + GlobalLogBacking, + Prompt, + Terminal => ITerminal +} import sbt.internal.{ ShutdownHooks, TaskProgress } import sbt.io.{ IO, Using } import sbt.protocol._ @@ -29,6 +34,8 @@ import scala.util.control.NonFatal import sbt.internal.FastTrackCommands import sbt.internal.SysProp +import java.text.ParseException + object MainLoop { /** Entry point to run the remaining commands in State with managed global logging.*/ @@ -212,16 +219,25 @@ object MainLoop { ) try { def process(): State = { - val progressState = state.get(sbt.Keys.currentTaskProgress) match { - case Some(_) => state - case _ => - if (state.get(Keys.stateBuildStructure).isDefined) { - val extracted = Project.extract(state) - val progress = EvaluateTask.executeProgress(extracted, extracted.structure, state) - state.put(sbt.Keys.currentTaskProgress, new Keys.TaskProgress(progress)) - } else state + def getOrSet[T](state: State, key: AttributeKey[T], value: Extracted => T): State = { + state.get(key) match { + case Some(_) => state + case _ => + if (state.get(Keys.stateBuildStructure).isDefined) { + val extracted = Project.extract(state) + state.put(key, value(extracted)) + } else state + } } - exchange.setState(progressState) + + val cmdProgressState = + getOrSet( + state, + sbt.Keys.currentCommandProgress, + extracted => EvaluateTask.executeProgress(extracted, extracted.structure, state) + ) + + exchange.setState(cmdProgressState) exchange.setExec(Some(exec)) val (restoreTerminal, termState) = channelName.flatMap(exchange.channelForName) match { case Some(c) => @@ -231,9 +247,13 @@ object MainLoop { (() => { ITerminal.set(prevTerminal) c.terminal.flush() - }) -> progressState.put(Keys.terminalKey, Terminal(c.terminal)) - case _ => (() => ()) -> progressState.put(Keys.terminalKey, Terminal(ITerminal.get)) + }) -> cmdProgressState.put(Keys.terminalKey, Terminal(c.terminal)) + case _ => (() => ()) -> cmdProgressState.put(Keys.terminalKey, Terminal(ITerminal.get)) } + + val currentCmdProgress = + cmdProgressState.get(sbt.Keys.currentCommandProgress) + currentCmdProgress.foreach(_.beforeCommand(exec.commandLine, cmdProgressState)) /* * FastTrackCommands.evaluate can be significantly faster than Command.process because * it avoids an expensive parsing step for internal commands that are easy to parse. @@ -241,16 +261,29 @@ object MainLoop { * but slower. */ val newState = try { - FastTrackCommands + var errorMsg: Option[String] = None + val res = FastTrackCommands .evaluate(termState, exec.commandLine) - .getOrElse(Command.process(exec.commandLine, termState)) + .getOrElse(Command.process(exec.commandLine, termState, m => errorMsg = Some(m))) + errorMsg match { + case Some(msg) => + currentCmdProgress.foreach( + _.afterCommand(exec.commandLine, Left(new ParseException(msg, 0))) + ) + case None => currentCmdProgress.foreach(_.afterCommand(exec.commandLine, Right(res))) + } + res } catch { case _: RejectedExecutionException => - // No stack trace since this is just to notify the user which command they cancelled - object Cancelled extends Throwable(exec.commandLine, null, true, false) { - override def toString: String = s"Cancelled: ${exec.commandLine}" - } - throw Cancelled + val cancelled = new Cancelled(exec.commandLine) + currentCmdProgress + .foreach(_.afterCommand(exec.commandLine, Left(cancelled))) + throw cancelled + + case e: Throwable => + currentCmdProgress + .foreach(_.afterCommand(exec.commandLine, Left(e))) + throw e } finally { // Flush the terminal output after command evaluation to ensure that all output // is displayed in the thin client before we report the command status. Also @@ -269,8 +302,10 @@ object MainLoop { exchange.respondStatus(doneEvent) } exchange.setExec(None) - newState.get(sbt.Keys.currentTaskProgress).foreach(_.progress.stop()) - newState.remove(sbt.Keys.currentTaskProgress).remove(Keys.terminalKey) + newState.get(sbt.Keys.currentCommandProgress).foreach(_.stop()) + newState + .remove(Keys.terminalKey) + .remove(Keys.currentCommandProgress) } state.get(CheckBuildSourcesKey) match { case Some(cbs) => @@ -341,3 +376,8 @@ object MainLoop { ExitCode(ErrorCodes.UnknownError) } else ExitCode.Success } + +// No stack trace since this is just to notify the user which command they cancelled +class Cancelled(cmdLine: String) extends Throwable(cmdLine, null, true, false) { + override def toString: String = s"Cancelled: $cmdLine" +} diff --git a/main/src/main/scala/sbt/Opts.scala b/main/src/main/scala/sbt/Opts.scala index ca271448dd..40281553b6 100644 --- a/main/src/main/scala/sbt/Opts.scala +++ b/main/src/main/scala/sbt/Opts.scala @@ -16,6 +16,7 @@ import java.net.URL import sbt.io.Path import Path._ +import scala.annotation.nowarn /** Options for well-known tasks. */ object Opts { @@ -42,22 +43,52 @@ object Opts { } object resolver { import sbt.io.syntax._ - @deprecated("Use sonatypeOssReleases instead", "1.7.0") + @deprecated("Sonatype OSS Repository Hosting (OSSRH) will be sunset on 2025-06-30", "1.7.0") val sonatypeReleases = Resolver.sonatypeRepo("releases") + + @deprecated("Sonatype OSS Repository Hosting (OSSRH) will be sunset on 2025-06-30", "1.11.2") val sonatypeOssReleases = Resolver.sonatypeOssRepos("releases") - @deprecated("Use sonatypeOssSnapshots instead", "1.7.0") + @deprecated( + """Sonatype OSS Repository Hosting (OSSRH) will be sunset on 2025-06-30; use the following instead: + resolvers += Resolver.sonatypeCentralSnapshots""", + "1.7.0" + ) val sonatypeSnapshots = Resolver.sonatypeRepo("snapshots") + + @deprecated( + """Sonatype OSS Repository Hosting (OSSRH) will be sunset on 2025-06-30; use the following instead: + resolvers += Resolver.sonatypeCentralSnapshots""", + "1.11.2" + ) val sonatypeOssSnapshots = Resolver.sonatypeOssRepos("snapshots") + @deprecated( + """Sonatype OSS Repository Hosting (OSSRH) will be sunset on 2025-06-30; use the following instead: + publishTo := { + if (isSnapshot.value) Some(Resolver.sonatypeCentralSnapshots) + else localStaging.value + }""", + "1.11.2" + ) val sonatypeStaging = MavenRepository( "sonatype-staging", "https://oss.sonatype.org/service/local/staging/deploy/maven2" ) + val mavenLocalFile = Resolver.file("Local Repository", userHome / ".m2" / "repository")( Resolver.defaultPatterns ) + @deprecated( + """Bintray was shut down""", + "1.11.2" + ) val sbtSnapshots = Resolver.bintrayRepo("sbt", "maven-snapshots") + + @deprecated( + """Bintray was shut down""", + "1.11.2" + ) val sbtIvySnapshots = Resolver.bintrayIvyRepo("sbt", "ivy-snapshots") } } @@ -77,10 +108,14 @@ object DefaultOptions { doc.title(name) ++ doc.version(version) def resolvers(snapshot: Boolean): Vector[Resolver] = { - if (snapshot) Vector(resolver.sbtSnapshots) else Vector.empty + if (snapshot) Vector(resolver.sbtSnapshots: @nowarn("cat=deprecation")) else Vector.empty } def pluginResolvers(plugin: Boolean, snapshot: Boolean): Vector[Resolver] = { - if (plugin && snapshot) Vector(resolver.sbtSnapshots, resolver.sbtIvySnapshots) + if (plugin && snapshot) + Vector( + resolver.sbtSnapshots: @nowarn("cat=deprecation"), + resolver.sbtIvySnapshots: @nowarn("cat=deprecation"), + ) else Vector.empty } def addResolvers: Setting[_] = Keys.resolvers ++= { resolvers(Keys.isSnapshot.value) } diff --git a/main/src/main/scala/sbt/PluginCross.scala b/main/src/main/scala/sbt/PluginCross.scala index a12fc4b60d..6ce9bc5c56 100644 --- a/main/src/main/scala/sbt/PluginCross.scala +++ b/main/src/main/scala/sbt/PluginCross.scala @@ -100,7 +100,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.18" + case VersionNumber(Seq(1, 0, _*), _, _) => "2.12.20" case _ => sys.error(s"Unsupported sbt binary version: $sv") } } diff --git a/main/src/main/scala/sbt/Plugins.scala b/main/src/main/scala/sbt/Plugins.scala index 92a9947cdc..42a19b4bc3 100644 --- a/main/src/main/scala/sbt/Plugins.scala +++ b/main/src/main/scala/sbt/Plugins.scala @@ -216,12 +216,12 @@ object Plugins extends PluginsFunctions { case Right(results) => log.debug(s" :: deduced result: ${results}") val selectedAtoms: List[Atom] = results.ordered - val selectedPlugins = selectedAtoms map { a => + val selectedPlugins = (selectedAtoms map { a => byAtomMap.getOrElse( a, throw AutoPluginException(s"${a} was not found in atom map.") ) - } + }).sortBy(_.getClass.getName) val forbidden: Set[AutoPlugin] = (selectedPlugins flatMap { Plugins.asExclusions }).toSet val c = selectedPlugins.toSet & forbidden diff --git a/main/src/main/scala/sbt/ScriptedPlugin.scala b/main/src/main/scala/sbt/ScriptedPlugin.scala index 85f8c9181b..7ec7dbd5fd 100644 --- a/main/src/main/scala/sbt/ScriptedPlugin.scala +++ b/main/src/main/scala/sbt/ScriptedPlugin.scala @@ -71,13 +71,11 @@ object ScriptedPlugin extends AutoPlugin { "org.scala-sbt" % "scripted-sbt" % scriptedSbt.value % ScriptedConf, "org.scala-sbt" % "sbt-launch" % scriptedSbt.value % ScriptedLaunchConf ) - case Some((1, _)) => + case _ => Seq( "org.scala-sbt" %% "scripted-sbt" % scriptedSbt.value % ScriptedConf, "org.scala-sbt" % "sbt-launch" % scriptedSbt.value % ScriptedLaunchConf ) - case Some((x, y)) => sys error s"Unknown sbt version ${scriptedSbt.value} ($x.$y)" - case None => sys error s"Unknown sbt version ${scriptedSbt.value}" }), scriptedClasspath := getJars(ScriptedConf).value, scriptedTests := scriptedTestsTask.value, diff --git a/main/src/main/scala/sbt/TemplateCommandUtil.scala b/main/src/main/scala/sbt/TemplateCommandUtil.scala index afb2c5ac7f..71f8035189 100644 --- a/main/src/main/scala/sbt/TemplateCommandUtil.scala +++ b/main/src/main/scala/sbt/TemplateCommandUtil.scala @@ -22,6 +22,7 @@ import sbt.librarymanagement._ import sbt.librarymanagement.ivy.{ IvyConfiguration, IvyDependencyResolution } import sbt.internal.inc.classpath.ClasspathUtil import BasicCommandStrings._, BasicKeys._ +import sbt.internal.util.Terminal.hasConsole private[sbt] object TemplateCommandUtil { def templateCommand: Command = templateCommand0(TemplateCommand) @@ -84,7 +85,9 @@ private[sbt] object TemplateCommandUtil { hit } match { case Some(_) => // do nothing - case None => System.err.println("Template not found for: " + arguments.mkString(" ")) + case None => + val error = "Template not found for: " + arguments.mkString(" ") + throw new IllegalArgumentException(error) } private def tryTemplate( @@ -183,7 +186,7 @@ private[sbt] object TemplateCommandUtil { "disneystreaming/smithy4s.g8" -> "A Smithy4s project", ) private def fortifyArgs(templates: List[(String, String)]): List[String] = - if (System.console eq null) Nil + if (!hasConsole) Nil else ITerminal.withStreams(true, false) { assert(templates.size <= 20, "template list cannot have more than 20 items") @@ -276,12 +279,13 @@ private[sbt] object TemplateCommandUtil { case TypelevelToolkitSlug :: Nil => typelevelToolkitTemplate() case SbtCrossPlatformSlug :: Nil => sbtCrossPlatformTemplate() case _ => - System.err.println("Local template not found for: " + arguments.mkString(" ")) + val error = "Local template not found for: " + arguments.mkString(" ") + throw new IllegalArgumentException(error) } - private final val defaultScalaV = "3.3.0" + private final val defaultScalaV = "3.3.4" private def scalaToolkitTemplate(): Unit = { - val defaultScalaToolkitV = "0.2.0" + val defaultScalaToolkitV = "0.5.0" val scalaV = ask("Scala version", defaultScalaV) val toolkitV = ask("Scala Toolkit version", defaultScalaToolkitV) val content = s""" @@ -299,7 +303,7 @@ libraryDependencies += (toolkitTest % Test) } private def typelevelToolkitTemplate(): Unit = { - val defaultTypelevelToolkitV = "0.1.3" + val defaultTypelevelToolkitV = "0.1.28" val scalaV = ask("Scala version", defaultScalaV) val toolkitV = ask("Typelevel Toolkit version", defaultTypelevelToolkitV) val content = s""" @@ -335,9 +339,9 @@ lazy val core = (projectMatrix in file("core")) IO.write(new File("build.sbt"), content) val pluginsContent = """ -addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.9.0") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.10.1") -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.10") +addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.10.0") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.17.0") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.5") """ IO.write(new File("project/plugins.sbt"), pluginsContent) copyResource("ScalaMain.scala.txt", new File("core/src/main/scala/example/Main.scala")) diff --git a/main/src/main/scala/sbt/coursierint/LMCoursier.scala b/main/src/main/scala/sbt/coursierint/LMCoursier.scala index 865fbf8993..fd7ebb5463 100644 --- a/main/src/main/scala/sbt/coursierint/LMCoursier.scala +++ b/main/src/main/scala/sbt/coursierint/LMCoursier.scala @@ -74,6 +74,7 @@ object LMCoursier { def relaxedForAllModules: Seq[(ModuleMatchers, Reconciliation)] = Vector((ModuleMatchers.all, Reconciliation.Relaxed)) + // For binary compatibility / MiMa def coursierConfiguration( rs: Seq[Resolver], interProjectDependencies: Seq[CProject], @@ -119,6 +120,113 @@ object LMCoursier { strict, depsOverrides, None, + Nil, + None, + log + ) + + // For binary compatibility / MiMa + def coursierConfiguration( + rs: Seq[Resolver], + interProjectDependencies: Seq[CProject], + extraProjects: Seq[CProject], + fallbackDeps: Seq[FallbackDependency], + appConfig: AppConfiguration, + classifiers: Option[Seq[Classifier]], + profiles: Set[String], + scalaOrg: String, + scalaVer: String, + scalaBinaryVer: String, + autoScalaLib: Boolean, + scalaModInfo: Option[ScalaModuleInfo], + excludeDeps: Seq[InclExclRule], + credentials: Seq[Credentials], + createLogger: Option[CacheLogger], + cacheDirectory: File, + reconciliation: Seq[(ModuleMatchers, Reconciliation)], + ivyHome: Option[File], + strict: Option[CStrict], + depsOverrides: Seq[ModuleID], + updateConfig: Option[UpdateConfiguration], + log: Logger + ): CoursierConfiguration = + coursierConfiguration( + rs, + interProjectDependencies, + extraProjects, + fallbackDeps, + appConfig, + classifiers, + profiles, + scalaOrg, + scalaVer, + scalaBinaryVer, + autoScalaLib, + scalaModInfo, + excludeDeps, + credentials, + createLogger, + cacheDirectory, + reconciliation, + ivyHome, + strict, + depsOverrides, + updateConfig, + Nil, + None, + log + ) + + // For binary compatibility / MiMa + def coursierConfiguration( + rs: Seq[Resolver], + interProjectDependencies: Seq[CProject], + extraProjects: Seq[CProject], + fallbackDeps: Seq[FallbackDependency], + appConfig: AppConfiguration, + classifiers: Option[Seq[Classifier]], + profiles: Set[String], + scalaOrg: String, + scalaVer: String, + scalaBinaryVer: String, + autoScalaLib: Boolean, + scalaModInfo: Option[ScalaModuleInfo], + excludeDeps: Seq[InclExclRule], + credentials: Seq[Credentials], + createLogger: Option[CacheLogger], + cacheDirectory: File, + reconciliation: Seq[(ModuleMatchers, Reconciliation)], + ivyHome: Option[File], + strict: Option[CStrict], + depsOverrides: Seq[ModuleID], + updateConfig: Option[UpdateConfiguration], + sameVersions: Seq[Set[InclExclRule]], + log: Logger + ): CoursierConfiguration = + coursierConfiguration( + rs, + interProjectDependencies, + extraProjects, + fallbackDeps, + appConfig, + classifiers, + profiles, + scalaOrg, + scalaVer, + scalaBinaryVer, + autoScalaLib, + scalaModInfo, + excludeDeps, + credentials, + createLogger, + cacheDirectory, + reconciliation, + ivyHome, + strict, + depsOverrides, + updateConfig, + sameVersions, + None, log ) @@ -144,6 +252,8 @@ object LMCoursier { strict: Option[CStrict], depsOverrides: Seq[ModuleID], updateConfig: Option[UpdateConfiguration], + sameVersions: Seq[Set[InclExclRule]], + enableDependencyOverrides: Option[Boolean], log: Logger ): CoursierConfiguration = { val coursierExcludeDeps = Inputs @@ -197,6 +307,8 @@ object LMCoursier { .withStrict(strict) .withForceVersions(userForceVersions.toVector) .withMissingOk(missingOk) + .withSameVersions(sameVersions) + .withEnableDependencyOverrides(enableDependencyOverrides) } def coursierConfigurationTask: Def.Initialize[Task[CoursierConfiguration]] = Def.task { @@ -212,7 +324,7 @@ object LMCoursier { scalaOrganization.value, sv, scalaBinaryVersion.value, - autoScalaLibrary.value && !ScalaArtifacts.isScala3(sv), + autoScalaLibrary.value && !ScalaArtifacts.isScala3(sv) && !Classpaths.isScala213(sv), scalaModuleInfo.value, allExcludeDependencies.value, CoursierInputsTasks.credentialsTask.value, @@ -223,6 +335,8 @@ object LMCoursier { CoursierInputsTasks.strictTask.value, dependencyOverrides.value, Some(updateConfiguration.value), + csrSameVersions.value, + Some(csrMavenDependencyOverride.value), streams.value.log ) } @@ -240,7 +354,7 @@ object LMCoursier { scalaOrganization.value, sv, scalaBinaryVersion.value, - autoScalaLibrary.value && !ScalaArtifacts.isScala3(sv), + autoScalaLibrary.value && !ScalaArtifacts.isScala3(sv) && !Classpaths.isScala213(sv), scalaModuleInfo.value, allExcludeDependencies.value, CoursierInputsTasks.credentialsTask.value, @@ -251,6 +365,8 @@ object LMCoursier { CoursierInputsTasks.strictTask.value, dependencyOverrides.value, Some(updateConfiguration.value), + csrSameVersions.value, + Some(csrMavenDependencyOverride.value), streams.value.log ) } @@ -268,7 +384,7 @@ object LMCoursier { scalaOrganization.value, sv, scalaBinaryVersion.value, - autoScalaLibrary.value && !ScalaArtifacts.isScala3(sv), + autoScalaLibrary.value && !ScalaArtifacts.isScala3(sv) && !Classpaths.isScala213(sv), scalaModuleInfo.value, allExcludeDependencies.value, CoursierInputsTasks.credentialsTask.value, @@ -279,6 +395,8 @@ object LMCoursier { CoursierInputsTasks.strictTask.value, dependencyOverrides.value, Some(updateConfiguration.value), + csrSameVersions.value, + Some(csrMavenDependencyOverride.value), streams.value.log ) } @@ -296,7 +414,7 @@ object LMCoursier { scalaOrganization.value, sv, scalaBinaryVersion.value, - autoScalaLibrary.value && !ScalaArtifacts.isScala3(sv), + autoScalaLibrary.value && !ScalaArtifacts.isScala3(sv) && !Classpaths.isScala213(sv), scalaModuleInfo.value, allExcludeDependencies.value, CoursierInputsTasks.credentialsTask.value, @@ -307,6 +425,8 @@ object LMCoursier { CoursierInputsTasks.strictTask.value, dependencyOverrides.value, Some(updateConfiguration.value), + csrSameVersions.value, + Some(csrMavenDependencyOverride.value), streams.value.log ) } diff --git a/main/src/main/scala/sbt/internal/AbstractTaskProgress.scala b/main/src/main/scala/sbt/internal/AbstractTaskProgress.scala index 18e6ffe2da..bce7bb2baa 100644 --- a/main/src/main/scala/sbt/internal/AbstractTaskProgress.scala +++ b/main/src/main/scala/sbt/internal/AbstractTaskProgress.scala @@ -9,6 +9,7 @@ package sbt package internal +import sbt.internal.util.Util import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicLong import scala.collection.JavaConverters._ @@ -122,7 +123,8 @@ private[sbt] abstract class AbstractTaskExecuteProgress extends ExecuteProgress[ object AbstractTaskExecuteProgress { private[sbt] class Timer() { val startNanos: Long = System.nanoTime() - val threadId: Long = Thread.currentThread().getId + val threadName: String = Thread.currentThread().getName + val threadId: Long = Util.threadId var endNanos: Long = 0L def stop(): Unit = { endNanos = System.nanoTime() diff --git a/main/src/main/scala/sbt/internal/Aggregation.scala b/main/src/main/scala/sbt/internal/Aggregation.scala index 8a26dcd77e..311e1beb1d 100644 --- a/main/src/main/scala/sbt/internal/Aggregation.scala +++ b/main/src/main/scala/sbt/internal/Aggregation.scala @@ -17,6 +17,7 @@ import sbt.SlashSyntax0._ import sbt.internal.util.complete.Parser import sbt.internal.util.complete.Parser.{ failure, seq, success } import sbt.internal.util._ +import sbt.internal.client.NetworkClient import sbt.std.Transform.DummyTaskMap import sbt.util.{ Logger, Show } import scala.annotation.nowarn @@ -152,20 +153,7 @@ object Aggregation { } def timing(format: java.text.DateFormat, startTime: Long, endTime: Long): String = { - val nowString = format.format(new java.util.Date(endTime)) - val total = (endTime - startTime + 500) / 1000 - val totalString = s"$total s" + - (if (total <= 60) "" - else { - val maybeHours = total / 3600 match { - case 0 => "" - case h => f"$h%02d:" - } - val mins = f"${total % 3600 / 60}%02d" - val secs = f"${total % 60}%02d" - s" ($maybeHours$mins:$secs)" - }) - s"Total time: $totalString, completed $nowString" + NetworkClient.timing(format, startTime, endTime) } def defaultFormat: DateFormat = { diff --git a/main/src/main/scala/sbt/internal/AnalysisUtil.scala b/main/src/main/scala/sbt/internal/AnalysisUtil.scala new file mode 100644 index 0000000000..049e3cf5ab --- /dev/null +++ b/main/src/main/scala/sbt/internal/AnalysisUtil.scala @@ -0,0 +1,39 @@ +/* + * sbt + * Copyright 2023, Scala center + * Copyright 2011 - 2022, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt +package internal + +import java.nio.file.Path +import sbt.internal.inc.MixedAnalyzingCompiler +import xsbti.compile.{ AnalysisStore => XAnalysisStore } +import xsbti.compile.analysis.ReadWriteMappers + +private[sbt] object AnalysisUtil { + // some machines have many cores. + // we don't want to occupy them all for analysis serialization. + lazy val parallelism: Int = + scala.math.min( + Runtime.getRuntime.availableProcessors(), + 8, + ) + def staticCachedStore( + analysisFile: Path, + useTextAnalysis: Boolean, + useConsistent: Boolean, + ): XAnalysisStore = + MixedAnalyzingCompiler.staticCachedStore( + analysisFile = analysisFile, + useTextAnalysis = useTextAnalysis, + useConsistent = useConsistent, + mappers = ReadWriteMappers.getEmptyMappers(), + // reproducisble = true will wipe out the timestamp, which we need for sbt 1.x + reproducible = false, + parallelism = parallelism, + ) +} diff --git a/main/src/main/scala/sbt/internal/Banner.scala b/main/src/main/scala/sbt/internal/Banner.scala index 559f01ef1c..0a3c415e63 100644 --- a/main/src/main/scala/sbt/internal/Banner.scala +++ b/main/src/main/scala/sbt/internal/Banner.scala @@ -11,6 +11,23 @@ package sbt.internal private[sbt] object Banner { def apply(version: String): Option[String] = version match { + case v if v.startsWith("1.11.0") => + Some(s""" + |Here are some highlights of sbt 1.11.0: + | - The Central Repository publishing + |See https://eed3si9n.com/sbt-1.11.0 for full release notes. + |Hide the banner for this release by running `skipBanner`. + |""".stripMargin.linesIterator.mkString("\n")) + case v if v.startsWith("1.10.0") => + Some(s""" + |Here are some highlights of sbt 1.10.0: + | - SIP-51 support for Scala 2.13 evolution + | - Various Zinc fixes + | - ConsistentAnalysisFormat: new Zinc Analysis serialization + | - CommandProgress API + |See https://eed3si9n.com/sbt-1.10.0 for full release notes. + |Hide the banner for this release by running `skipBanner`. + |""".stripMargin.linesIterator.mkString("\n")) case v if v.startsWith("1.9.0") => Some(s""" |Here are some highlights of sbt 1.9.0: diff --git a/main/src/main/scala/sbt/internal/ClassLoaders.scala b/main/src/main/scala/sbt/internal/ClassLoaders.scala index e54ebfa0b3..a1d2095105 100644 --- a/main/src/main/scala/sbt/internal/ClassLoaders.scala +++ b/main/src/main/scala/sbt/internal/ClassLoaders.scala @@ -12,7 +12,6 @@ package internal import java.io.File import java.net.URL import java.nio.file.Path - import sbt.ClassLoaderLayeringStrategy._ import sbt.Keys._ import sbt.SlashSyntax0._ @@ -22,10 +21,12 @@ import sbt.internal.inc.classpath.ClasspathUtil import sbt.internal.util.Attributed import sbt.internal.util.Attributed.data import sbt.io.IO +import sbt.librarymanagement.ScalaArtifacts import sbt.nio.FileStamp import sbt.nio.FileStamp.LastModified import sbt.nio.Keys._ import sbt.util.Logger +import xsbti.ArtifactInfo private[sbt] object ClassLoaders { private implicit class SeqFileOps(val files: Seq[File]) extends AnyVal { @@ -154,14 +155,24 @@ private[sbt] object ClassLoaders { case _: AllLibraryJars => true case _ => false } + val cpFiles = fullCP.map(_._1) val scalaLibraryLayer = { + val jars = + if (ScalaArtifacts.isScala3(si.version) || Classpaths.isScala213(si.version)) + cpFiles + .filter(f => { + val name = f.getName + name.contains(ArtifactInfo.ScalaLibraryID) || si.libraryJars + .exists(_.getName == name) + }) + .toArray + else si.libraryJars cache.apply( - si.libraryJars.map(j => j -> IO.getModifiedTimeOrZero(j)).toList, + jars.map(j => j -> IO.getModifiedTimeOrZero(j)).toList, interfaceLoader, - () => new ScalaLibraryClassLoader(si.libraryJars.map(_.toURI.toURL), interfaceLoader) + () => new ScalaLibraryClassLoader(jars.map(_.toURI.toURL), interfaceLoader) ) } - val cpFiles = fullCP.map(_._1) val allDependencies = cpFiles.filter(allDependenciesSet) def isReflectJar(f: File): Boolean = diff --git a/main/src/main/scala/sbt/internal/Continuous.scala b/main/src/main/scala/sbt/internal/Continuous.scala index b78f167d20..50e3a4d01e 100644 --- a/main/src/main/scala/sbt/internal/Continuous.scala +++ b/main/src/main/scala/sbt/internal/Continuous.scala @@ -1129,7 +1129,7 @@ private[sbt] object Continuous extends DeprecatedContinuous { val callbacks: Callbacks, val dynamicInputs: mutable.Set[DynamicInput], val pending: Boolean, - var failAction: Option[Watch.Action], + var terminationAction: Option[Watch.Action], ) { def this( count: Int, @@ -1162,7 +1162,8 @@ private[sbt] object Continuous extends DeprecatedContinuous { afterWatch, callbacks, dynamicInputs, - p + p, + terminationAction, ) private def withCount(c: Int): ContinuousState = new ContinuousState( @@ -1173,7 +1174,8 @@ private[sbt] object Continuous extends DeprecatedContinuous { afterWatch, callbacks, dynamicInputs, - pending + pending, + terminationAction, ) } } @@ -1341,12 +1343,13 @@ private[sbt] object ContinuousCommands { case Watch.Trigger => Right(s"$runWatch ${channel.name}") case Watch.Reload => val rewatch = s"$ContinuousExecutePrefix ${ws.count} ${cs.commands mkString "; "}" + cs.terminationAction = Some(Watch.Reload) stop.map(_ :: "reload" :: rewatch :: Nil mkString "; ") case Watch.Prompt => stop.map(_ :: s"$PromptChannel ${channel.name}" :: Nil mkString ";") case Watch.Run(commands) => stop.map(_ +: commands.map(_.commandLine).filter(_.nonEmpty) mkString "; ") case a @ Watch.HandleError(_) => - cs.failAction = Some(a) + cs.terminationAction = Some(a) stop.map(_ :: s"$failWatch ${channel.name}" :: Nil mkString "; ") case _ => stop } @@ -1394,7 +1397,7 @@ private[sbt] object ContinuousCommands { } val commands = cs.commands.mkString("; ") val count = cs.count - val action = cs.failAction.getOrElse(Watch.CancelWatch) + val action = cs.terminationAction.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 diff --git a/main/src/main/scala/sbt/internal/CrossJava.scala b/main/src/main/scala/sbt/internal/CrossJava.scala index 38765232c0..6761b30f1a 100644 --- a/main/src/main/scala/sbt/internal/CrossJava.scala +++ b/main/src/main/scala/sbt/internal/CrossJava.scala @@ -412,13 +412,19 @@ private[sbt] object CrossJava { class MacOsDiscoverConfig extends JavaDiscoverConf { val base: File = file("/Library") / "Java" / "JavaVirtualMachines" + // User-specific JDKs are installed, for example, by IntelliJ IDEA + private val baseInUserHome: File = Path.userHome / "Library" / "Java" / "JavaVirtualMachines" def javaHomes: Vector[(String, File)] = - wrapNull(base.list()) - .collect { - case dir @ JavaHomeDir(version) => - version -> (base / dir / "Contents" / "Home") - } + findAllHomes(base) ++ + findAllHomes(baseInUserHome) + + private def findAllHomes(root: File): Vector[(String, File)] = { + wrapNull(root.list()).collect { + case dir @ JavaHomeDir(version) => + version -> (root / dir / "Contents" / "Home") + } + } } class JabbaDiscoverConfig extends JavaDiscoverConf { diff --git a/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala b/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala index 37d3022290..232204d467 100644 --- a/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala +++ b/main/src/main/scala/sbt/internal/DefaultBackgroundJobService.scala @@ -114,7 +114,8 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe override val spawningTask: ScopedKey[_], val logger: ManagedLogger, val workingDirectory: File, - val job: BackgroundJob + val job: BackgroundJob, + @volatile var isAutoCancel: Boolean = false, ) extends AbstractJobHandle { // EC for onStop handler below implicit val executionContext: ExecutionContext = @@ -140,6 +141,17 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe private final class DeadHandle(override val id: Long, override val humanReadableName: String) extends AbstractJobHandle { override val spawningTask: ScopedKey[_] = unknownTask + override val isAutoCancel = false + } + + private[sbt] def createWorkingDirectory: File = { + val id = nextId.getAndIncrement() + createWorkingDirectory(id) + } + private[sbt] def createWorkingDirectory(id: Long): File = { + val workingDir = serviceTempDir / s"job-$id" + IO.createDirectory(workingDir) + workingDir } def doRunInBackground( @@ -151,8 +163,7 @@ private[sbt] abstract class AbstractBackgroundJobService extends BackgroundJobSe val extracted = Project.extract(state) val logger = LogManager.constructBackgroundLog(extracted.structure.data, state, context)(spawningTask) - val workingDir = serviceTempDir / s"job-$id" - IO.createDirectory(workingDir) + val workingDir = createWorkingDirectory(id) val job = try { new ThreadJobHandle(id, spawningTask, logger, workingDir, start(logger, workingDir)) } catch { @@ -508,6 +519,15 @@ private[sbt] object DefaultBackgroundJobService { backgroundJobServices.values.forEach(_.shutdown()) backgroundJobServices.clear() } + + private[sbt] def stop(): Unit = { + backgroundJobServices + .values() + .forEach(jobService => { + jobService.jobs.filter(_.isAutoCancel).foreach(jobService.stop) + }) + } + private[sbt] lazy val backgroundJobServiceSetting: Setting[_] = (GlobalScope / Keys.bgJobService) := { val path = (GlobalScope / sbt.Keys.bgJobServiceDirectory).value diff --git a/main/src/main/scala/sbt/internal/GCUtil.scala b/main/src/main/scala/sbt/internal/GCUtil.scala index 51d08d41c7..53a5c00317 100644 --- a/main/src/main/scala/sbt/internal/GCUtil.scala +++ b/main/src/main/scala/sbt/internal/GCUtil.scala @@ -36,10 +36,6 @@ private[sbt] object GCUtil { log.debug(s"Forcing garbage collection...") // Force the detection of finalizers for scala.reflect weakhashsets System.gc() - // Force finalizers to run. - System.runFinalization() - // Force actually cleaning the weak hash maps. - System.gc() } catch { case NonFatal(_) => // gotta catch em all } diff --git a/main/src/main/scala/sbt/internal/InstallSbtn.scala b/main/src/main/scala/sbt/internal/InstallSbtn.scala index 8278269923..6315a2a6dd 100644 --- a/main/src/main/scala/sbt/internal/InstallSbtn.scala +++ b/main/src/main/scala/sbt/internal/InstallSbtn.scala @@ -11,6 +11,7 @@ package internal import Def._ import Keys.{ sbtVersion, state, terminal } +import sbt.internal.util.Terminal.hasConsole import java.io.{ File, FileInputStream, FileOutputStream, InputStream, IOException } import java.net.URI @@ -37,7 +38,7 @@ private[sbt] object InstallSbtn { Files.deleteIfExists(tmp) () } - val shell = if (System.console != null) getShell(term) else "none" + val shell = if (hasConsole) getShell(term) else "none" shell match { case "none" => case s => @@ -65,10 +66,16 @@ private[sbt] object InstallSbtn { if (Properties.isWin) "pc-win32.exe" else if (Properties.isLinux) "pc-linux" else "apple-darwin" - val isArmArchitecture: Boolean = sys.props - .getOrElse("os.arch", "") - .toLowerCase(java.util.Locale.ROOT) == "aarch64" - val arch = if (Properties.isLinux && isArmArchitecture) "aarch64" else "x86_64" + val isArmArchitecture: Boolean = { + val prop = sys.props + .getOrElse("os.arch", "") + .toLowerCase(java.util.Locale.ROOT) + prop == "arm64" || prop == "aarch64" + } + val arch = + if (Properties.isWin) "x86_64" + else if (Properties.isLinux && isArmArchitecture) "aarch64" + else "universal" val sbtnName = s"sbt/bin/sbtn-$arch-$bin" val fis = new FileInputStream(sbtZip.toFile) val zipInputStream = new ZipInputStream(fis) diff --git a/main/src/main/scala/sbt/internal/InternalDependencies.scala b/main/src/main/scala/sbt/internal/InternalDependencies.scala index 4c866b17bb..6a1bf74d67 100644 --- a/main/src/main/scala/sbt/internal/InternalDependencies.scala +++ b/main/src/main/scala/sbt/internal/InternalDependencies.scala @@ -13,7 +13,9 @@ import sbt.Keys._ private[sbt] object InternalDependencies { def configurations: Def.Initialize[Seq[(ProjectRef, Set[String])]] = Def.setting { - val allConfigs = Classpaths.allConfigs(configuration.value).map(_.name).toSet + val configMap = internalConfigurationMap.value + val config = configMap(configuration.value) + val allConfigs = Classpaths.allConfigs(config).map(_.name).toSet val ref = thisProjectRef.value val projectDependencies = buildDependencies.value.classpath.get(ref).toSeq.flatten val applicableConfigs = allConfigs + "*" diff --git a/main/src/main/scala/sbt/internal/LibraryManagement.scala b/main/src/main/scala/sbt/internal/LibraryManagement.scala index 220115e6ab..750a51c9f8 100644 --- a/main/src/main/scala/sbt/internal/LibraryManagement.scala +++ b/main/src/main/scala/sbt/internal/LibraryManagement.scala @@ -110,13 +110,16 @@ private[sbt] object LibraryManagement { } /* Skip resolve if last output exists, otherwise error. */ - def skipResolve(cache: CacheStore): UpdateInputs => UpdateReport = { + def skipResolve(cache: CacheStore)(inputs: UpdateInputs): UpdateReport = { import sbt.librarymanagement.LibraryManagementCodec._ - Tracked.lastOutput[UpdateInputs, UpdateReport](cache) { - case (_, Some(out)) => markAsCached(out) - case _ => - sys.error("Skipping update requested, but update has not previously run successfully.") - } + val cachedReport = Tracked + .lastOutput[UpdateInputs, UpdateReport](cache) { + case (_, Some(out)) => out + case _ => + sys.error("Skipping update requested, but update has not previously run successfully.") + } + .apply(inputs) + markAsCached(cachedReport) } // Mark UpdateReport#stats as "cached." This is used by the dependers later @@ -127,15 +130,21 @@ private[sbt] object LibraryManagement { def doResolve(cache: CacheStore): UpdateInputs => UpdateReport = { val doCachedResolve = { (inChanged: Boolean, updateInputs: UpdateInputs) => import sbt.librarymanagement.LibraryManagementCodec._ - val cachedResolve = Tracked.lastOutput[UpdateInputs, UpdateReport](cache) { - case (_, Some(out)) if upToDate(inChanged, out) => markAsCached(out) - case pair => - log.debug(s"""not up to date. inChanged = $inChanged, force = $force""") - resolve - } - import scala.util.control.Exception.catching - catching(classOf[NullPointerException], classOf[OutOfMemoryError]) - .withApply { t => + try { + var isCached = false + val report = Tracked + .lastOutput[UpdateInputs, UpdateReport](cache) { + case (_, Some(out)) if upToDate(inChanged, out) => + isCached = true + out + case pair => + log.debug(s"""not up to date. inChanged = $inChanged, force = $force""") + resolve + } + .apply(updateInputs) + if (isCached) markAsCached(report) else report + } catch { + case t @ (_: NullPointerException | _: OutOfMemoryError) => val resolvedAgain = resolve val culprit = t.getClass.getSimpleName log.warn(s"Update task caching failed due to $culprit.") @@ -143,8 +152,7 @@ private[sbt] object LibraryManagement { resolvedAgain.toString.linesIterator.foreach(log.warn(_)) log.trace(t) resolvedAgain - } - .apply(cachedResolve(updateInputs)) + } } import LibraryManagementCodec._ Tracked.inputChanged(cacheStoreFactory.make("inputs"))(doCachedResolve) @@ -155,7 +163,7 @@ private[sbt] object LibraryManagement { val extraInputHash = module.extraInputHash val settings = module.moduleSettings val outStore = cacheStoreFactory.make("output") - val handler = if (skip && !force) skipResolve(outStore) else doResolve(outStore) + val handler = if (skip && !force) skipResolve(outStore)(_) else doResolve(outStore) // Remove clock for caching purpose val withoutClock = updateConfig.withLogicalClock(LogicalClock.unknown) handler((extraInputHash, settings, withoutClock)) @@ -185,7 +193,11 @@ private[sbt] object LibraryManagement { import config.{ updateConfiguration => c, module => mod } import mod.{ id, dependencies => deps, scalaModuleInfo } val base = restrictedCopy(id, true).withName(id.name + "$" + label) - val module = lm.moduleDescriptor(base, deps, scalaModuleInfo) + val moduleSettings = ModuleDescriptorConfiguration(base, ModuleInfo(base.name)) + .withScalaModuleInfo(scalaModuleInfo) + .withDependencies(deps) + .withConfigurations(mod.configurations) + val module = lm.moduleDescriptor(moduleSettings) val report = lm.update(module, c, uwconfig, log) match { case Right(r) => r case Left(w) => @@ -253,7 +265,7 @@ private[sbt] object LibraryManagement { forceUpdatePeriod.value match { case None => false case Some(period) => - val fullUpdateOutput = cacheDirectory / "out" + val fullUpdateOutput = cacheDirectory / "output" val now = System.currentTimeMillis val diff = now - fullUpdateOutput.lastModified() val elapsedDuration = new scala.concurrent.duration.FiniteDuration( diff --git a/main/src/main/scala/sbt/internal/LintUnused.scala b/main/src/main/scala/sbt/internal/LintUnused.scala index c2d2db0062..d41169be72 100644 --- a/main/src/main/scala/sbt/internal/LintUnused.scala +++ b/main/src/main/scala/sbt/internal/LintUnused.scala @@ -34,6 +34,7 @@ object LintUnused { commands, crossScalaVersions, crossSbtVersions, + allowUnsafeScalaLibUpgrade, initialize, lintUnusedKeysOnLoad, onLoad, @@ -43,6 +44,7 @@ object LintUnused { serverConnectionType, serverIdleTimeout, shellPrompt, + sonaDeploymentName, ), includeLintKeys := Set( scalacOptions, diff --git a/main/src/main/scala/sbt/internal/Load.scala b/main/src/main/scala/sbt/internal/Load.scala index 3a3da38103..f3f2265a61 100755 --- a/main/src/main/scala/sbt/internal/Load.scala +++ b/main/src/main/scala/sbt/internal/Load.scala @@ -79,7 +79,7 @@ private[sbt] object Load { val dependencyResolution = IvyDependencyResolution(ivyConfiguration) val si = ScalaInstance(scalaProvider.version, scalaProvider.launcher) val zincDir = BuildPaths.getZincDirectory(state, globalBase) - val classpathOptions = ClasspathOptionsUtil.boot + val classpathOptions = ClasspathOptionsUtil.noboot(si.version) val scalac = ZincLmUtil.scalaCompiler( scalaInstance = si, classpathOptions = classpathOptions, @@ -1054,7 +1054,7 @@ private[sbt] object Load { def settings(files: Seq[File]): Seq[Setting[_]] = { if (files.nonEmpty) log.info( - s"${files.map(_.getName).mkString(s"loading settings for project ${p.id} from ", ",", " ...")}" + s"${files.map(_.getName).mkString(s"loading settings for project ${p.id} from ", ", ", "...")}" ) for { file <- files @@ -1176,21 +1176,25 @@ private[sbt] object Load { val prod = (Configurations.Runtime / exportedProducts).value val cp = (Configurations.Runtime / fullClasspath).value val opts = (Configurations.Compile / scalacOptions).value + val javaOpts = (Configurations.Compile / javacOptions).value val unmanagedSrcDirs = (Configurations.Compile / unmanagedSourceDirectories).value val unmanagedSrcs = (Configurations.Compile / unmanagedSources).value val managedSrcDirs = (Configurations.Compile / managedSourceDirectories).value val managedSrcs = (Configurations.Compile / managedSources).value val buildTarget = (Configurations.Compile / bspTargetIdentifier).value + val clsDir = (Configurations.Compile / classDirectory).value PluginData( removeEntries(cp, prod), prod, Some(fullResolvers.value.toVector), Some(update.value), opts, + javaOpts, unmanagedSrcDirs, unmanagedSrcs, managedSrcDirs, managedSrcs, + Some(clsDir), Some(buildTarget) ) }, @@ -1244,11 +1248,7 @@ private[sbt] object Load { } def noPlugins(dir: File, config: LoadBuildConfiguration): LoadedPlugins = - loadPluginDefinition( - dir, - config, - PluginData(config.globalPluginClasspath, Nil, None, None, Nil, Nil, Nil, Nil, Nil, None) - ) + loadPluginDefinition(dir, config, PluginData(config.globalPluginClasspath)) def buildPlugins(dir: File, s: State, config: LoadBuildConfiguration): LoadedPlugins = loadPluginDefinition(dir, config, buildPluginDefinition(dir, s, config)) @@ -1444,6 +1444,8 @@ final case class LoadBuildConfiguration( Nil, Nil, Nil, + Nil, + None, None ) case None => PluginData(globalPluginClasspath) diff --git a/main/src/main/scala/sbt/internal/SysProp.scala b/main/src/main/scala/sbt/internal/SysProp.scala index bcb2fe8c03..33a33e8f22 100644 --- a/main/src/main/scala/sbt/internal/SysProp.scala +++ b/main/src/main/scala/sbt/internal/SysProp.scala @@ -139,6 +139,8 @@ object SysProp { def useLog4J: Boolean = getOrFalse("sbt.log.uselog4j") def turbo: Boolean = getOrFalse("sbt.turbo") def pipelining: Boolean = getOrFalse("sbt.pipelining") + // opt-in or out of Zinc's consistent Analysis format. + def analysis2024: Boolean = getOrFalse("sbt.analysis2024") def taskTimings: Boolean = getOrFalse("sbt.task.timings") def taskTimingsOnShutdown: Boolean = getOrFalse("sbt.task.timings.on.shutdown") @@ -224,6 +226,17 @@ object SysProp { lazy val sbtCredentialsEnv: Option[Credentials] = sys.env.get("SBT_CREDENTIALS").map(raw => new FileCredentials(new File(raw))) + def sonatypeCredentalsEnv: Option[Credentials] = + for { + username <- sys.env.get("SONATYPE_USERNAME") + password <- sys.env.get("SONATYPE_PASSWORD") + } yield Credentials( + "Sonatype Nexus Repository Manager", + sona.Sona.host, + username, + password + ) + private[sbt] def setSwovalTempDir(): Unit = { val _ = getOrUpdateSwovalTmpDir( runtimeDirectory.resolve("swoval").toString diff --git a/main/src/main/scala/sbt/internal/TaskTraceEvent.scala b/main/src/main/scala/sbt/internal/TaskTraceEvent.scala index 57b2900e75..914ee84a19 100644 --- a/main/src/main/scala/sbt/internal/TaskTraceEvent.scala +++ b/main/src/main/scala/sbt/internal/TaskTraceEvent.scala @@ -61,7 +61,7 @@ private[sbt] final class TaskTraceEvent def durationEvent(name: String, cat: String, t: Timer): String = { val sb = new java.lang.StringBuilder(name.length + 2) CompactPrinter.print(new JString(name), sb) - s"""{"name": ${sb.toString}, "cat": "$cat", "ph": "X", "ts": ${(t.startMicros)}, "dur": ${(t.durationMicros)}, "pid": 0, "tid": ${t.threadId}}""" + s"""{"name": ${sb.toString}, "cat": "$cat", "ph": "X", "ts": ${(t.startMicros)}, "dur": ${(t.durationMicros)}, "pid": 0, "tid": "${t.threadId}"}""" } val entryIterator = currentTimings while (entryIterator.hasNext) { diff --git a/main/src/main/scala/sbt/internal/VirtualFileValueCache.scala b/main/src/main/scala/sbt/internal/VirtualFileValueCache.scala index 2f2c97c0ca..1140649e32 100644 --- a/main/src/main/scala/sbt/internal/VirtualFileValueCache.scala +++ b/main/src/main/scala/sbt/internal/VirtualFileValueCache.scala @@ -32,8 +32,10 @@ object VirtualFileValueCache { } } def apply[A](converter: FileConverter)(f: VirtualFile => A): VirtualFileValueCache[A] = { - import collection.mutable.{ HashMap, Map } - val stampCache: Map[VirtualFileRef, (Long, XStamp)] = new HashMap + import collection.concurrent.Map + import java.util.concurrent.ConcurrentHashMap + import scala.collection.JavaConverters._ + val stampCache: Map[VirtualFileRef, (Long, XStamp)] = new ConcurrentHashMap().asScala make( Stamper.timeWrap(stampCache, converter, { case (vf: VirtualFile) => Stamper.forContentHash(vf) diff --git a/main/src/main/scala/sbt/internal/graph/rendering/Statistics.scala b/main/src/main/scala/sbt/internal/graph/rendering/Statistics.scala index e569b916d0..720d15c9f9 100644 --- a/main/src/main/scala/sbt/internal/graph/rendering/Statistics.scala +++ b/main/src/main/scala/sbt/internal/graph/rendering/Statistics.scala @@ -47,6 +47,9 @@ object Statistics { } def format(stats: ModuleStats): String = { + import java.util.Locale + val dl = Locale.getDefault + Locale.setDefault(Locale.US) import stats._ def mb(bytes: Long): Double = bytes.toDouble / 1000000 val selfSize = @@ -54,7 +57,10 @@ object Statistics { case Some(size) => f"${mb(size)}%7.3f" case None => "-------" } - f"${mb(transitiveSize)}%7.3f MB $selfSize MB $numTransitiveDependencies%4d $numDirectDependencies%4d ${id.idString}%s" + val r = + f"${mb(transitiveSize)}%7.3f MB $selfSize MB $numTransitiveDependencies%4d $numDirectDependencies%4d ${id.idString}%s" + Locale.setDefault(dl) + r } val allStats = diff --git a/main/src/main/scala/sbt/internal/librarymanagement/Publishing.scala b/main/src/main/scala/sbt/internal/librarymanagement/Publishing.scala new file mode 100644 index 0000000000..eca6e22198 --- /dev/null +++ b/main/src/main/scala/sbt/internal/librarymanagement/Publishing.scala @@ -0,0 +1,68 @@ +/* + * sbt + * Copyright 2023, Scala center + * Copyright 2011 - 2022, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt +package internal +package librarymanagement + +import java.nio.file.Path +import sbt.internal.util.MessageOnlyException +import sbt.io.IO +import sbt.io.Path.contentOf +import sbt.librarymanagement.ivy.Credentials +import sona.{ Sona, PublishingType } + +object Publishing { + val sonaRelease: Command = + Command.command("sonaRelease")(sonatypeReleaseAction(PublishingType.Automatic)) + + val sonaUpload: Command = + Command.command("sonaUpload")(sonatypeReleaseAction(PublishingType.UserManaged)) + + def makeBundle(stagingDir: Path, bundlePath: Path): Path = { + if (bundlePath.toFile().exists()) { + IO.delete(bundlePath.toFile()) + } + IO.zip( + sources = contentOf(stagingDir.toFile()), + outputZip = bundlePath.toFile(), + time = Some(0L), + ) + bundlePath + } + + private def sonatypeReleaseAction(pt: PublishingType)(s0: State): State = { + val extracted = Project.extract(s0) + val log = extracted.get(Keys.sLog) + val dn = extracted.get(Keys.sonaDeploymentName) + val v = extracted.get(Keys.version) + if (v.endsWith("-SNAPSHOT")) { + log.error("""SNAPSHOTs are not supported on the Central Portal; +configure ThisBuild / publishTo to publish directly to the central-snapshots. +see https://www.scala-sbt.org/1.x/docs/Using-Sonatype.html for details.""") + s0.fail + } else { + val (s1, bundle) = extracted.runTask(Keys.sonaBundle, s0) + val (s2, creds) = extracted.runTask(Keys.credentials, s1) + val client = fromCreds(creds) + try { + client.uploadBundle(bundle.toPath(), dn, pt, log) + s2 + } finally { + client.close() + } + } + } + + private def fromCreds(creds: Seq[Credentials]): Sona = { + val cred = Credentials + .forHost(creds, Sona.host) + .getOrElse(throw new MessageOnlyException(s"no credentials are found for ${Sona.host}")) + Sona.oauthClient(cred.userName, cred.passwd) + } +} diff --git a/main/src/main/scala/sbt/internal/server/BspCompileTask.scala b/main/src/main/scala/sbt/internal/server/BspCompileTask.scala index 99cd0898a8..b2aed4e55f 100644 --- a/main/src/main/scala/sbt/internal/server/BspCompileTask.scala +++ b/main/src/main/scala/sbt/internal/server/BspCompileTask.scala @@ -11,24 +11,34 @@ package sbt.internal.server import sbt._ import sbt.internal.bsp._ import sbt.internal.io.Retry -import sbt.internal.server.BspCompileTask.{ compileReport, exchange } +import sbt.internal.server.BspCompileTask.compileReport +import sbt.internal.server.BspCompileTask.exchange import sbt.librarymanagement.Configuration +import sbt.util.InterfaceUtil import sjsonnew.support.scalajson.unsafe.Converter +import xsbti.CompileFailed +import xsbti.Problem +import xsbti.Severity import xsbti.compile.CompileResult -import xsbti.{ CompileFailed, Problem, Severity } +import xsbti.compile.Inputs import scala.util.control.NonFatal object BspCompileTask { private lazy val exchange = StandardMain.exchange - def compute(targetId: BuildTargetIdentifier, project: ProjectRef, config: Configuration)( + def compute( + targetId: BuildTargetIdentifier, + project: ProjectRef, + config: Configuration, + ci: Inputs + )( compile: BspCompileTask => CompileResult ): CompileResult = { - val task = BspCompileTask(targetId, project, config) + val task = BspCompileTask(targetId, project, config, ci) try { task.notifyStart() - val result = Retry(compile(task)) + val result = Retry.io(compile(task)) task.notifySuccess(result) result } catch { @@ -45,22 +55,24 @@ object BspCompileTask { private def apply( targetId: BuildTargetIdentifier, project: ProjectRef, - config: Configuration + config: Configuration, + inputs: Inputs ): BspCompileTask = { val taskId = TaskId(BuildServerTasks.uniqueId, Vector()) val targetName = BuildTargetName.fromScope(project.project, config.name) - BspCompileTask(targetId, targetName, taskId, System.currentTimeMillis()) + new BspCompileTask(targetId, targetName, taskId, inputs, System.currentTimeMillis()) } private def compileReport( problems: Seq[Problem], targetId: BuildTargetIdentifier, - elapsedTimeMillis: Long + elapsedTimeMillis: Long, + isNoOp: Option[Boolean] ): CompileReport = { val countBySeverity = problems.groupBy(_.severity()).mapValues(_.size) val warnings = countBySeverity.getOrElse(Severity.Warn, 0) val errors = countBySeverity.getOrElse(Severity.Error, 0) - CompileReport(targetId, None, errors, warnings, Some(elapsedTimeMillis.toInt)) + CompileReport(targetId, None, errors, warnings, Some(elapsedTimeMillis.toInt), isNoOp) } } @@ -68,6 +80,7 @@ case class BspCompileTask private ( targetId: BuildTargetIdentifier, targetName: String, id: TaskId, + inputs: Inputs, startTimeMillis: Long ) { import sbt.internal.bsp.codec.JsonProtocol._ @@ -89,7 +102,8 @@ case class BspCompileTask private ( sourceInfos.values.flatMap(_.getReportedProblems).toSeq case _ => Seq() } - val report = compileReport(problems, targetId, elapsedTimeMillis) + val isNoOp = InterfaceUtil.toOption(inputs.previousResult.analysis).map(_ == result.analysis) + val report = compileReport(problems, targetId, elapsedTimeMillis, isNoOp) val params = TaskFinishParams( id, endTimeMillis, @@ -122,7 +136,7 @@ case class BspCompileTask private ( val endTimeMillis = System.currentTimeMillis() val elapsedTimeMillis = endTimeMillis - startTimeMillis val problems = cause.map(_.problems().toSeq).getOrElse(Seq.empty[Problem]) - val report = compileReport(problems, targetId, elapsedTimeMillis) + val report = compileReport(problems, targetId, elapsedTimeMillis, None) val params = TaskFinishParams( id, endTimeMillis, diff --git a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala index e54a0139b5..9bb371a78e 100644 --- a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala @@ -33,8 +33,8 @@ 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.nio.file.Paths import java.util.concurrent.atomic.AtomicBoolean import scala.collection.mutable @@ -44,6 +44,8 @@ import scala.util.{ Failure, Success, Try } import scala.annotation.nowarn import sbt.testing.Framework import scala.collection.immutable.ListSet +import xsbti.VirtualFileRef +import java.util.concurrent.atomic.AtomicReference object BuildServerProtocol { import sbt.internal.bsp.codec.JsonProtocol._ @@ -201,24 +203,12 @@ object BuildServerProtocol { }.evaluated, bspBuildTargetCleanCache / aggregate := false, bspBuildTargetScalacOptions := bspInputTask { (state, _, workspace, filter) => - val builds = workspace.builds Def.task { val items = bspBuildTargetScalacOptionsItem.result.all(filter).value val appProvider = appConfiguration.value.provider() val sbtJars = appProvider.mainClasspath() - val buildItems = builds.map { - build => - val plugins: LoadedPlugins = build._2.unit.plugins - val scalacOptions = plugins.pluginData.scalacOptions - val pluginClassPath = plugins.classpath - val classpath = (pluginClassPath ++ sbtJars).map(_.toURI).toVector - val item = ScalacOptionsItem( - build._1, - scalacOptions.toVector, - classpath, - new File(build._2.localBase, "project/target").toURI - ) - Value(item) + val buildItems = workspace.builds.map { + case (targetId, build) => Value(scalacOptionsBuildItem(sbtJars, targetId, build)) } val successfulItems = anyOrThrow(items ++ buildItems) val result = ScalacOptionsResult(successfulItems.toVector) @@ -226,6 +216,20 @@ object BuildServerProtocol { } }.evaluated, bspBuildTargetScalacOptions / aggregate := false, + bspBuildTargetJavacOptions := bspInputTask { (state, _, workspace, filter) => + Def.task { + val items = bspBuildTargetJavacOptionsItem.result.all(filter).value + val appProvider = appConfiguration.value.provider() + val sbtJars = appProvider.mainClasspath() + val buildItems = workspace.builds.map { + case (targetId, build) => Value(javacOptionsBuildItem(sbtJars, targetId, build)) + } + val successfulItems = anyOrThrow(items ++ buildItems) + val result = JavacOptionsResult(successfulItems.toVector) + state.respondEvent(result) + } + }.evaluated, + bspBuildTargetJavacOptions / aggregate := false, bspScalaTestClasses := bspInputTask { (state, _, workspace, filter) => workspace.warnIfBuildsNonEmpty(Method.ScalaTestClasses, state.log) Def.task { @@ -244,7 +248,7 @@ object BuildServerProtocol { state.respondEvent(result) } }.evaluated, - bspScalaMainClasses / aggregate := false + bspScalaMainClasses / aggregate := false, ) // This will be scoped to Compile, Test, IntegrationTest etc @@ -286,7 +290,20 @@ object BuildServerProtocol { }, bspBuildTargetCompileItem := bspCompileTask.value, bspBuildTargetRun := bspRunTask.evaluated, - bspBuildTargetScalacOptionsItem := scalacOptionsTask.value, + bspBuildTargetScalacOptionsItem := { + val target = Keys.bspTargetIdentifier.value + val scalacOptions = Keys.scalacOptions.value.toVector + val classDirectory = Keys.classDirectory.value + val classpath = classpathTask.value + ScalacOptionsItem(target, scalacOptions, classpath, classDirectory.toURI) + }, + bspBuildTargetJavacOptionsItem := { + val target = Keys.bspTargetIdentifier.value + val javacOptions = Keys.javacOptions.value.toVector + val classDirectory = Keys.classDirectory.value + val classpath = classpathTask.value + JavacOptionsItem(target, javacOptions, classpath, classDirectory.toURI) + }, bspBuildTargetJVMRunEnvironment := bspInputTask { (state, _, _, filter) => Def.task { val items = bspBuildTargetJvmEnvironmentItem.result.all(filter).value @@ -314,11 +331,13 @@ object BuildServerProtocol { val underlying = (Keys.compile / compilerReporter).value val logger = streams.value.log val meta = isMetaBuild.value + val spms = sourcePositionMappers.value if (bspEnabled.value) { new BuildServerReporterImpl( targetId, bspCompileStateInstance, converter, + Defaults.foldMappers(spms, reportAbsolutePath.value, fileConverter.value), meta, logger, underlying @@ -326,9 +345,9 @@ object BuildServerProtocol { } else { new BuildServerForwarder(meta, logger, underlying) } - } + }, ) - private object Method { + private[sbt] object Method { final val Initialize = "build/initialize" final val BuildTargets = "workspace/buildTargets" final val Reload = "workspace/reload" @@ -596,12 +615,19 @@ object BuildServerProtocol { val thisProjectRef = Keys.thisProjectRef.value val thisConfig = Keys.configuration.value val scalaJars = Keys.scalaInstance.value.allJars.map(_.toURI.toString) + val (javaHomeForTarget, isForkedJava) = javaHome.value match { + case Some(forkedJava) => (Some(forkedJava.toURI), true) + case None => (sys.props.get("java.home").map(Paths.get(_)).map(_.toUri), false) + } + val javaVersionForTarget = extractJavaVersion(javacOptions.value, isForkedJava) + val jvmBuildTarget = JvmBuildTarget(javaHomeForTarget, javaVersionForTarget) val compileData = ScalaBuildTarget( scalaOrganization = scalaOrganization.value, scalaVersion = scalaVersion.value, scalaBinaryVersion = scalaBinaryVersion.value, platform = ScalaPlatform.JVM, - jars = scalaJars.toVector + jars = scalaJars.toVector, + jvmBuildTarget = jvmBuildTarget, ) val configuration = Keys.configuration.value val displayName = BuildTargetName.fromScope(thisProject.id, configuration.name) @@ -641,7 +667,11 @@ object BuildServerProtocol { scalaVersion = scalaProvider.version(), scalaBinaryVersion = binaryScalaVersion(scalaProvider.version()), platform = ScalaPlatform.JVM, - jars = scalaJars.toVector.map(_.toURI.toString) + jars = scalaJars.toVector.map(_.toURI.toString), + jvmBuildTarget = JvmBuildTarget( + sys.props.get("java.home").map(Paths.get(_)).map(_.toUri), + sys.props.get("java.version") + ), ) val sbtVersionValue = sbtVersion.value val sbtData = SbtBuildTarget( @@ -665,6 +695,34 @@ object BuildServerProtocol { ) } + private def scalacOptionsBuildItem( + sbtJars: Seq[File], + targetId: BuildTargetIdentifier, + build: LoadedBuildUnit + ): ScalacOptionsItem = { + val plugins: LoadedPlugins = build.unit.plugins + val scalacOptions = plugins.pluginData.scalacOptions.toVector + val pluginClassPath = plugins.classpath + val classpath = (pluginClassPath ++ sbtJars).map(_.toURI).toVector + val classDirectory = plugins.pluginData.classDirectory.map(_.toURI) + val item = ScalacOptionsItem(targetId, scalacOptions, classpath, classDirectory) + item + } + + private def javacOptionsBuildItem( + sbtJars: Seq[File], + targetId: BuildTargetIdentifier, + build: LoadedBuildUnit + ): JavacOptionsItem = { + val plugins: LoadedPlugins = build.unit.plugins + val javacOptions = plugins.pluginData.javacOptions.toVector + val pluginClassPath = plugins.classpath + val classpath = (pluginClassPath ++ sbtJars).map(_.toURI).toVector + val classDirectory = plugins.pluginData.classDirectory.map(_.toURI) + val item = JavacOptionsItem(targetId, javacOptions, classpath, classDirectory) + item + } + private def bspInputTask[T]( taskImpl: ( State, @@ -697,26 +755,17 @@ object BuildServerProtocol { ) } - private def scalacOptionsTask: Def.Initialize[Task[ScalacOptionsItem]] = Def.taskDyn { - val target = Keys.bspTargetIdentifier.value - val scalacOptions = Keys.scalacOptions.value - val classDirectory = Keys.classDirectory.value + private lazy val classpathTask: Def.Initialize[Task[Vector[URI]]] = Def.taskDyn { val externalDependencyClasspath = Keys.externalDependencyClasspath.value - val internalDependencyClasspath = for { (ref, configs) <- bspInternalDependencyConfigurations.value config <- configs } yield ref / config / Keys.classDirectory - Def.task { val classpath = internalDependencyClasspath.join.value.distinct ++ externalDependencyClasspath.map(_.data) - ScalacOptionsItem( - target, - scalacOptions.toVector, - classpath.map(_.toURI).toVector, - classDirectory.toURI - ) + + classpath.map(_.toURI).toVector } } @@ -948,6 +997,26 @@ object BuildServerProtocol { ) } + private def extractJavaVersion( + javacOptions: Seq[String], + isForkedJava: Boolean + ): Option[String] = { + def getVersionAfterFlag(flag: String): Option[String] = { + val index = javacOptions.indexOf(flag) + if (index >= 0) javacOptions.lift(index + 1) + else None + } + + val versionFromJavacOption = getVersionAfterFlag("--release") + .orElse(getVersionAfterFlag("--target")) + .orElse(getVersionAfterFlag("-target")) + + versionFromJavacOption.orElse { + // TODO: extract java version from forked javac + if (isForkedJava) None else sys.props.get("java.version") + } + } + // naming convention still seems like the only reliable way to get IntelliJ to import this correctly // https://github.com/JetBrains/intellij-scala/blob/a54c2a7c157236f35957049cbfd8c10587c9e60c/scala/scala-impl/src/org/jetbrains/sbt/language/SbtFileImpl.scala#L82-L84 private def toSbtTargetIdName(ref: LoadedBuildUnit): String = { @@ -1030,11 +1099,14 @@ object BuildServerProtocol { private[server] final class BspCompileState { /** - * keeps track of problems in given file so BSP reporter - * can omit unnecessary diagnostics updates + * keeps track of problems in a given file in a map of virtual source file to text documents. + * In most cases the only text document is the source file. In case of source generation, + * e.g. Twirl, the text documents are the input files, e.g. the Twirl files. + * We use the sourcePositionMappers to build this map. */ - val hasAnyProblems: java.util.Set[Path] = - java.util.concurrent.ConcurrentHashMap.newKeySet[Path] + val problemsBySourceFiles + : AtomicReference[Map[VirtualFileRef, Vector[TextDocumentIdentifier]]] = + new AtomicReference(Map.empty) /** * keeps track of those projects that were compiled at @@ -1042,6 +1114,6 @@ object BuildServerProtocol { * are compiled for the first time. * see: https://github.com/scalacenter/bloop/issues/726 */ - val compiledAtLeastOnce: AtomicBoolean = new AtomicBoolean(false) + val isFirstReport: AtomicBoolean = new AtomicBoolean(true) } } diff --git a/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala b/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala index fbf2d9a087..b4ef0d320f 100644 --- a/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala +++ b/main/src/main/scala/sbt/internal/server/BuildServerReporter.scala @@ -8,8 +8,6 @@ package sbt.internal.server -import java.nio.file.Path - import sbt.StandardMain import sbt.internal.bsp._ import sbt.internal.util.ManagedLogger @@ -27,7 +25,11 @@ import xsbti.{ import scala.collection.JavaConverters._ import scala.collection.mutable +import java.nio.file.Path +/** +Provides methods for sending success and failure reports and publishing diagnostics. + */ sealed trait BuildServerReporter extends Reporter { private final val sigFilesWritten = "[sig files written]" private final val pureExpression = "a pure expression does nothing in statement position" @@ -71,10 +73,16 @@ sealed trait BuildServerReporter extends Reporter { override def comment(pos: XPosition, msg: String): Unit = underlying.comment(pos, msg) } +/** + * @param bspCompileState what has already been reported in previous compilation. + * @param sourcePositionMapper a function that maps an xsbti.Position from the generated source + * (the Scala file) to the input file of the generator (e.g. Twirl file) + */ final class BuildServerReporterImpl( buildTarget: BuildTargetIdentifier, bspCompileState: BspCompileState, converter: FileConverter, + sourcePositionMapper: xsbti.Position => xsbti.Position, protected override val isMetaBuild: Boolean, protected override val logger: ManagedLogger, protected override val underlying: Reporter @@ -83,13 +91,13 @@ final class BuildServerReporterImpl( import sbt.internal.inc.JavaInterfaceUtil._ private lazy val exchange = StandardMain.exchange - private val problemsByFile = mutable.Map[Path, Vector[Diagnostic]]() + private val problemsByFile = mutable.Map[Path, Vector[Problem]]() // 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] = + private def toDocument(ref: VirtualFileRef): Option[TextDocumentIdentifier] = if (ref.id().contains("<")) None - else Some(converter.toPath(ref)) + else Some(TextDocumentIdentifier(converter.toPath(ref).toUri)) /** * Send diagnostics from the compilation to the client. @@ -97,65 +105,44 @@ final class BuildServerReporterImpl( * * @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) - } { - // clear problems for current file - val hadProblems = bspCompileState.hasAnyProblems.remove(filePath) - - val reportedProblems = infos.getReportedProblems.toVector - val diagnostics = reportedProblems.map(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 sendSuccessReport(analysis: CompileAnalysis): Unit = { + for ((source, infos) <- analysis.readSourceInfos.getAllSourceInfos.asScala) { + val problems = infos.getReportedProblems.toVector + sendReport(source, problems) } + notifyFirstReport() } - override def sendFailureReport(sources: Array[VirtualFile]): Unit = { - 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) - } + override def sendFailureReport(sources: Array[VirtualFile]): Unit = { + for (source <- sources) { + val problems = problemsByFile.getOrElse(converter.toPath(source), Vector.empty) + sendReport(source, problems) + } + notifyFirstReport() + } - if (shouldPublish) { + private def sendReport(source: VirtualFileRef, problems: Vector[Problem]): Unit = { + val oldDocuments = getAndClearPreviousDocuments(source) + + // publish diagnostics if: + // 1. file had any problems previously: update them with new ones + // 2. file has fresh problems: report them + // 3. build project is compiled for the first time: send success report + if (oldDocuments.nonEmpty || problems.nonEmpty || isFirstReport) { + val diagsByDocuments = problems + .flatMap(mapProblemToDiagnostic) + .groupBy { case (document, _) => document } + .mapValues(_.map { case (_, diag) => diag }) + updateNewDocuments(source, diagsByDocuments.keys.toVector) + + // send a report for the new documents, the old ones and the source file + (diagsByDocuments.keySet ++ oldDocuments ++ toDocument(source)).foreach { document => + val diags = diagsByDocuments.getOrElse(document, Vector.empty) val params = PublishDiagnosticsParams( - textDocument = TextDocumentIdentifier(filePath.toUri), + document, buildTarget, originId = None, - diagnostics, + diags, reset = true ) exchange.notifyEvent("build/publishDiagnostics", params) @@ -166,12 +153,17 @@ final class BuildServerReporterImpl( protected override def publishDiagnostic(problem: Problem): Unit = { for { id <- problem.position.sourcePath.toOption - filePath <- toSafePath(VirtualFileRef.of(id)) + (document, diagnostic) <- mapProblemToDiagnostic(problem) } { - val diagnostic = toDiagnostic(problem) - problemsByFile(filePath) = problemsByFile.getOrElse(filePath, Vector.empty) :+ diagnostic + // Note: We're putting the real path in `fileRef` because the `id` String can take + // two forms, either a ${something}/relativePath, or the absolute path of the source. + // But where we query this, we always have _only_ a ${something}/relativePath available. + // So here we "normalize" to the real path. + val fileRef = converter.toPath(VirtualFileRef.of(id)) + problemsByFile(fileRef) = problemsByFile.getOrElse(fileRef, Vector.empty) :+ problem + val params = PublishDiagnosticsParams( - TextDocumentIdentifier(filePath.toUri), + document, buildTarget, originId = None, Vector(diagnostic), @@ -181,13 +173,51 @@ final class BuildServerReporterImpl( } } - private def toRange(pos: XPosition): Range = { - val startLineOpt = pos.startLine.toOption.map(_.toLong - 1) - val startColumnOpt = pos.startColumn.toOption.map(_.toLong) - val endLineOpt = pos.endLine.toOption.map(_.toLong - 1) - val endColumnOpt = pos.endColumn.toOption.map(_.toLong) - val lineOpt = pos.line.toOption.map(_.toLong - 1) - val columnOpt = pos.pointer.toOption.map(_.toLong) + private def getAndClearPreviousDocuments(source: VirtualFileRef): Seq[TextDocumentIdentifier] = + bspCompileState.problemsBySourceFiles.getAndUpdate(_ - source).getOrElse(source, Seq.empty) + + private def updateNewDocuments( + source: VirtualFileRef, + documents: Vector[TextDocumentIdentifier] + ): Unit = { + val _ = bspCompileState.problemsBySourceFiles.updateAndGet(_ + (source -> documents)) + } + + private def isFirstReport: Boolean = bspCompileState.isFirstReport.get + private def notifyFirstReport(): Unit = { + val _ = bspCompileState.isFirstReport.set(false) + } + + /** + * Map a given problem, in a Scala source file, to a Diagnostic in an user-facing source file. + * E.g. if the source file is generated from Twirl, the diagnostic will be reported to the Twirl file. + */ + private def mapProblemToDiagnostic( + problem: Problem + ): Option[(TextDocumentIdentifier, Diagnostic)] = { + val mappedPosition = sourcePositionMapper(problem.position) + for { + mappedSource <- mappedPosition.sourcePath.toOption + document <- toDocument(VirtualFileRef.of(mappedSource)) + } yield { + val diagnostic = Diagnostic( + toRange(mappedPosition), + Option(toDiagnosticSeverity(problem.severity)), + problem.diagnosticCode().toOption.map(_.code), + Option("sbt"), + problem.message + ) + (document, diagnostic) + } + } + + private def toRange(position: xsbti.Position): Range = { + val startLineOpt = position.startLine.toOption.map(_.toLong - 1) + val startColumnOpt = position.startColumn.toOption.map(_.toLong) + val endLineOpt = position.endLine.toOption.map(_.toLong - 1) + val endColumnOpt = position.endColumn.toOption.map(_.toLong) + val lineOpt = position.line.toOption.map(_.toLong - 1) + val columnOpt = position.pointer.toOption.map(_.toLong) def toPosition(lineOpt: Option[Long], columnOpt: Option[Long]): Option[Position] = lineOpt.map(line => Position(line, columnOpt.getOrElse(0L))) @@ -199,45 +229,6 @@ final class BuildServerReporterImpl( Range(startPos, endPosOpt.getOrElse(startPos)) } - private def toDiagnostic(problem: Problem): Diagnostic = { - val actions0 = problem.actions().asScala.toVector - val data = - if (actions0.isEmpty) None - else - Some( - ScalaDiagnostic( - actions = actions0.map { a => - ScalaAction( - title = a.title, - description = a.description.toOption, - edit = Some( - ScalaWorkspaceEdit( - changes = a.edit.changes().asScala.toVector.map { edit => - ScalaTextEdit( - range = toRange(edit.position), - newText = edit.newText, - ) - } - ) - ), - ) - } - ) - ) - Diagnostic( - range = toRange(problem.position), - severity = Option(toDiagnosticSeverity(problem.severity)), - code = problem.diagnosticCode().toOption.map(_.code), - source = Option("sbt"), - message = problem.message, - relatedInformation = Vector.empty, - dataKind = data.map { _ => - "scala" - }, - data = data, - ) - } - private def toDiagnosticSeverity(severity: Severity): Long = severity match { case Severity.Info => DiagnosticSeverity.Information case Severity.Warn => DiagnosticSeverity.Warning diff --git a/main/src/main/scala/sbt/internal/server/ClientJob.scala b/main/src/main/scala/sbt/internal/server/ClientJob.scala new file mode 100644 index 0000000000..e016b98f00 --- /dev/null +++ b/main/src/main/scala/sbt/internal/server/ClientJob.scala @@ -0,0 +1,94 @@ +/* + * sbt + * Copyright 2023, Scala center + * Copyright 2011 - 2022, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt +package internal +package server + +import java.io.File +import sbt.BuildSyntax._ +import sbt.Def._ +import sbt.Keys._ +import sbt.SlashSyntax0._ +import sbt.internal.util.complete.Parser +import sbt.internal.worker.{ ClientJobParams, FilePath, JvmRunInfo, RunInfo } +import sbt.io.IO +import sbt.protocol.Serialization + +/** + * A ClientJob represents a unit of work that sbt server process + * can outsourse back to the client. Initially intended for sbtn client-side run. + */ +object ClientJob { + lazy val globalSettings: Seq[Def.Setting[_]] = Seq( + clientJob := clientJobTask.evaluated, + clientJob / aggregate := false, + ) + + private def clientJobTask: Def.Initialize[InputTask[ClientJobParams]] = Def.inputTaskDyn { + val tokens = spaceDelimited().parsed + val state = Keys.state.value + val p = Act.aggregatedKeyParser(state) + if (tokens.isEmpty) { + sys.error("expected an argument, for example foo/run") + } + val scopedKey = Parser.parse(tokens.head, p) match { + case Right(x :: Nil) => x + case Right(xs) => sys.error("too many keys") + case Left(err) => sys.error(err) + } + if (scopedKey.key == run.key) + clientJobRunInfo.in(scopedKey.scope).toTask(" " + tokens.tail.mkString(" ")) + else sys.error(s"unsupported task for clientJob $scopedKey") + } + + // This will be scoped to Compile, Test, etc + lazy val configSettings: Seq[Def.Setting[_]] = Seq( + clientJobRunInfo := clientJobRunInfoTask.evaluated, + ) + + private def clientJobRunInfoTask: Def.Initialize[InputTask[ClientJobParams]] = Def.inputTask { + val state = Keys.state.value + val args = spaceDelimited().parsed + val mainClass = (Keys.run / Keys.mainClass).value + val service = bgJobService.value + val fo = (Keys.run / Keys.forkOptions).value + val workingDir = service.createWorkingDirectory + val cp = service.copyClasspath( + exportedProductJars.value, + fullClasspathAsJars.value, + workingDir, + hashContents = true, + ) + val strategy = fo.outputStrategy.map(_.getClass().getSimpleName().filter(_ != '$')) + // sbtn doesn't set java.home, so we need to do the fallback here + val javaHome = + fo.javaHome.map(IO.toURI).orElse(sys.props.get("java.home").map(x => IO.toURI(new File(x)))) + val jvmRunInfo = JvmRunInfo( + args = args.toVector, + classpath = cp.map(x => IO.toURI(x.data)).map(FilePath(_, "")).toVector, + mainClass = mainClass.getOrElse(sys.error("no main class")), + connectInput = fo.connectInput, + javaHome = javaHome, + outputStrategy = strategy, + workingDirectory = fo.workingDirectory.map(IO.toURI), + jvmOptions = fo.runJVMOptions, + environmentVariables = fo.envVars.toMap, + ) + val info = RunInfo( + jvm = true, + jvmRunInfo = jvmRunInfo, + ) + val result = ClientJobParams( + runInfo = info + ) + import sbt.internal.worker.codec.JsonProtocol._ + state.notifyEvent(Serialization.clientJob, result) + result + } +} diff --git a/main/src/main/scala/sbt/internal/server/Definition.scala b/main/src/main/scala/sbt/internal/server/Definition.scala index 4deed73859..669c9dbbdc 100644 --- a/main/src/main/scala/sbt/internal/server/Definition.scala +++ b/main/src/main/scala/sbt/internal/server/Definition.scala @@ -24,7 +24,7 @@ import sjsonnew.JsonFormat import sjsonnew.shaded.scalajson.ast.unsafe.JValue import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter } -import sbt.internal.inc.{ Analysis, MixedAnalyzingCompiler } +import sbt.internal.inc.Analysis import sbt.internal.inc.JavaInterfaceUtil._ import sbt.internal.protocol.JsonRpcResponseError import sbt.internal.protocol.codec.JsonRPCProtocol @@ -183,11 +183,19 @@ private[sbt] object Definition { } private[this] val AnalysesKey = "lsp.definition.analyses.key" - private[server] type Analyses = Set[((String, Boolean), Option[Analysis])] + private[server] type Analyses = Set[((String, Boolean, Boolean), Option[Analysis])] - private def storeAnalysis(cacheFile: Path, useBinary: Boolean): Option[Analysis] = - MixedAnalyzingCompiler - .staticCachedStore(cacheFile, !useBinary) + private def storeAnalysis( + cacheFile: Path, + useBinary: Boolean, + useConsistent: Boolean, + ): Option[Analysis] = + AnalysisUtil + .staticCachedStore( + analysisFile = cacheFile, + useTextAnalysis = !useBinary, + useConsistent = useConsistent, + ) .get .toOption .map { _.getAnalysis } @@ -195,13 +203,13 @@ private[sbt] object Definition { private[sbt] def updateCache( cache: Cache[String, Analyses] - )(cacheFile: String, useBinary: Boolean): Any = { - cache.get(AnalysesKey, k => Set(cacheFile -> useBinary -> None)) match { + )(cacheFile: String, useBinary: Boolean, useConsistent: Boolean): Any = { + cache.get(AnalysesKey, k => Set((cacheFile, useBinary, useConsistent) -> None)) match { case null => new AnyRef case set => val newSet = set - .filterNot { case ((file, _), _) => file == cacheFile } - .+(cacheFile -> useBinary -> None) + .filterNot { case ((file, _, _), _) => file == cacheFile } + .+((cacheFile, useBinary, useConsistent) -> None) cache.put(AnalysesKey, newSet) } } @@ -221,10 +229,13 @@ private[sbt] object Definition { def collectAnalysesTask = Def.task { val cacheFile: String = compileIncSetup.value.cacheFile.getAbsolutePath - val useBinary = enableBinaryCompileAnalysis.value val s = state.value - s.log.debug(s"analysis location ${cacheFile -> useBinary}") - updateCache(AnalysesAccess.cache)(cacheFile, useBinary) + s.log.debug(s"analysis location ${cacheFile}") + updateCache(AnalysesAccess.cache)( + cacheFile = cacheFile, + useBinary = enableBinaryCompileAnalysis.value, + useConsistent = enableConsistentCompileAnalysis.value, + ) } private[sbt] def getAnalyses: Future[Seq[Analysis]] = { @@ -243,8 +254,9 @@ private[sbt] object Definition { case (_, None) => false } val addToCache = uninitialized.collect { - case (title @ (file, useBinary), _) if Files.exists(Paths.get(file)) => - (title, storeAnalysis(Paths.get(file), !useBinary)) + case (title @ (file, useBinary, useConsistent), _) + if Files.exists(Paths.get(file)) => + (title, storeAnalysis(Paths.get(file), !useBinary, useConsistent)) } val validCaches = working ++ addToCache if (addToCache.nonEmpty) { diff --git a/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala b/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala index 2bf2878bda..5ea1918128 100644 --- a/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/LanguageServerProtocol.scala @@ -62,6 +62,7 @@ private[sbt] object LanguageServerProtocol { else throw LangServerError(ErrorCodes.InvalidRequest, "invalid token") } else () setInitialized(true) + setInitializeOption(opt) if (!opt.skipAnalysis.getOrElse(false)) appendExec("collectAnalyses", None) jsonRpcRespond(InitializeResult(serverCapabilities), Some(r.id)) diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index 95c0e16c0d..36ace91f14 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -24,6 +24,7 @@ import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference } import sbt.BasicCommandStrings.{ Shutdown, TerminateAction } import sbt.internal.langserver.{ CancelRequestParams, ErrorCodes, LogMessageParams, MessageType } import sbt.internal.protocol.{ + InitializeOption, JsonRpcNotificationMessage, JsonRpcRequestMessage, JsonRpcResponseError, @@ -83,6 +84,10 @@ final class NetworkChannel( private val delimiter: Byte = '\n'.toByte private val out = connection.getOutputStream private var initialized = false + + /** Reference to the client-side custom options + */ + private val initializeOption = new AtomicReference[InitializeOption](null) private val pendingRequests: mutable.Map[String, JsonRpcRequestMessage] = mutable.Map() private[this] val inputBuffer = new LinkedBlockingQueue[Int]() @@ -124,7 +129,7 @@ final class NetworkChannel( self.jsonRpcNotify(method, params) def appendExec(commandLine: String, execId: Option[String]): Boolean = - self.append(Exec(commandLine, execId, Some(CommandSource(name)))) + self.appendExec(commandLine, execId) def appendExec(exec: Exec): Boolean = self.append(exec) @@ -133,6 +138,8 @@ final class NetworkChannel( private[sbt] def authOptions: Set[ServerAuthentication] = self.authOptions private[sbt] def authenticate(token: String): Boolean = self.authenticate(token) private[sbt] def setInitialized(value: Boolean): Unit = self.setInitialized(value) + private[sbt] def setInitializeOption(opts: InitializeOption): Unit = + self.setInitializeOption(opts) private[sbt] def onSettingQuery(execId: Option[String], req: SettingQuery): Unit = self.onSettingQuery(execId, req) private[sbt] def onCompletionRequest(execId: Option[String], cp: CompletionParams): Unit = @@ -141,6 +148,15 @@ final class NetworkChannel( self.onCancellationRequest(execId, crp) } + protected def setInitializeOption(opts: InitializeOption): Unit = initializeOption.set(opts) + + // Returns true if sbtn has declared with canWork: true + protected def clientCanWork: Boolean = + Option(initializeOption.get) match { + case Some(opts) => opts.canWork.getOrElse(false) + case _ => false + } + protected def authenticate(token: String): Boolean = instance.authenticate(token) protected def setInitialized(value: Boolean): Unit = initialized = value @@ -163,7 +179,7 @@ final class NetworkChannel( } } - val thread = new Thread(s"sbt-networkchannel-${connection.getPort}") { + private[this] val thread = new Thread(s"sbt-networkchannel-${connection.getPort}") { private val ct = "Content-Type: " private val x1 = "application/sbt-x1" override def run(): Unit = { @@ -241,7 +257,6 @@ final class NetworkChannel( } } } - thread.start() private[sbt] def isLanguageServerProtocol: Boolean = true @@ -365,46 +380,11 @@ final class NetworkChannel( impl() }, s"sbt-$name-write-thread") writeThread.setDaemon(true) - writeThread.start() def publishBytes(event: Array[Byte], delimit: Boolean): Unit = try pendingWrites.put(event -> delimit) catch { case _: InterruptedException => } - def onCommand(command: CommandMessage): Unit = command match { - case x: InitCommand => onInitCommand(x) - case x: ExecCommand => onExecCommand(x) - case x: SettingQuery => onSettingQuery(None, x) - } - - private def onInitCommand(cmd: InitCommand): Unit = { - if (auth(ServerAuthentication.Token)) { - cmd.token match { - case Some(x) => - authenticate(x) match { - case true => - initialized = true - notifyEvent(ChannelAcceptedEvent(name)) - case _ => sys.error("invalid token") - } - case None => sys.error("init command but without token.") - } - } else { - initialized = true - } - } - - private def onExecCommand(cmd: ExecCommand) = { - if (initialized) { - append( - Exec(cmd.commandLine, cmd.execId orElse Some(Exec.newExecId), Some(CommandSource(name))) - ) - () - } else { - log.warn(s"ignoring command $cmd before initialization") - } - } - protected def onSettingQuery(execId: Option[String], req: SettingQuery) = { if (initialized) { StandardMain.exchange.withState { s => @@ -914,6 +894,9 @@ final class NetworkChannel( } } private[sbt] def isAttached: Boolean = attached.get + + thread.start() + writeThread.start() } object NetworkChannel { diff --git a/main/src/main/scala/sbt/plugins/DependencyTreeSettings.scala b/main/src/main/scala/sbt/plugins/DependencyTreeSettings.scala index 9cf6ee368c..85618a77df 100644 --- a/main/src/main/scala/sbt/plugins/DependencyTreeSettings.scala +++ b/main/src/main/scala/sbt/plugins/DependencyTreeSettings.scala @@ -147,8 +147,9 @@ object DependencyTreeSettings { .asciiTree(GraphTransformations.reverseGraphStartingAt(graph, module), graphWidth) } .mkString("\n") - - streams.value.log.info(output) + synchronized { + streams.value.log.info(output) + } output }, ) ++ @@ -171,7 +172,9 @@ object DependencyTreeSettings { key := { val s = streams.value val str = (key / asString).value - s.log.info(str) + synchronized { + s.log.info(str) + } }, key / toFile := { val (targetFile, force) = targetFileAndForceParser.parsed @@ -198,7 +201,7 @@ object DependencyTreeSettings { rendering.DOT.AngleBrackets, dependencyDotNodeColors.value ) - val link = DagreHTML.createLink(dotGraph, target.value) + val link = DagreHTML.createLink(dotGraph, dependencyBrowseGraphTarget.value) streams.value.log.info(s"HTML graph written to $link") link } @@ -207,7 +210,7 @@ object DependencyTreeSettings { Def.task { val graph = dependencyTreeModuleGraph0.value val renderedTree = TreeView.createJson(graph) - val link = TreeView.createLink(renderedTree, target.value) + val link = TreeView.createLink(renderedTree, dependencyBrowseTreeTarget.value) streams.value.log.info(s"HTML tree written to $link") link } @@ -245,7 +248,10 @@ object DependencyTreeSettings { Def.task { val uri = uriKey.value streams.value.log.info(s"Opening ${uri} in browser...") - java.awt.Desktop.getDesktop.browse(uri) + val desktop = java.awt.Desktop.getDesktop + desktop.synchronized { + desktop.browse(uri) + } uri } diff --git a/main/src/main/scala/sbt/plugins/Giter8TemplatePlugin.scala b/main/src/main/scala/sbt/plugins/Giter8TemplatePlugin.scala index 51e60575b5..4aedac6aa8 100644 --- a/main/src/main/scala/sbt/plugins/Giter8TemplatePlugin.scala +++ b/main/src/main/scala/sbt/plugins/Giter8TemplatePlugin.scala @@ -27,7 +27,7 @@ object Giter8TemplatePlugin extends AutoPlugin { ModuleID( "org.scala-sbt.sbt-giter8-resolver", "sbt-giter8-resolver", - "0.16.2" + "0.17.0" ) cross CrossVersion.binary, "sbtgiter8resolver.Giter8TemplateResolver" ) diff --git a/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala b/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala index 5d603bc113..46a0183978 100644 --- a/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala +++ b/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala @@ -27,7 +27,7 @@ object SemanticdbPlugin extends AutoPlugin { semanticdbEnabled := SysProp.semanticdb, semanticdbIncludeInJar := false, semanticdbOptions := List(), - semanticdbVersion := "4.7.8" + semanticdbVersion := "4.9.9" ) override lazy val projectSettings: Seq[Def.Setting[_]] = Seq( diff --git a/main/src/test/scala/PluginCommandTest.scala b/main/src/test/scala/PluginCommandTest.scala index b0fa5662bb..d49950a6b1 100644 --- a/main/src/test/scala/PluginCommandTest.scala +++ b/main/src/test/scala/PluginCommandTest.scala @@ -115,7 +115,7 @@ object FakeState { Nil ) - val pluginData = PluginData(Nil, Nil, None, None, Nil, Nil, Nil, Nil, Nil, None) + val pluginData = PluginData(Nil, Nil, None, None, Nil, Nil, Nil, Nil, Nil, Nil, None, None) val builds: DetectedModules[BuildDef] = new DetectedModules[BuildDef](Nil) val detectedAutoPlugins: Seq[DetectedAutoPlugin] = diff --git a/main/src/test/scala/PluginsTest.scala b/main/src/test/scala/PluginsTest.scala index e7bdcf8262..f0fc850e1e 100644 --- a/main/src/test/scala/PluginsTest.scala +++ b/main/src/test/scala/PluginsTest.scala @@ -48,7 +48,7 @@ object PluginsTest extends verify.BasicTestSuite { assertEquals( s"""Contradiction in enabled plugins: - requested: sbt.AI$$S - - enabled: sbt.AI$$S, sbt.AI$$Q, sbt.AI$$R, sbt.AI$$B, sbt.AI$$A + - enabled: sbt.AI$$A, sbt.AI$$B, sbt.AI$$Q, sbt.AI$$R, sbt.AI$$S - conflict: sbt.AI$$R is enabled by sbt.AI$$Q; excluded by sbt.AI$$S""", e.message ) @@ -63,8 +63,8 @@ object PluginsTest extends verify.BasicTestSuite { assertEquals( s"""Contradiction in enabled plugins: - requested: sbt.AI$$T && sbt.AI$$U - - enabled: sbt.AI$$U, sbt.AI$$T, sbt.AI$$A, sbt.AI$$Q, sbt.AI$$R, sbt.AI$$B - - conflict: sbt.AI$$Q is enabled by sbt.AI$$A && sbt.AI$$B; required by sbt.AI$$T, sbt.AI$$R; excluded by sbt.AI$$U + - enabled: sbt.AI$$A, sbt.AI$$B, sbt.AI$$Q, sbt.AI$$R, sbt.AI$$T, sbt.AI$$U + - conflict: sbt.AI$$Q is enabled by sbt.AI$$A && sbt.AI$$B; required by sbt.AI$$R, sbt.AI$$T; excluded by sbt.AI$$U - conflict: sbt.AI$$R is enabled by sbt.AI$$Q; excluded by sbt.AI$$T""", e.message ) diff --git a/main/src/test/scala/TagsTest.scala b/main/src/test/scala/TagsTest.scala index 270ca646ee..e75dd7e183 100644 --- a/main/src/test/scala/TagsTest.scala +++ b/main/src/test/scala/TagsTest.scala @@ -18,7 +18,7 @@ object TagsTest extends Properties("Tags") { def tagMap: Gen[TagMap] = for (ts <- listOf(tagAndFrequency)) yield ts.toMap def tagAndFrequency: Gen[(Tag, Int)] = - for (t <- tag; count <- Arbitrary.arbitrary[Int]) yield (t, count) + for (t <- tag; count <- Gen.choose(0, Int.MaxValue)) yield (t, count) def tag: Gen[Tag] = for (s <- Gen.alphaStr if !s.isEmpty) yield Tag(s) def size: Gen[Size] = for (i <- Arbitrary.arbitrary[Int] if i != Int.MinValue) yield Size(math.abs(i)) diff --git a/main/src/test/scala/sbt/internal/AggregationSpec.scala b/main/src/test/scala/sbt/internal/AggregationSpec.scala index 9acf09dc39..b6cbdb681e 100644 --- a/main/src/test/scala/sbt/internal/AggregationSpec.scala +++ b/main/src/test/scala/sbt/internal/AggregationSpec.scala @@ -12,14 +12,18 @@ object AggregationSpec extends verify.BasicTestSuite { val timing = Aggregation.timing(Aggregation.defaultFormat, 0, _: Long) test("timing should format total time properly") { - assert(timing(101).startsWith("Total time: 0 s,")) - assert(timing(1000).startsWith("Total time: 1 s,")) - assert(timing(3000).startsWith("Total time: 3 s,")) - assert(timing(30399).startsWith("Total time: 30 s,")) - assert(timing(60399).startsWith("Total time: 60 s,")) - assert(timing(60699).startsWith("Total time: 61 s (01:01),")) - assert(timing(303099).startsWith("Total time: 303 s (05:03),")) - assert(timing(6003099).startsWith("Total time: 6003 s (01:40:03),")) - assert(timing(96003099).startsWith("Total time: 96003 s (26:40:03),")) + assert(timing(101).startsWith("Total time: 0 s")) + assert(timing(1000).startsWith("Total time: 1 s")) + assert(timing(3000).startsWith("Total time: 3 s")) + assert(timing(30399).startsWith("Total time: 30 s")) + assert(timing(60399).startsWith("Total time: 60 s")) + assert(timing(60699).startsWith("Total time: 61 s (0:01:01.0)")) + assert(timing(303099).startsWith("Total time: 303 s (0:05:03.0)")) + assert(timing(6003099).startsWith("Total time: 6003 s (01:40:03.0)")) + assert(timing(96003099).startsWith("Total time: 96003 s (26:40:03.0)")) + } + + test("timing should not emit special space characters") { + assert(!timing(96003099).contains("\u202F")) } } diff --git a/main/src/test/scala/sbt/internal/server/DefinitionTest.scala b/main/src/test/scala/sbt/internal/server/DefinitionTest.scala index 0d28294c16..30cde95abb 100644 --- a/main/src/test/scala/sbt/internal/server/DefinitionTest.scala +++ b/main/src/test/scala/sbt/internal/server/DefinitionTest.scala @@ -201,12 +201,13 @@ object DefinitionTest extends verify.BasicTestSuite { val cache = Caffeine.newBuilder().build[String, Definition.Analyses]() val cacheFile = "Test.scala" val useBinary = true + val useConsistent = true - Definition.updateCache(cache)(cacheFile, useBinary) + Definition.updateCache(cache)(cacheFile, useBinary, useConsistent) val actual = Definition.AnalysesAccess.getFrom(cache) - assert(actual.get.contains(("Test.scala" -> true -> None))) + assert(actual.get.contains((("Test.scala", true, true) -> None))) } test("it should replace cache data in cache") { @@ -214,13 +215,14 @@ object DefinitionTest extends verify.BasicTestSuite { val cacheFile = "Test.scala" val useBinary = true val falseUseBinary = false + val useConsistent = true - Definition.updateCache(cache)(cacheFile, falseUseBinary) - Definition.updateCache(cache)(cacheFile, useBinary) + Definition.updateCache(cache)(cacheFile, falseUseBinary, useConsistent) + Definition.updateCache(cache)(cacheFile, useBinary, useConsistent) val actual = Definition.AnalysesAccess.getFrom(cache) - assert(actual.get.contains(("Test.scala" -> true -> None))) + assert(actual.get.contains((("Test.scala", true, true) -> None))) } test("it should cache more data in cache") { @@ -229,15 +231,16 @@ object DefinitionTest extends verify.BasicTestSuite { val useBinary = true val otherCacheFile = "OtherTest.scala" val otherUseBinary = false + val useConsistent = true - Definition.updateCache(cache)(otherCacheFile, otherUseBinary) - Definition.updateCache(cache)(cacheFile, useBinary) + Definition.updateCache(cache)(otherCacheFile, otherUseBinary, useConsistent) + Definition.updateCache(cache)(cacheFile, useBinary, useConsistent) val actual = Definition.AnalysesAccess.getFrom(cache) assert( - actual.get.contains("Test.scala" -> true -> Option.empty[Analysis]) && - actual.get.contains("OtherTest.scala" -> false -> Option.empty[Analysis]) + actual.get.contains(("Test.scala", true, true) -> Option.empty[Analysis]) && + actual.get.contains(("OtherTest.scala", false, true) -> Option.empty[Analysis]) ) } } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index a9cfdfbcab..6564f942b1 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -4,29 +4,29 @@ import sbt.contraband.ContrabandPlugin.autoImport._ object Dependencies { // WARNING: Please Scala update versions in PluginCross.scala too - val scala212 = "2.12.18" - val scala213 = "2.13.12" + val scala212 = "2.12.20" + val scala213 = "2.13.16" 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.9.1") + private val ioVersion = nightlyVersion.getOrElse("1.10.5") private val lmVersion = - sys.props.get("sbt.build.lm.version").orElse(nightlyVersion).getOrElse("1.9.3") - val zincVersion = nightlyVersion.getOrElse("1.9.5") + sys.props.get("sbt.build.lm.version").orElse(nightlyVersion).getOrElse("1.11.2") + val zincVersion = nightlyVersion.getOrElse("1.10.8") private val sbtIO = "org.scala-sbt" %% "io" % ioVersion private val libraryManagementCore = "org.scala-sbt" %% "librarymanagement-core" % lmVersion private val libraryManagementIvy = "org.scala-sbt" %% "librarymanagement-ivy" % lmVersion - val launcherVersion = "1.4.2" + val launcherVersion = "1.4.4" val launcherInterface = "org.scala-sbt" % "launcher-interface" % launcherVersion val rawLauncher = "org.scala-sbt" % "launcher" % launcherVersion val testInterface = "org.scala-sbt" % "test-interface" % "1.0" - val ipcSocket = "org.scala-sbt.ipcsocket" % "ipcsocket" % "1.6.2" + val ipcSocket = "org.scala-sbt.ipcsocket" % "ipcsocket" % "1.6.3" private val compilerInterface = "org.scala-sbt" % "compiler-interface" % zincVersion private val compilerClasspath = "org.scala-sbt" %% "zinc-classpath" % zincVersion @@ -77,23 +77,20 @@ object Dependencies { def addSbtZincCompile = addSbtModule(sbtZincPath, "zincCompile", zincCompile) def addSbtZincCompileCore = addSbtModule(sbtZincPath, "zincCompileCore", zincCompileCore) - val lmCoursierShaded = "io.get-coursier" %% "lm-coursier-shaded" % "2.1.1" + val lmCoursierShaded = "io.get-coursier" %% "lm-coursier-shaded" % "2.1.10" def sjsonNew(n: String) = - Def.setting("com.eed3si9n" %% n % "0.9.1") // contrabandSjsonNewVersion.value + Def.setting("com.eed3si9n" %% n % "0.10.1") // contrabandSjsonNewVersion.value val sjsonNewScalaJson = sjsonNew("sjson-new-scalajson") val sjsonNewMurmurhash = sjsonNew("sjson-new-murmurhash") - // JLine 3 version must be coordinated together with JAnsi version - // and the JLine 2 fork version, which uses the same JAnsi - val jline = "org.scala-sbt.jline" % "jline" % "2.14.7-sbt-a1b0ffbb8f64bb820f4f84a0c07a0c0964507493" - val jline3Version = "3.19.0" + val jline = "org.scala-sbt.jline" % "jline" % "2.14.7-sbt-9a88bc413e2b34a4580c001c654d1a7f4f65bf18" + val jline3Version = "3.27.1" val jline3Terminal = "org.jline" % "jline-terminal" % jline3Version - val jline3Jansi = "org.jline" % "jline-terminal-jansi" % jline3Version - val jline3JNA = "org.jline" % "jline-terminal-jna" % jline3Version + val jline3JNI = "org.jline" % "jline-terminal-jni" % jline3Version + val jline3Native = "org.jline" % "jline-native" % jline3Version 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.2.10" val scalacheck = "org.scalacheck" %% "scalacheck" % "1.15.4" val junit = "junit" % "junit" % "4.13.1" @@ -102,9 +99,9 @@ object Dependencies { val scalaXml = Def.setting( if (scalaBinaryVersion.value == "3") { - "org.scala-lang.modules" %% "scala-xml" % "2.1.0" + "org.scala-lang.modules" %% "scala-xml" % "2.2.0" } else { - "org.scala-lang.modules" %% "scala-xml" % "2.1.0" + "org.scala-lang.modules" %% "scala-xml" % "2.2.0" } ) val scalaParsers = Def.setting( @@ -134,5 +131,6 @@ object Dependencies { 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) + val kindProjector = ("org.typelevel" % "kind-projector" % "0.13.3").cross(CrossVersion.full) + val gigahorseOkHttp = "com.eed3si9n" %% "gigahorse-apache-http" % "0.9.3" } diff --git a/project/Util.scala b/project/Util.scala index cf8b75cc85..df8b3a06a2 100644 --- a/project/Util.scala +++ b/project/Util.scala @@ -2,8 +2,6 @@ import scala.util.control.NonFatal import sbt._ import Keys._ -import sbt.internal.inc.Analysis - object Util { val version2_13 = settingKey[String]("version number") val ExclusiveTest: Tags.Tag = Tags.Tag("exclusive-test") @@ -75,30 +73,6 @@ object Util { run.run(mainClass, cp.files, args, s.log).failed foreach (e => sys error e.getMessage) (out ** "*.java").get } - def lastCompilationTime(analysis: Analysis): Long = { - val lastCompilation = analysis.compilations.allCompilations.lastOption - lastCompilation.map(_.getStartTime) getOrElse 0L - } - def generateVersionFile( - version: String, - dir: File, - s: TaskStreams, - analysis: Analysis - ): Seq[File] = { - import java.util.{ Date, TimeZone } - val formatter = new java.text.SimpleDateFormat("yyyyMMdd'T'HHmmss") - formatter.setTimeZone(TimeZone.getTimeZone("GMT")) - val timestamp = formatter.format(new Date) - val content = versionLine(version) + "\ntimestamp=" + timestamp - val f = dir / "xsbt.version.properties" - // TODO: replace lastModified() with sbt.io.IO.getModifiedTimeOrZero(), once the build - // has been upgraded to a version of sbt that includes that call. - if (!f.exists || f.lastModified < lastCompilationTime(analysis) || !containsVersion(f, version)) { - s.log.info("Writing version information to " + f + " :\n" + content) - IO.write(f, content) - } - f :: Nil - } def versionLine(version: String): String = "version=" + version def containsVersion(propFile: File, version: String): Boolean = IO.read(propFile).contains(versionLine(version)) diff --git a/project/build.properties b/project/build.properties index 3c0b78a7c6..6520f6981d 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.9.1 +sbt.version=1.11.0 diff --git a/project/plugins.sbt b/project/plugins.sbt index 28508259b4..6e23461996 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -2,10 +2,10 @@ ThisBuild / useCoursier := false scalacOptions ++= Seq("-feature", "-language:postfixOps", "-Ywarn-unused:_,-imports") -addSbtPlugin("com.dwijnand" % "sbt-dynver" % "4.0.0") +addSbtPlugin("com.github.sbt" % "sbt-dynver" % "5.0.1") 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("org.scala-sbt" % "sbt-contraband" % "0.5.3") addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.6.5") addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.2.0") addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.8.1") diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/CompileReport.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/CompileReport.scala index 5eb2ac41a1..1144cd8a3a 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/CompileReport.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/CompileReport.scala @@ -10,28 +10,30 @@ package sbt.internal.bsp * @param errors The total number of reported errors compiling this target. * @param warnings The total number of reported warnings compiling the target. * @param time The total number of milliseconds it took to compile the target. + * @param noOp The compilation was a noOp compilation. */ final class CompileReport private ( val target: sbt.internal.bsp.BuildTargetIdentifier, val originId: Option[String], val errors: Int, val warnings: Int, - val time: Option[Int]) extends Serializable { + val time: Option[Int], + val noOp: Option[Boolean]) extends Serializable { override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { - case x: CompileReport => (this.target == x.target) && (this.originId == x.originId) && (this.errors == x.errors) && (this.warnings == x.warnings) && (this.time == x.time) + case x: CompileReport => (this.target == x.target) && (this.originId == x.originId) && (this.errors == x.errors) && (this.warnings == x.warnings) && (this.time == x.time) && (this.noOp == x.noOp) case _ => false }) override def hashCode: Int = { - 37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.CompileReport".##) + target.##) + originId.##) + errors.##) + warnings.##) + time.##) + 37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.CompileReport".##) + target.##) + originId.##) + errors.##) + warnings.##) + time.##) + noOp.##) } override def toString: String = { - "CompileReport(" + target + ", " + originId + ", " + errors + ", " + warnings + ", " + time + ")" + "CompileReport(" + target + ", " + originId + ", " + errors + ", " + warnings + ", " + time + ", " + noOp + ")" } - private[this] def copy(target: sbt.internal.bsp.BuildTargetIdentifier = target, originId: Option[String] = originId, errors: Int = errors, warnings: Int = warnings, time: Option[Int] = time): CompileReport = { - new CompileReport(target, originId, errors, warnings, time) + private[this] def copy(target: sbt.internal.bsp.BuildTargetIdentifier = target, originId: Option[String] = originId, errors: Int = errors, warnings: Int = warnings, time: Option[Int] = time, noOp: Option[Boolean] = noOp): CompileReport = { + new CompileReport(target, originId, errors, warnings, time, noOp) } def withTarget(target: sbt.internal.bsp.BuildTargetIdentifier): CompileReport = { copy(target = target) @@ -54,9 +56,15 @@ final class CompileReport private ( def withTime(time: Int): CompileReport = { copy(time = Option(time)) } + def withNoOp(noOp: Option[Boolean]): CompileReport = { + copy(noOp = noOp) + } + def withNoOp(noOp: Boolean): CompileReport = { + copy(noOp = Option(noOp)) + } } object CompileReport { - def apply(target: sbt.internal.bsp.BuildTargetIdentifier, originId: Option[String], errors: Int, warnings: Int, time: Option[Int]): CompileReport = new CompileReport(target, originId, errors, warnings, time) - def apply(target: sbt.internal.bsp.BuildTargetIdentifier, originId: String, errors: Int, warnings: Int, time: Int): CompileReport = new CompileReport(target, Option(originId), errors, warnings, Option(time)) + def apply(target: sbt.internal.bsp.BuildTargetIdentifier, originId: Option[String], errors: Int, warnings: Int, time: Option[Int], noOp: Option[Boolean]): CompileReport = new CompileReport(target, originId, errors, warnings, time, noOp) + def apply(target: sbt.internal.bsp.BuildTargetIdentifier, originId: String, errors: Int, warnings: Int, time: Int, noOp: Boolean): CompileReport = new CompileReport(target, Option(originId), errors, warnings, Option(time), Option(noOp)) } diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/JavacOptionsItem.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/JavacOptionsItem.scala new file mode 100644 index 0000000000..30157dfb53 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/JavacOptionsItem.scala @@ -0,0 +1,57 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp +/** + * @param options Additional arguments to the compiler. + For example, -deprecation. + * @param classpath The dependency classpath for this target, must be + identical to what is passed as arguments to + the -classpath flag in the command line interface + of scalac. + * @param classDirectory The output directory for classfiles produced by this target + */ +final class JavacOptionsItem private ( + val target: sbt.internal.bsp.BuildTargetIdentifier, + val options: Vector[String], + val classpath: Vector[java.net.URI], + val classDirectory: Option[java.net.URI]) extends Serializable { + + + + override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { + case x: JavacOptionsItem => (this.target == x.target) && (this.options == x.options) && (this.classpath == x.classpath) && (this.classDirectory == x.classDirectory) + case _ => false + }) + override def hashCode: Int = { + 37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.JavacOptionsItem".##) + target.##) + options.##) + classpath.##) + classDirectory.##) + } + override def toString: String = { + "JavacOptionsItem(" + target + ", " + options + ", " + classpath + ", " + classDirectory + ")" + } + private[this] def copy(target: sbt.internal.bsp.BuildTargetIdentifier = target, options: Vector[String] = options, classpath: Vector[java.net.URI] = classpath, classDirectory: Option[java.net.URI] = classDirectory): JavacOptionsItem = { + new JavacOptionsItem(target, options, classpath, classDirectory) + } + def withTarget(target: sbt.internal.bsp.BuildTargetIdentifier): JavacOptionsItem = { + copy(target = target) + } + def withOptions(options: Vector[String]): JavacOptionsItem = { + copy(options = options) + } + def withClasspath(classpath: Vector[java.net.URI]): JavacOptionsItem = { + copy(classpath = classpath) + } + def withClassDirectory(classDirectory: Option[java.net.URI]): JavacOptionsItem = { + copy(classDirectory = classDirectory) + } + def withClassDirectory(classDirectory: java.net.URI): JavacOptionsItem = { + copy(classDirectory = Option(classDirectory)) + } +} +object JavacOptionsItem { + + def apply(target: sbt.internal.bsp.BuildTargetIdentifier, options: Vector[String], classpath: Vector[java.net.URI], classDirectory: Option[java.net.URI]): JavacOptionsItem = new JavacOptionsItem(target, options, classpath, classDirectory) + def apply(target: sbt.internal.bsp.BuildTargetIdentifier, options: Vector[String], classpath: Vector[java.net.URI], classDirectory: java.net.URI): JavacOptionsItem = new JavacOptionsItem(target, options, classpath, Option(classDirectory)) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/JavacOptionsParams.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/JavacOptionsParams.scala new file mode 100644 index 0000000000..4cc1ebbe24 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/JavacOptionsParams.scala @@ -0,0 +1,37 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp +/** + * Javac options + * The build target javac options request is sent from the client to the server + * to query for the list of compiler options necessary to compile in a given list of targets. + */ +final class JavacOptionsParams private ( + val targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]) extends Serializable { + + + + override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { + case x: JavacOptionsParams => (this.targets == x.targets) + case _ => false + }) + override def hashCode: Int = { + 37 * (37 * (17 + "sbt.internal.bsp.JavacOptionsParams".##) + targets.##) + } + override def toString: String = { + "JavacOptionsParams(" + targets + ")" + } + private[this] def copy(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier] = targets): JavacOptionsParams = { + new JavacOptionsParams(targets) + } + def withTargets(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]): JavacOptionsParams = { + copy(targets = targets) + } +} +object JavacOptionsParams { + + def apply(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]): JavacOptionsParams = new JavacOptionsParams(targets) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/JavacOptionsResult.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/JavacOptionsResult.scala new file mode 100644 index 0000000000..fb89063b49 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/JavacOptionsResult.scala @@ -0,0 +1,32 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp +final class JavacOptionsResult private ( + val items: Vector[sbt.internal.bsp.JavacOptionsItem]) extends Serializable { + + + + override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { + case x: JavacOptionsResult => (this.items == x.items) + case _ => false + }) + override def hashCode: Int = { + 37 * (37 * (17 + "sbt.internal.bsp.JavacOptionsResult".##) + items.##) + } + override def toString: String = { + "JavacOptionsResult(" + items + ")" + } + private[this] def copy(items: Vector[sbt.internal.bsp.JavacOptionsItem] = items): JavacOptionsResult = { + new JavacOptionsResult(items) + } + def withItems(items: Vector[sbt.internal.bsp.JavacOptionsItem]): JavacOptionsResult = { + copy(items = items) + } +} +object JavacOptionsResult { + + def apply(items: Vector[sbt.internal.bsp.JavacOptionsItem]): JavacOptionsResult = new JavacOptionsResult(items) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/JvmBuildTarget.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/JvmBuildTarget.scala new file mode 100644 index 0000000000..488b8b8e68 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/JvmBuildTarget.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 +/** + * Contains jvm-specific metadata, specifically JDK reference + * @param javaHome Uri representing absolute path to jdk + * @param javaVersion The java version this target is supposed to use (can be set using javac `-target` flag) + */ +final class JvmBuildTarget private ( + val javaHome: Option[java.net.URI], + val javaVersion: Option[String]) extends Serializable { + + + + override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { + case x: JvmBuildTarget => (this.javaHome == x.javaHome) && (this.javaVersion == x.javaVersion) + case _ => false + }) + override def hashCode: Int = { + 37 * (37 * (37 * (17 + "sbt.internal.bsp.JvmBuildTarget".##) + javaHome.##) + javaVersion.##) + } + override def toString: String = { + "JvmBuildTarget(" + javaHome + ", " + javaVersion + ")" + } + private[this] def copy(javaHome: Option[java.net.URI] = javaHome, javaVersion: Option[String] = javaVersion): JvmBuildTarget = { + new JvmBuildTarget(javaHome, javaVersion) + } + def withJavaHome(javaHome: Option[java.net.URI]): JvmBuildTarget = { + copy(javaHome = javaHome) + } + def withJavaHome(javaHome: java.net.URI): JvmBuildTarget = { + copy(javaHome = Option(javaHome)) + } + def withJavaVersion(javaVersion: Option[String]): JvmBuildTarget = { + copy(javaVersion = javaVersion) + } + def withJavaVersion(javaVersion: String): JvmBuildTarget = { + copy(javaVersion = Option(javaVersion)) + } +} +object JvmBuildTarget { + + def apply(javaHome: Option[java.net.URI], javaVersion: Option[String]): JvmBuildTarget = new JvmBuildTarget(javaHome, javaVersion) + def apply(javaHome: java.net.URI, javaVersion: String): JvmBuildTarget = new JvmBuildTarget(Option(javaHome), Option(javaVersion)) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/ScalaBuildTarget.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/ScalaBuildTarget.scala index 32e5885a91..e686d84dd5 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/ScalaBuildTarget.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/ScalaBuildTarget.scala @@ -14,28 +14,30 @@ package sbt.internal.bsp For example, 2.12 if scalaVersion is 2.12.4. * @param platform The target platform for this target * @param jars A sequence of Scala jars such as scala-library, scala-compiler and scala-reflect. + * @param jvmBuildTarget The jvm build target describing jdk to be used */ final class ScalaBuildTarget private ( val scalaOrganization: String, val scalaVersion: String, val scalaBinaryVersion: String, val platform: Int, - val jars: Vector[String]) extends Serializable { + val jars: Vector[String], + val jvmBuildTarget: Option[sbt.internal.bsp.JvmBuildTarget]) extends Serializable { override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { - case x: ScalaBuildTarget => (this.scalaOrganization == x.scalaOrganization) && (this.scalaVersion == x.scalaVersion) && (this.scalaBinaryVersion == x.scalaBinaryVersion) && (this.platform == x.platform) && (this.jars == x.jars) + case x: ScalaBuildTarget => (this.scalaOrganization == x.scalaOrganization) && (this.scalaVersion == x.scalaVersion) && (this.scalaBinaryVersion == x.scalaBinaryVersion) && (this.platform == x.platform) && (this.jars == x.jars) && (this.jvmBuildTarget == x.jvmBuildTarget) case _ => false }) override def hashCode: Int = { - 37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.ScalaBuildTarget".##) + scalaOrganization.##) + scalaVersion.##) + scalaBinaryVersion.##) + platform.##) + jars.##) + 37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.ScalaBuildTarget".##) + scalaOrganization.##) + scalaVersion.##) + scalaBinaryVersion.##) + platform.##) + jars.##) + jvmBuildTarget.##) } override def toString: String = { - "ScalaBuildTarget(" + scalaOrganization + ", " + scalaVersion + ", " + scalaBinaryVersion + ", " + platform + ", " + jars + ")" + "ScalaBuildTarget(" + scalaOrganization + ", " + scalaVersion + ", " + scalaBinaryVersion + ", " + platform + ", " + jars + ", " + jvmBuildTarget + ")" } - private[this] def copy(scalaOrganization: String = scalaOrganization, scalaVersion: String = scalaVersion, scalaBinaryVersion: String = scalaBinaryVersion, platform: Int = platform, jars: Vector[String] = jars): ScalaBuildTarget = { - new ScalaBuildTarget(scalaOrganization, scalaVersion, scalaBinaryVersion, platform, jars) + private[this] def copy(scalaOrganization: String = scalaOrganization, scalaVersion: String = scalaVersion, scalaBinaryVersion: String = scalaBinaryVersion, platform: Int = platform, jars: Vector[String] = jars, jvmBuildTarget: Option[sbt.internal.bsp.JvmBuildTarget] = jvmBuildTarget): ScalaBuildTarget = { + new ScalaBuildTarget(scalaOrganization, scalaVersion, scalaBinaryVersion, platform, jars, jvmBuildTarget) } def withScalaOrganization(scalaOrganization: String): ScalaBuildTarget = { copy(scalaOrganization = scalaOrganization) @@ -52,8 +54,15 @@ final class ScalaBuildTarget private ( def withJars(jars: Vector[String]): ScalaBuildTarget = { copy(jars = jars) } + def withJvmBuildTarget(jvmBuildTarget: Option[sbt.internal.bsp.JvmBuildTarget]): ScalaBuildTarget = { + copy(jvmBuildTarget = jvmBuildTarget) + } + def withJvmBuildTarget(jvmBuildTarget: sbt.internal.bsp.JvmBuildTarget): ScalaBuildTarget = { + copy(jvmBuildTarget = Option(jvmBuildTarget)) + } } object ScalaBuildTarget { - def apply(scalaOrganization: String, scalaVersion: String, scalaBinaryVersion: String, platform: Int, jars: Vector[String]): ScalaBuildTarget = new ScalaBuildTarget(scalaOrganization, scalaVersion, scalaBinaryVersion, platform, jars) + def apply(scalaOrganization: String, scalaVersion: String, scalaBinaryVersion: String, platform: Int, jars: Vector[String], jvmBuildTarget: Option[sbt.internal.bsp.JvmBuildTarget]): ScalaBuildTarget = new ScalaBuildTarget(scalaOrganization, scalaVersion, scalaBinaryVersion, platform, jars, jvmBuildTarget) + def apply(scalaOrganization: String, scalaVersion: String, scalaBinaryVersion: String, platform: Int, jars: Vector[String], jvmBuildTarget: sbt.internal.bsp.JvmBuildTarget): ScalaBuildTarget = new ScalaBuildTarget(scalaOrganization, scalaVersion, scalaBinaryVersion, platform, jars, Option(jvmBuildTarget)) } 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 9c8684ba3c..6aba27ae75 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 @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait BuildServerCapabilitiesFormats { self: sbt.internal.bsp.codec.CompileProviderFormats with sbt.internal.bsp.codec.TestProviderFormats with sbt.internal.bsp.codec.RunProviderFormats with sjsonnew.BasicJsonProtocol => +trait BuildServerCapabilitiesFormats { self: sbt.internal.bsp.codec.CompileProviderFormats with sjsonnew.BasicJsonProtocol with sbt.internal.bsp.codec.TestProviderFormats with sbt.internal.bsp.codec.RunProviderFormats => implicit lazy val BuildServerCapabilitiesFormat: JsonFormat[sbt.internal.bsp.BuildServerCapabilities] = new JsonFormat[sbt.internal.bsp.BuildServerCapabilities] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.BuildServerCapabilities = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildTargetFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildTargetFormats.scala index 8515f37eec..76eff0d93a 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildTargetFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/BuildTargetFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait BuildTargetFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sbt.internal.bsp.codec.BuildTargetCapabilitiesFormats with sbt.internal.util.codec.JValueFormats with sjsonnew.BasicJsonProtocol => +trait BuildTargetFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol with sbt.internal.bsp.codec.BuildTargetCapabilitiesFormats with sbt.internal.util.codec.JValueFormats => implicit lazy val BuildTargetFormat: JsonFormat[sbt.internal.bsp.BuildTarget] = new JsonFormat[sbt.internal.bsp.BuildTarget] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.BuildTarget = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/CompileReportFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/CompileReportFormats.scala index 953258a863..dde3a35985 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/CompileReportFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/CompileReportFormats.scala @@ -16,8 +16,9 @@ implicit lazy val CompileReportFormat: JsonFormat[sbt.internal.bsp.CompileReport val errors = unbuilder.readField[Int]("errors") val warnings = unbuilder.readField[Int]("warnings") val time = unbuilder.readField[Option[Int]]("time") + val noOp = unbuilder.readField[Option[Boolean]]("noOp") unbuilder.endObject() - sbt.internal.bsp.CompileReport(target, originId, errors, warnings, time) + sbt.internal.bsp.CompileReport(target, originId, errors, warnings, time, noOp) case None => deserializationError("Expected JsObject but found None") } @@ -29,6 +30,7 @@ implicit lazy val CompileReportFormat: JsonFormat[sbt.internal.bsp.CompileReport builder.addField("errors", obj.errors) builder.addField("warnings", obj.warnings) builder.addField("time", obj.time) + builder.addField("noOp", obj.noOp) builder.endObject() } } diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/DebugSessionParamsFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/DebugSessionParamsFormats.scala index 8d4ccab6ef..2ea073b09d 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/DebugSessionParamsFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/DebugSessionParamsFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait DebugSessionParamsFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sbt.internal.util.codec.JValueFormats with sjsonnew.BasicJsonProtocol => +trait DebugSessionParamsFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol with sbt.internal.util.codec.JValueFormats => implicit lazy val DebugSessionParamsFormat: JsonFormat[sbt.internal.bsp.DebugSessionParams] = new JsonFormat[sbt.internal.bsp.DebugSessionParams] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.DebugSessionParams = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/DependencySourcesResultFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/DependencySourcesResultFormats.scala index 974150a782..0901c083f4 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/DependencySourcesResultFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/DependencySourcesResultFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait DependencySourcesResultFormats { self: sbt.internal.bsp.codec.DependencySourcesItemFormats with sjsonnew.BasicJsonProtocol => +trait DependencySourcesResultFormats { self: sbt.internal.bsp.codec.DependencySourcesItemFormats with sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol => implicit lazy val DependencySourcesResultFormat: JsonFormat[sbt.internal.bsp.DependencySourcesResult] = new JsonFormat[sbt.internal.bsp.DependencySourcesResult] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.DependencySourcesResult = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/DiagnosticFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/DiagnosticFormats.scala index 0e0291d9f9..6a8fb8bffb 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/DiagnosticFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/DiagnosticFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait DiagnosticFormats { self: sbt.internal.bsp.codec.RangeFormats with sbt.internal.bsp.codec.DiagnosticRelatedInformationFormats with sbt.internal.bsp.codec.ScalaDiagnosticFormats with sjsonnew.BasicJsonProtocol => +trait DiagnosticFormats { self: sbt.internal.bsp.codec.RangeFormats with sbt.internal.bsp.codec.PositionFormats with sjsonnew.BasicJsonProtocol with sbt.internal.bsp.codec.DiagnosticRelatedInformationFormats with sbt.internal.bsp.codec.LocationFormats with sbt.internal.bsp.codec.ScalaDiagnosticFormats with sbt.internal.bsp.codec.ScalaActionFormats with sbt.internal.bsp.codec.ScalaWorkspaceEditFormats with sbt.internal.bsp.codec.ScalaTextEditFormats => implicit lazy val DiagnosticFormat: JsonFormat[sbt.internal.bsp.Diagnostic] = new JsonFormat[sbt.internal.bsp.Diagnostic] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.Diagnostic = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/DiagnosticRelatedInformationFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/DiagnosticRelatedInformationFormats.scala index 7886ec851e..b4271dbcda 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/DiagnosticRelatedInformationFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/DiagnosticRelatedInformationFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait DiagnosticRelatedInformationFormats { self: sbt.internal.bsp.codec.LocationFormats with sjsonnew.BasicJsonProtocol => +trait DiagnosticRelatedInformationFormats { self: sbt.internal.bsp.codec.LocationFormats with sbt.internal.bsp.codec.RangeFormats with sbt.internal.bsp.codec.PositionFormats with sjsonnew.BasicJsonProtocol => implicit lazy val DiagnosticRelatedInformationFormat: JsonFormat[sbt.internal.bsp.DiagnosticRelatedInformation] = new JsonFormat[sbt.internal.bsp.DiagnosticRelatedInformation] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.DiagnosticRelatedInformation = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/InitializeBuildParamsFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/InitializeBuildParamsFormats.scala index 943feb6643..31e0eeceae 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/InitializeBuildParamsFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/InitializeBuildParamsFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait InitializeBuildParamsFormats { self: sbt.internal.bsp.codec.BuildClientCapabilitiesFormats with sbt.internal.util.codec.JValueFormats with sjsonnew.BasicJsonProtocol => +trait InitializeBuildParamsFormats { self: sbt.internal.bsp.codec.BuildClientCapabilitiesFormats with sjsonnew.BasicJsonProtocol with sbt.internal.util.codec.JValueFormats => implicit lazy val InitializeBuildParamsFormat: JsonFormat[sbt.internal.bsp.InitializeBuildParams] = new JsonFormat[sbt.internal.bsp.InitializeBuildParams] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.InitializeBuildParams = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/InitializeBuildResultFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/InitializeBuildResultFormats.scala index 1b555b42cb..8854dfe95a 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/InitializeBuildResultFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/InitializeBuildResultFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait InitializeBuildResultFormats { self: sbt.internal.bsp.codec.BuildServerCapabilitiesFormats with sbt.internal.util.codec.JValueFormats with sjsonnew.BasicJsonProtocol => +trait InitializeBuildResultFormats { self: sbt.internal.bsp.codec.BuildServerCapabilitiesFormats with sbt.internal.bsp.codec.CompileProviderFormats with sjsonnew.BasicJsonProtocol with sbt.internal.bsp.codec.TestProviderFormats with sbt.internal.bsp.codec.RunProviderFormats with sbt.internal.util.codec.JValueFormats => implicit lazy val InitializeBuildResultFormat: JsonFormat[sbt.internal.bsp.InitializeBuildResult] = new JsonFormat[sbt.internal.bsp.InitializeBuildResult] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.InitializeBuildResult = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JavacOptionsItemFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JavacOptionsItemFormats.scala new file mode 100644 index 0000000000..2605a99dcf --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JavacOptionsItemFormats.scala @@ -0,0 +1,33 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp.codec +import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } +trait JavacOptionsItemFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val JavacOptionsItemFormat: JsonFormat[sbt.internal.bsp.JavacOptionsItem] = new JsonFormat[sbt.internal.bsp.JavacOptionsItem] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.JavacOptionsItem = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val target = unbuilder.readField[sbt.internal.bsp.BuildTargetIdentifier]("target") + val options = unbuilder.readField[Vector[String]]("options") + val classpath = unbuilder.readField[Vector[java.net.URI]]("classpath") + val classDirectory = unbuilder.readField[Option[java.net.URI]]("classDirectory") + unbuilder.endObject() + sbt.internal.bsp.JavacOptionsItem(target, options, classpath, classDirectory) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.JavacOptionsItem, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("target", obj.target) + builder.addField("options", obj.options) + builder.addField("classpath", obj.classpath) + builder.addField("classDirectory", obj.classDirectory) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JavacOptionsParamsFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JavacOptionsParamsFormats.scala new file mode 100644 index 0000000000..91bd411772 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JavacOptionsParamsFormats.scala @@ -0,0 +1,27 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp.codec +import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } +trait JavacOptionsParamsFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val JavacOptionsParamsFormat: JsonFormat[sbt.internal.bsp.JavacOptionsParams] = new JsonFormat[sbt.internal.bsp.JavacOptionsParams] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.JavacOptionsParams = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val targets = unbuilder.readField[Vector[sbt.internal.bsp.BuildTargetIdentifier]]("targets") + unbuilder.endObject() + sbt.internal.bsp.JavacOptionsParams(targets) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.JavacOptionsParams, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("targets", obj.targets) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JavacOptionsResultFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JavacOptionsResultFormats.scala new file mode 100644 index 0000000000..a3f054618f --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JavacOptionsResultFormats.scala @@ -0,0 +1,27 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.bsp.codec +import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } +trait JavacOptionsResultFormats { self: sbt.internal.bsp.codec.JavacOptionsItemFormats with sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val JavacOptionsResultFormat: JsonFormat[sbt.internal.bsp.JavacOptionsResult] = new JsonFormat[sbt.internal.bsp.JavacOptionsResult] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.JavacOptionsResult = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val items = unbuilder.readField[Vector[sbt.internal.bsp.JavacOptionsItem]]("items") + unbuilder.endObject() + sbt.internal.bsp.JavacOptionsResult(items) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.JavacOptionsResult, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("items", obj.items) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JsonProtocol.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JsonProtocol.scala index 3a3ff09062..c98c92a87b 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 @@ -55,11 +55,15 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol with sbt.internal.bsp.codec.TestResultFormats with sbt.internal.bsp.codec.RunParamsFormats with sbt.internal.bsp.codec.RunResultFormats + with sbt.internal.bsp.codec.JvmBuildTargetFormats with sbt.internal.bsp.codec.ScalaBuildTargetFormats - with sbt.internal.bsp.codec.SbtBuildTargetFormats with sbt.internal.bsp.codec.ScalacOptionsParamsFormats with sbt.internal.bsp.codec.ScalacOptionsItemFormats with sbt.internal.bsp.codec.ScalacOptionsResultFormats + with sbt.internal.bsp.codec.JavacOptionsParamsFormats + with sbt.internal.bsp.codec.JavacOptionsItemFormats + with sbt.internal.bsp.codec.JavacOptionsResultFormats + with sbt.internal.bsp.codec.SbtBuildTargetFormats with sbt.internal.bsp.codec.BspConnectionDetailsFormats with sbt.internal.bsp.codec.MetalsMetadataFormats with sbt.internal.bsp.codec.ScalaTestClassesItemFormats diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmBuildTargetFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmBuildTargetFormats.scala new file mode 100644 index 0000000000..301e778ba8 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmBuildTargetFormats.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 JvmBuildTargetFormats { self: sjsonnew.BasicJsonProtocol => +implicit lazy val JvmBuildTargetFormat: JsonFormat[sbt.internal.bsp.JvmBuildTarget] = new JsonFormat[sbt.internal.bsp.JvmBuildTarget] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.JvmBuildTarget = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val javaHome = unbuilder.readField[Option[java.net.URI]]("javaHome") + val javaVersion = unbuilder.readField[Option[String]]("javaVersion") + unbuilder.endObject() + sbt.internal.bsp.JvmBuildTarget(javaHome, javaVersion) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.bsp.JvmBuildTarget, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("javaHome", obj.javaHome) + builder.addField("javaVersion", obj.javaVersion) + 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 index 6a9d40a5e3..36f1cb72ca 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmRunEnvironmentResultFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmRunEnvironmentResultFormats.scala @@ -5,7 +5,7 @@ // 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 => +trait JvmRunEnvironmentResultFormats { self: sbt.internal.bsp.codec.JvmEnvironmentItemFormats with sbt.internal.bsp.codec.BuildTargetIdentifierFormats 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 { 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 index cd9c9d2978..60598fa654 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmTestEnvironmentResultFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/JvmTestEnvironmentResultFormats.scala @@ -5,7 +5,7 @@ // 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 => +trait JvmTestEnvironmentResultFormats { self: sbt.internal.bsp.codec.JvmEnvironmentItemFormats with sbt.internal.bsp.codec.BuildTargetIdentifierFormats 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 { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/LocationFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/LocationFormats.scala index 54da583612..e6fb639126 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/LocationFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/LocationFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait LocationFormats { self: sbt.internal.bsp.codec.RangeFormats with sjsonnew.BasicJsonProtocol => +trait LocationFormats { self: sbt.internal.bsp.codec.RangeFormats with sbt.internal.bsp.codec.PositionFormats with sjsonnew.BasicJsonProtocol => implicit lazy val LocationFormat: JsonFormat[sbt.internal.bsp.Location] = new JsonFormat[sbt.internal.bsp.Location] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.Location = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/OutputPathsItemFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/OutputPathsItemFormats.scala index f3c0795ee6..602b0e51fe 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/OutputPathsItemFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/OutputPathsItemFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait OutputPathsItemFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sbt.internal.bsp.codec.OutputPathItemFormats with sjsonnew.BasicJsonProtocol => +trait OutputPathsItemFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol with sbt.internal.bsp.codec.OutputPathItemFormats => implicit lazy val OutputPathsItemFormat: JsonFormat[sbt.internal.bsp.OutputPathsItem] = new JsonFormat[sbt.internal.bsp.OutputPathsItem] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.OutputPathsItem = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/OutputPathsResultFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/OutputPathsResultFormats.scala index 0aa4750972..c2c7851101 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/OutputPathsResultFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/OutputPathsResultFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait OutputPathsResultFormats { self: sbt.internal.bsp.codec.OutputPathsItemFormats with sjsonnew.BasicJsonProtocol => +trait OutputPathsResultFormats { self: sbt.internal.bsp.codec.OutputPathsItemFormats with sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol with sbt.internal.bsp.codec.OutputPathItemFormats => implicit lazy val OutputPathsResultFormat: JsonFormat[sbt.internal.bsp.OutputPathsResult] = new JsonFormat[sbt.internal.bsp.OutputPathsResult] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.OutputPathsResult = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/PublishDiagnosticsParamsFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/PublishDiagnosticsParamsFormats.scala index 0b5551c66d..d8bf69965a 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/PublishDiagnosticsParamsFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/PublishDiagnosticsParamsFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait PublishDiagnosticsParamsFormats { self: sbt.internal.bsp.codec.TextDocumentIdentifierFormats with sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sbt.internal.bsp.codec.DiagnosticFormats with sjsonnew.BasicJsonProtocol => +trait PublishDiagnosticsParamsFormats { self: sbt.internal.bsp.codec.TextDocumentIdentifierFormats with sjsonnew.BasicJsonProtocol with sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sbt.internal.bsp.codec.DiagnosticFormats with sbt.internal.bsp.codec.RangeFormats with sbt.internal.bsp.codec.PositionFormats with sbt.internal.bsp.codec.DiagnosticRelatedInformationFormats with sbt.internal.bsp.codec.LocationFormats with sbt.internal.bsp.codec.ScalaDiagnosticFormats with sbt.internal.bsp.codec.ScalaActionFormats with sbt.internal.bsp.codec.ScalaWorkspaceEditFormats with sbt.internal.bsp.codec.ScalaTextEditFormats => implicit lazy val PublishDiagnosticsParamsFormat: JsonFormat[sbt.internal.bsp.PublishDiagnosticsParams] = new JsonFormat[sbt.internal.bsp.PublishDiagnosticsParams] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.PublishDiagnosticsParams = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ResourcesResultFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ResourcesResultFormats.scala index 6add5e4591..e8e2350296 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ResourcesResultFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ResourcesResultFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait ResourcesResultFormats { self: sbt.internal.bsp.codec.ResourcesItemFormats with sjsonnew.BasicJsonProtocol => +trait ResourcesResultFormats { self: sbt.internal.bsp.codec.ResourcesItemFormats with sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol => implicit lazy val ResourcesResultFormat: JsonFormat[sbt.internal.bsp.ResourcesResult] = new JsonFormat[sbt.internal.bsp.ResourcesResult] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.ResourcesResult = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/RunParamsFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/RunParamsFormats.scala index 808c53fcf7..8c4e47a02a 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/RunParamsFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/RunParamsFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait RunParamsFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sbt.internal.util.codec.JValueFormats with sjsonnew.BasicJsonProtocol => +trait RunParamsFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol with sbt.internal.util.codec.JValueFormats => implicit lazy val RunParamsFormat: JsonFormat[sbt.internal.bsp.RunParams] = new JsonFormat[sbt.internal.bsp.RunParams] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.RunParams = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SbtBuildTargetFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SbtBuildTargetFormats.scala index 788d26a981..4e057a969d 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SbtBuildTargetFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SbtBuildTargetFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait SbtBuildTargetFormats { self: sbt.internal.bsp.codec.ScalaBuildTargetFormats with sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol => +trait SbtBuildTargetFormats { self: sbt.internal.bsp.codec.ScalaBuildTargetFormats with sbt.internal.bsp.codec.JvmBuildTargetFormats with sjsonnew.BasicJsonProtocol with sbt.internal.bsp.codec.BuildTargetIdentifierFormats => implicit lazy val SbtBuildTargetFormat: JsonFormat[sbt.internal.bsp.SbtBuildTarget] = new JsonFormat[sbt.internal.bsp.SbtBuildTarget] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.SbtBuildTarget = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaActionFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaActionFormats.scala index 6df072a384..9262da34c3 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaActionFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaActionFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait ScalaActionFormats { self: sbt.internal.bsp.codec.ScalaWorkspaceEditFormats with sjsonnew.BasicJsonProtocol => +trait ScalaActionFormats { self: sbt.internal.bsp.codec.ScalaWorkspaceEditFormats with sbt.internal.bsp.codec.ScalaTextEditFormats with sbt.internal.bsp.codec.RangeFormats with sbt.internal.bsp.codec.PositionFormats with sjsonnew.BasicJsonProtocol => implicit lazy val ScalaActionFormat: JsonFormat[sbt.internal.bsp.ScalaAction] = new JsonFormat[sbt.internal.bsp.ScalaAction] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.ScalaAction = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaBuildTargetFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaBuildTargetFormats.scala index 900994c4ed..67b1389a36 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaBuildTargetFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaBuildTargetFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait ScalaBuildTargetFormats { self: sjsonnew.BasicJsonProtocol => +trait ScalaBuildTargetFormats { self: sbt.internal.bsp.codec.JvmBuildTargetFormats with sjsonnew.BasicJsonProtocol => implicit lazy val ScalaBuildTargetFormat: JsonFormat[sbt.internal.bsp.ScalaBuildTarget] = new JsonFormat[sbt.internal.bsp.ScalaBuildTarget] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.ScalaBuildTarget = { __jsOpt match { @@ -16,8 +16,9 @@ implicit lazy val ScalaBuildTargetFormat: JsonFormat[sbt.internal.bsp.ScalaBuild val scalaBinaryVersion = unbuilder.readField[String]("scalaBinaryVersion") val platform = unbuilder.readField[Int]("platform") val jars = unbuilder.readField[Vector[String]]("jars") + val jvmBuildTarget = unbuilder.readField[Option[sbt.internal.bsp.JvmBuildTarget]]("jvmBuildTarget") unbuilder.endObject() - sbt.internal.bsp.ScalaBuildTarget(scalaOrganization, scalaVersion, scalaBinaryVersion, platform, jars) + sbt.internal.bsp.ScalaBuildTarget(scalaOrganization, scalaVersion, scalaBinaryVersion, platform, jars, jvmBuildTarget) case None => deserializationError("Expected JsObject but found None") } @@ -29,6 +30,7 @@ implicit lazy val ScalaBuildTargetFormat: JsonFormat[sbt.internal.bsp.ScalaBuild builder.addField("scalaBinaryVersion", obj.scalaBinaryVersion) builder.addField("platform", obj.platform) builder.addField("jars", obj.jars) + builder.addField("jvmBuildTarget", obj.jvmBuildTarget) builder.endObject() } } diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaDiagnosticFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaDiagnosticFormats.scala index 0813333522..e5f1d0dbf6 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaDiagnosticFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaDiagnosticFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait ScalaDiagnosticFormats { self: sbt.internal.bsp.codec.ScalaActionFormats with sjsonnew.BasicJsonProtocol => +trait ScalaDiagnosticFormats { self: sbt.internal.bsp.codec.ScalaActionFormats with sbt.internal.bsp.codec.ScalaWorkspaceEditFormats with sbt.internal.bsp.codec.ScalaTextEditFormats with sbt.internal.bsp.codec.RangeFormats with sbt.internal.bsp.codec.PositionFormats with sjsonnew.BasicJsonProtocol => implicit lazy val ScalaDiagnosticFormat: JsonFormat[sbt.internal.bsp.ScalaDiagnostic] = new JsonFormat[sbt.internal.bsp.ScalaDiagnostic] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.ScalaDiagnostic = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaMainClassesItemFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaMainClassesItemFormats.scala index 2f96a2dc56..549559ac52 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaMainClassesItemFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaMainClassesItemFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait ScalaMainClassesItemFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sbt.internal.bsp.codec.ScalaMainClassFormats with sjsonnew.BasicJsonProtocol => +trait ScalaMainClassesItemFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol with sbt.internal.bsp.codec.ScalaMainClassFormats => implicit lazy val ScalaMainClassesItemFormat: JsonFormat[sbt.internal.bsp.ScalaMainClassesItem] = new JsonFormat[sbt.internal.bsp.ScalaMainClassesItem] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.ScalaMainClassesItem = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaMainClassesResultFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaMainClassesResultFormats.scala index b7410be21b..be3c8eb57c 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaMainClassesResultFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaMainClassesResultFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait ScalaMainClassesResultFormats { self: sbt.internal.bsp.codec.ScalaMainClassesItemFormats with sjsonnew.BasicJsonProtocol => +trait ScalaMainClassesResultFormats { self: sbt.internal.bsp.codec.ScalaMainClassesItemFormats with sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol with sbt.internal.bsp.codec.ScalaMainClassFormats => implicit lazy val ScalaMainClassesResultFormat: JsonFormat[sbt.internal.bsp.ScalaMainClassesResult] = new JsonFormat[sbt.internal.bsp.ScalaMainClassesResult] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.ScalaMainClassesResult = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaTestClassesResultFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaTestClassesResultFormats.scala index ab51a7bb20..80331a4fd2 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaTestClassesResultFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaTestClassesResultFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait ScalaTestClassesResultFormats { self: sbt.internal.bsp.codec.ScalaTestClassesItemFormats with sjsonnew.BasicJsonProtocol => +trait ScalaTestClassesResultFormats { self: sbt.internal.bsp.codec.ScalaTestClassesItemFormats with sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol => implicit lazy val ScalaTestClassesResultFormat: JsonFormat[sbt.internal.bsp.ScalaTestClassesResult] = new JsonFormat[sbt.internal.bsp.ScalaTestClassesResult] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.ScalaTestClassesResult = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaTestParamsFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaTestParamsFormats.scala index 3837db231c..c4d0fb6cfa 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaTestParamsFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaTestParamsFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait ScalaTestParamsFormats { self: sbt.internal.bsp.codec.ScalaTestClassesItemFormats with sjsonnew.BasicJsonProtocol => +trait ScalaTestParamsFormats { self: sbt.internal.bsp.codec.ScalaTestClassesItemFormats with sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol => implicit lazy val ScalaTestParamsFormat: JsonFormat[sbt.internal.bsp.ScalaTestParams] = new JsonFormat[sbt.internal.bsp.ScalaTestParams] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.ScalaTestParams = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaTextEditFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaTextEditFormats.scala index 320ff8a552..9ffda9946f 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaTextEditFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaTextEditFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait ScalaTextEditFormats { self: sbt.internal.bsp.codec.RangeFormats with sjsonnew.BasicJsonProtocol => +trait ScalaTextEditFormats { self: sbt.internal.bsp.codec.RangeFormats with sbt.internal.bsp.codec.PositionFormats with sjsonnew.BasicJsonProtocol => implicit lazy val ScalaTextEditFormat: JsonFormat[sbt.internal.bsp.ScalaTextEdit] = new JsonFormat[sbt.internal.bsp.ScalaTextEdit] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.ScalaTextEdit = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaWorkspaceEditFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaWorkspaceEditFormats.scala index 6e09a05af4..b439045cc3 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaWorkspaceEditFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaWorkspaceEditFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait ScalaWorkspaceEditFormats { self: sbt.internal.bsp.codec.ScalaTextEditFormats with sjsonnew.BasicJsonProtocol => +trait ScalaWorkspaceEditFormats { self: sbt.internal.bsp.codec.ScalaTextEditFormats with sbt.internal.bsp.codec.RangeFormats with sbt.internal.bsp.codec.PositionFormats with sjsonnew.BasicJsonProtocol => implicit lazy val ScalaWorkspaceEditFormat: JsonFormat[sbt.internal.bsp.ScalaWorkspaceEdit] = new JsonFormat[sbt.internal.bsp.ScalaWorkspaceEdit] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.ScalaWorkspaceEdit = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalacOptionsResultFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalacOptionsResultFormats.scala index ac7a2863b5..6eb060da9c 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalacOptionsResultFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalacOptionsResultFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait ScalacOptionsResultFormats { self: sbt.internal.bsp.codec.ScalacOptionsItemFormats with sjsonnew.BasicJsonProtocol => +trait ScalacOptionsResultFormats { self: sbt.internal.bsp.codec.ScalacOptionsItemFormats with sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol => implicit lazy val ScalacOptionsResultFormat: JsonFormat[sbt.internal.bsp.ScalacOptionsResult] = new JsonFormat[sbt.internal.bsp.ScalacOptionsResult] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.ScalacOptionsResult = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourcesItemFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourcesItemFormats.scala index 6862a8575c..4602ed51d9 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourcesItemFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourcesItemFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait SourcesItemFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sbt.internal.bsp.codec.SourceItemFormats with sjsonnew.BasicJsonProtocol => +trait SourcesItemFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol with sbt.internal.bsp.codec.SourceItemFormats => implicit lazy val SourcesItemFormat: JsonFormat[sbt.internal.bsp.SourcesItem] = new JsonFormat[sbt.internal.bsp.SourcesItem] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.SourcesItem = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourcesResultFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourcesResultFormats.scala index d021e1e4f7..b0d29da9fb 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourcesResultFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/SourcesResultFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait SourcesResultFormats { self: sbt.internal.bsp.codec.SourcesItemFormats with sjsonnew.BasicJsonProtocol => +trait SourcesResultFormats { self: sbt.internal.bsp.codec.SourcesItemFormats with sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol with sbt.internal.bsp.codec.SourceItemFormats => implicit lazy val SourcesResultFormat: JsonFormat[sbt.internal.bsp.SourcesResult] = new JsonFormat[sbt.internal.bsp.SourcesResult] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.SourcesResult = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/TaskFinishParamsFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/TaskFinishParamsFormats.scala index d9186fe333..d83eaf20c6 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/TaskFinishParamsFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/TaskFinishParamsFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait TaskFinishParamsFormats { self: sbt.internal.bsp.codec.TaskIdFormats with sbt.internal.util.codec.JValueFormats with sjsonnew.BasicJsonProtocol => +trait TaskFinishParamsFormats { self: sbt.internal.bsp.codec.TaskIdFormats with sjsonnew.BasicJsonProtocol with sbt.internal.util.codec.JValueFormats => implicit lazy val TaskFinishParamsFormat: JsonFormat[sbt.internal.bsp.TaskFinishParams] = new JsonFormat[sbt.internal.bsp.TaskFinishParams] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.TaskFinishParams = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/TaskProgressParamsFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/TaskProgressParamsFormats.scala index 4931e0a29e..7743336cba 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/TaskProgressParamsFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/TaskProgressParamsFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait TaskProgressParamsFormats { self: sbt.internal.bsp.codec.TaskIdFormats with sbt.internal.util.codec.JValueFormats with sjsonnew.BasicJsonProtocol => +trait TaskProgressParamsFormats { self: sbt.internal.bsp.codec.TaskIdFormats with sjsonnew.BasicJsonProtocol with sbt.internal.util.codec.JValueFormats => implicit lazy val TaskProgressParamsFormat: JsonFormat[sbt.internal.bsp.TaskProgressParams] = new JsonFormat[sbt.internal.bsp.TaskProgressParams] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.TaskProgressParams = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/TaskStartParamsFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/TaskStartParamsFormats.scala index a6f4ef766f..f333512b56 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/TaskStartParamsFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/TaskStartParamsFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait TaskStartParamsFormats { self: sbt.internal.bsp.codec.TaskIdFormats with sbt.internal.util.codec.JValueFormats with sjsonnew.BasicJsonProtocol => +trait TaskStartParamsFormats { self: sbt.internal.bsp.codec.TaskIdFormats with sjsonnew.BasicJsonProtocol with sbt.internal.util.codec.JValueFormats => implicit lazy val TaskStartParamsFormat: JsonFormat[sbt.internal.bsp.TaskStartParams] = new JsonFormat[sbt.internal.bsp.TaskStartParams] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.TaskStartParams = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/TestParamsFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/TestParamsFormats.scala index 7e07dd1887..9dd0dd6836 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/TestParamsFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/TestParamsFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait TestParamsFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sbt.internal.util.codec.JValueFormats with sjsonnew.BasicJsonProtocol => +trait TestParamsFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol with sbt.internal.util.codec.JValueFormats => implicit lazy val TestParamsFormat: JsonFormat[sbt.internal.bsp.TestParams] = new JsonFormat[sbt.internal.bsp.TestParams] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.TestParams = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/WorkspaceBuildTargetsResultFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/WorkspaceBuildTargetsResultFormats.scala index d21532d726..9157e04a3e 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/WorkspaceBuildTargetsResultFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/WorkspaceBuildTargetsResultFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.bsp.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait WorkspaceBuildTargetsResultFormats { self: sbt.internal.bsp.codec.BuildTargetFormats with sjsonnew.BasicJsonProtocol => +trait WorkspaceBuildTargetsResultFormats { self: sbt.internal.bsp.codec.BuildTargetFormats with sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol with sbt.internal.bsp.codec.BuildTargetCapabilitiesFormats with sbt.internal.util.codec.JValueFormats => implicit lazy val WorkspaceBuildTargetsResultFormat: JsonFormat[sbt.internal.bsp.WorkspaceBuildTargetsResult] = new JsonFormat[sbt.internal.bsp.WorkspaceBuildTargetsResult] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.WorkspaceBuildTargetsResult = { __jsOpt match { 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 index eaf3c902c7..eeef032845 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/graph/codec/ModuleModelFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/graph/codec/ModuleModelFormats.scala @@ -5,7 +5,7 @@ // 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 => +trait ModuleModelFormats { self: 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 { diff --git a/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/DiagnosticFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/DiagnosticFormats.scala index 6873fcf62a..e46132f3f6 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/DiagnosticFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/DiagnosticFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.langserver.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait DiagnosticFormats { self: sbt.internal.langserver.codec.RangeFormats with sjsonnew.BasicJsonProtocol => +trait DiagnosticFormats { self: sbt.internal.langserver.codec.RangeFormats with sbt.internal.langserver.codec.PositionFormats with sjsonnew.BasicJsonProtocol => implicit lazy val DiagnosticFormat: JsonFormat[sbt.internal.langserver.Diagnostic] = new JsonFormat[sbt.internal.langserver.Diagnostic] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.langserver.Diagnostic = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/InitializeResultFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/InitializeResultFormats.scala index 37b42fbd42..eba490e178 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/InitializeResultFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/InitializeResultFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.langserver.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait InitializeResultFormats { self: sbt.internal.langserver.codec.ServerCapabilitiesFormats with sjsonnew.BasicJsonProtocol => +trait InitializeResultFormats { self: sbt.internal.langserver.codec.ServerCapabilitiesFormats with sbt.internal.langserver.codec.TextDocumentSyncOptionsFormats with sbt.internal.langserver.codec.SaveOptionsFormats with sjsonnew.BasicJsonProtocol => implicit lazy val InitializeResultFormat: JsonFormat[sbt.internal.langserver.InitializeResult] = new JsonFormat[sbt.internal.langserver.InitializeResult] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.langserver.InitializeResult = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/LocationFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/LocationFormats.scala index 04566a4cf4..d8bcde3314 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/LocationFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/LocationFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.langserver.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait LocationFormats { self: sbt.internal.langserver.codec.RangeFormats with sjsonnew.BasicJsonProtocol => +trait LocationFormats { self: sbt.internal.langserver.codec.RangeFormats with sbt.internal.langserver.codec.PositionFormats with sjsonnew.BasicJsonProtocol => implicit lazy val LocationFormat: JsonFormat[sbt.internal.langserver.Location] = new JsonFormat[sbt.internal.langserver.Location] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.langserver.Location = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/PublishDiagnosticsParamsFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/PublishDiagnosticsParamsFormats.scala index a9730ce2fa..669b702e3d 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/PublishDiagnosticsParamsFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/PublishDiagnosticsParamsFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.langserver.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait PublishDiagnosticsParamsFormats { self: sbt.internal.langserver.codec.DiagnosticFormats with sjsonnew.BasicJsonProtocol => +trait PublishDiagnosticsParamsFormats { self: sbt.internal.langserver.codec.DiagnosticFormats with sbt.internal.langserver.codec.RangeFormats with sbt.internal.langserver.codec.PositionFormats with sjsonnew.BasicJsonProtocol => implicit lazy val PublishDiagnosticsParamsFormat: JsonFormat[sbt.internal.langserver.PublishDiagnosticsParams] = new JsonFormat[sbt.internal.langserver.PublishDiagnosticsParams] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.langserver.PublishDiagnosticsParams = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/ServerCapabilitiesFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/ServerCapabilitiesFormats.scala index b980c95fed..dd45045776 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/ServerCapabilitiesFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/ServerCapabilitiesFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.langserver.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait ServerCapabilitiesFormats { self: sbt.internal.langserver.codec.TextDocumentSyncOptionsFormats with sjsonnew.BasicJsonProtocol => +trait ServerCapabilitiesFormats { self: sbt.internal.langserver.codec.TextDocumentSyncOptionsFormats with sbt.internal.langserver.codec.SaveOptionsFormats with sjsonnew.BasicJsonProtocol => implicit lazy val ServerCapabilitiesFormat: JsonFormat[sbt.internal.langserver.ServerCapabilities] = new JsonFormat[sbt.internal.langserver.ServerCapabilities] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.langserver.ServerCapabilities = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/TextDocumentPositionParamsFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/TextDocumentPositionParamsFormats.scala index f5aa877e3f..91de603986 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/TextDocumentPositionParamsFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/TextDocumentPositionParamsFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.internal.langserver.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait TextDocumentPositionParamsFormats { self: sbt.internal.langserver.codec.TextDocumentIdentifierFormats with sbt.internal.langserver.codec.PositionFormats with sjsonnew.BasicJsonProtocol => +trait TextDocumentPositionParamsFormats { self: sbt.internal.langserver.codec.TextDocumentIdentifierFormats with sjsonnew.BasicJsonProtocol with sbt.internal.langserver.codec.PositionFormats => implicit lazy val TextDocumentPositionParamsFormat: JsonFormat[sbt.internal.langserver.TextDocumentPositionParams] = new JsonFormat[sbt.internal.langserver.TextDocumentPositionParams] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.langserver.TextDocumentPositionParams = { __jsOpt match { diff --git a/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/TextDocumentPositionParamsInterfaceFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/TextDocumentPositionParamsInterfaceFormats.scala index 3dff106a5f..83843d5e2d 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/TextDocumentPositionParamsInterfaceFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/langserver/codec/TextDocumentPositionParamsInterfaceFormats.scala @@ -6,6 +6,6 @@ package sbt.internal.langserver.codec import _root_.sjsonnew.JsonFormat -trait TextDocumentPositionParamsInterfaceFormats { self: sbt.internal.langserver.codec.TextDocumentIdentifierFormats with sbt.internal.langserver.codec.PositionFormats with sjsonnew.BasicJsonProtocol with sbt.internal.langserver.codec.TextDocumentPositionParamsFormats => +trait TextDocumentPositionParamsInterfaceFormats { self: sbt.internal.langserver.codec.TextDocumentIdentifierFormats with sjsonnew.BasicJsonProtocol with sbt.internal.langserver.codec.PositionFormats with sbt.internal.langserver.codec.TextDocumentPositionParamsFormats => implicit lazy val TextDocumentPositionParamsInterfaceFormat: JsonFormat[sbt.internal.langserver.TextDocumentPositionParamsInterface] = flatUnionFormat1[sbt.internal.langserver.TextDocumentPositionParamsInterface, sbt.internal.langserver.TextDocumentPositionParams]("type") } diff --git a/protocol/src/main/contraband-scala/sbt/internal/protocol/InitializeOption.scala b/protocol/src/main/contraband-scala/sbt/internal/protocol/InitializeOption.scala index b900d641d6..0a5f72a079 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/protocol/InitializeOption.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/protocol/InitializeOption.scala @@ -4,24 +4,30 @@ // DO NOT EDIT MANUALLY package sbt.internal.protocol +/** + * Passed into InitializeParams as part of "initialize" request as the user-defined option. + * https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize + */ final class InitializeOption private ( val token: Option[String], - val skipAnalysis: Option[Boolean]) extends Serializable { + val skipAnalysis: Option[Boolean], + val canWork: Option[Boolean]) extends Serializable { - private def this(token: Option[String]) = this(token, None) + private def this(token: Option[String]) = this(token, None, None) + private def this(token: Option[String], skipAnalysis: Option[Boolean]) = this(token, skipAnalysis, None) override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { - case x: InitializeOption => (this.token == x.token) && (this.skipAnalysis == x.skipAnalysis) + case x: InitializeOption => (this.token == x.token) && (this.skipAnalysis == x.skipAnalysis) && (this.canWork == x.canWork) case _ => false }) override def hashCode: Int = { - 37 * (37 * (37 * (17 + "sbt.internal.protocol.InitializeOption".##) + token.##) + skipAnalysis.##) + 37 * (37 * (37 * (37 * (17 + "sbt.internal.protocol.InitializeOption".##) + token.##) + skipAnalysis.##) + canWork.##) } override def toString: String = { - "InitializeOption(" + token + ", " + skipAnalysis + ")" + "InitializeOption(" + token + ", " + skipAnalysis + ", " + canWork + ")" } - private[this] def copy(token: Option[String] = token, skipAnalysis: Option[Boolean] = skipAnalysis): InitializeOption = { - new InitializeOption(token, skipAnalysis) + private[this] def copy(token: Option[String] = token, skipAnalysis: Option[Boolean] = skipAnalysis, canWork: Option[Boolean] = canWork): InitializeOption = { + new InitializeOption(token, skipAnalysis, canWork) } def withToken(token: Option[String]): InitializeOption = { copy(token = token) @@ -35,6 +41,12 @@ final class InitializeOption private ( def withSkipAnalysis(skipAnalysis: Boolean): InitializeOption = { copy(skipAnalysis = Option(skipAnalysis)) } + def withCanWork(canWork: Option[Boolean]): InitializeOption = { + copy(canWork = canWork) + } + def withCanWork(canWork: Boolean): InitializeOption = { + copy(canWork = Option(canWork)) + } } object InitializeOption { @@ -42,4 +54,6 @@ object InitializeOption { def apply(token: String): InitializeOption = new InitializeOption(Option(token)) def apply(token: Option[String], skipAnalysis: Option[Boolean]): InitializeOption = new InitializeOption(token, skipAnalysis) def apply(token: String, skipAnalysis: Boolean): InitializeOption = new InitializeOption(Option(token), Option(skipAnalysis)) + def apply(token: Option[String], skipAnalysis: Option[Boolean], canWork: Option[Boolean]): InitializeOption = new InitializeOption(token, skipAnalysis, canWork) + def apply(token: String, skipAnalysis: Boolean, canWork: Boolean): InitializeOption = new InitializeOption(Option(token), Option(skipAnalysis), Option(canWork)) } diff --git a/protocol/src/main/contraband-scala/sbt/internal/protocol/codec/InitializeOptionFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/protocol/codec/InitializeOptionFormats.scala index f7f3a09e79..a5b04d6a42 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/protocol/codec/InitializeOptionFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/protocol/codec/InitializeOptionFormats.scala @@ -13,8 +13,9 @@ implicit lazy val InitializeOptionFormat: JsonFormat[sbt.internal.protocol.Initi unbuilder.beginObject(__js) val token = unbuilder.readField[Option[String]]("token") val skipAnalysis = unbuilder.readField[Option[Boolean]]("skipAnalysis") + val canWork = unbuilder.readField[Option[Boolean]]("canWork") unbuilder.endObject() - sbt.internal.protocol.InitializeOption(token, skipAnalysis) + sbt.internal.protocol.InitializeOption(token, skipAnalysis, canWork) case None => deserializationError("Expected JsObject but found None") } @@ -23,6 +24,7 @@ implicit lazy val InitializeOptionFormat: JsonFormat[sbt.internal.protocol.Initi builder.beginObject() builder.addField("token", obj.token) builder.addField("skipAnalysis", obj.skipAnalysis) + builder.addField("canWork", obj.canWork) builder.endObject() } } diff --git a/protocol/src/main/contraband-scala/sbt/internal/worker/ClientJobParams.scala b/protocol/src/main/contraband-scala/sbt/internal/worker/ClientJobParams.scala new file mode 100644 index 0000000000..d72bf2b9f2 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/worker/ClientJobParams.scala @@ -0,0 +1,45 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.worker +/** + * Client-side job support. + * + * Notification: sbt/clientJob + * + * Parameter for the sbt/clientJob notification. + * A client-side job represents a unit of work that sbt server + * can outsourse back to the client, for example for run task. + */ +final class ClientJobParams private ( + val runInfo: Option[sbt.internal.worker.RunInfo]) extends Serializable { + + + + override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { + case x: ClientJobParams => (this.runInfo == x.runInfo) + case _ => false + }) + override def hashCode: Int = { + 37 * (37 * (17 + "sbt.internal.worker.ClientJobParams".##) + runInfo.##) + } + override def toString: String = { + "ClientJobParams(" + runInfo + ")" + } + private[this] def copy(runInfo: Option[sbt.internal.worker.RunInfo] = runInfo): ClientJobParams = { + new ClientJobParams(runInfo) + } + def withRunInfo(runInfo: Option[sbt.internal.worker.RunInfo]): ClientJobParams = { + copy(runInfo = runInfo) + } + def withRunInfo(runInfo: sbt.internal.worker.RunInfo): ClientJobParams = { + copy(runInfo = Option(runInfo)) + } +} +object ClientJobParams { + + def apply(runInfo: Option[sbt.internal.worker.RunInfo]): ClientJobParams = new ClientJobParams(runInfo) + def apply(runInfo: sbt.internal.worker.RunInfo): ClientJobParams = new ClientJobParams(Option(runInfo)) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/worker/FilePath.scala b/protocol/src/main/contraband-scala/sbt/internal/worker/FilePath.scala new file mode 100644 index 0000000000..24647f3c09 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/worker/FilePath.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.worker +final class FilePath private ( + val path: java.net.URI, + val digest: String) extends Serializable { + + + + override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { + case x: FilePath => (this.path == x.path) && (this.digest == x.digest) + case _ => false + }) + override def hashCode: Int = { + 37 * (37 * (37 * (17 + "sbt.internal.worker.FilePath".##) + path.##) + digest.##) + } + override def toString: String = { + "FilePath(" + path + ", " + digest + ")" + } + private[this] def copy(path: java.net.URI = path, digest: String = digest): FilePath = { + new FilePath(path, digest) + } + def withPath(path: java.net.URI): FilePath = { + copy(path = path) + } + def withDigest(digest: String): FilePath = { + copy(digest = digest) + } +} +object FilePath { + + def apply(path: java.net.URI, digest: String): FilePath = new FilePath(path, digest) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/worker/JvmRunInfo.scala b/protocol/src/main/contraband-scala/sbt/internal/worker/JvmRunInfo.scala new file mode 100644 index 0000000000..d0d6b5b73d --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/worker/JvmRunInfo.scala @@ -0,0 +1,84 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.worker +final class JvmRunInfo private ( + val args: Vector[String], + val classpath: Vector[sbt.internal.worker.FilePath], + val mainClass: String, + val connectInput: Boolean, + val javaHome: Option[java.net.URI], + val outputStrategy: Option[String], + val workingDirectory: Option[java.net.URI], + val jvmOptions: Vector[String], + val environmentVariables: scala.collection.immutable.Map[String, String], + val inputs: Vector[sbt.internal.worker.FilePath], + val outputs: Vector[sbt.internal.worker.FilePath]) extends Serializable { + + private def this(args: Vector[String], classpath: Vector[sbt.internal.worker.FilePath], mainClass: String, connectInput: Boolean, javaHome: Option[java.net.URI], outputStrategy: Option[String], workingDirectory: Option[java.net.URI], jvmOptions: Vector[String], environmentVariables: scala.collection.immutable.Map[String, String]) = this(args, classpath, mainClass, connectInput, javaHome, outputStrategy, workingDirectory, jvmOptions, environmentVariables, Vector(), Vector()) + + override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { + case x: JvmRunInfo => (this.args == x.args) && (this.classpath == x.classpath) && (this.mainClass == x.mainClass) && (this.connectInput == x.connectInput) && (this.javaHome == x.javaHome) && (this.outputStrategy == x.outputStrategy) && (this.workingDirectory == x.workingDirectory) && (this.jvmOptions == x.jvmOptions) && (this.environmentVariables == x.environmentVariables) && (this.inputs == x.inputs) && (this.outputs == x.outputs) + case _ => false + }) + override def hashCode: Int = { + 37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.worker.JvmRunInfo".##) + args.##) + classpath.##) + mainClass.##) + connectInput.##) + javaHome.##) + outputStrategy.##) + workingDirectory.##) + jvmOptions.##) + environmentVariables.##) + inputs.##) + outputs.##) + } + override def toString: String = { + "JvmRunInfo(" + args + ", " + classpath + ", " + mainClass + ", " + connectInput + ", " + javaHome + ", " + outputStrategy + ", " + workingDirectory + ", " + jvmOptions + ", " + environmentVariables + ", " + inputs + ", " + outputs + ")" + } + private[this] def copy(args: Vector[String] = args, classpath: Vector[sbt.internal.worker.FilePath] = classpath, mainClass: String = mainClass, connectInput: Boolean = connectInput, javaHome: Option[java.net.URI] = javaHome, outputStrategy: Option[String] = outputStrategy, workingDirectory: Option[java.net.URI] = workingDirectory, jvmOptions: Vector[String] = jvmOptions, environmentVariables: scala.collection.immutable.Map[String, String] = environmentVariables, inputs: Vector[sbt.internal.worker.FilePath] = inputs, outputs: Vector[sbt.internal.worker.FilePath] = outputs): JvmRunInfo = { + new JvmRunInfo(args, classpath, mainClass, connectInput, javaHome, outputStrategy, workingDirectory, jvmOptions, environmentVariables, inputs, outputs) + } + def withArgs(args: Vector[String]): JvmRunInfo = { + copy(args = args) + } + def withClasspath(classpath: Vector[sbt.internal.worker.FilePath]): JvmRunInfo = { + copy(classpath = classpath) + } + def withMainClass(mainClass: String): JvmRunInfo = { + copy(mainClass = mainClass) + } + def withConnectInput(connectInput: Boolean): JvmRunInfo = { + copy(connectInput = connectInput) + } + def withJavaHome(javaHome: Option[java.net.URI]): JvmRunInfo = { + copy(javaHome = javaHome) + } + def withJavaHome(javaHome: java.net.URI): JvmRunInfo = { + copy(javaHome = Option(javaHome)) + } + def withOutputStrategy(outputStrategy: Option[String]): JvmRunInfo = { + copy(outputStrategy = outputStrategy) + } + def withOutputStrategy(outputStrategy: String): JvmRunInfo = { + copy(outputStrategy = Option(outputStrategy)) + } + def withWorkingDirectory(workingDirectory: Option[java.net.URI]): JvmRunInfo = { + copy(workingDirectory = workingDirectory) + } + def withWorkingDirectory(workingDirectory: java.net.URI): JvmRunInfo = { + copy(workingDirectory = Option(workingDirectory)) + } + def withJvmOptions(jvmOptions: Vector[String]): JvmRunInfo = { + copy(jvmOptions = jvmOptions) + } + def withEnvironmentVariables(environmentVariables: scala.collection.immutable.Map[String, String]): JvmRunInfo = { + copy(environmentVariables = environmentVariables) + } + def withInputs(inputs: Vector[sbt.internal.worker.FilePath]): JvmRunInfo = { + copy(inputs = inputs) + } + def withOutputs(outputs: Vector[sbt.internal.worker.FilePath]): JvmRunInfo = { + copy(outputs = outputs) + } +} +object JvmRunInfo { + + def apply(args: Vector[String], classpath: Vector[sbt.internal.worker.FilePath], mainClass: String, connectInput: Boolean, javaHome: Option[java.net.URI], outputStrategy: Option[String], workingDirectory: Option[java.net.URI], jvmOptions: Vector[String], environmentVariables: scala.collection.immutable.Map[String, String]): JvmRunInfo = new JvmRunInfo(args, classpath, mainClass, connectInput, javaHome, outputStrategy, workingDirectory, jvmOptions, environmentVariables) + def apply(args: Vector[String], classpath: Vector[sbt.internal.worker.FilePath], mainClass: String, connectInput: Boolean, javaHome: java.net.URI, outputStrategy: String, workingDirectory: java.net.URI, jvmOptions: Vector[String], environmentVariables: scala.collection.immutable.Map[String, String]): JvmRunInfo = new JvmRunInfo(args, classpath, mainClass, connectInput, Option(javaHome), Option(outputStrategy), Option(workingDirectory), jvmOptions, environmentVariables) + def apply(args: Vector[String], classpath: Vector[sbt.internal.worker.FilePath], mainClass: String, connectInput: Boolean, javaHome: Option[java.net.URI], outputStrategy: Option[String], workingDirectory: Option[java.net.URI], jvmOptions: Vector[String], environmentVariables: scala.collection.immutable.Map[String, String], inputs: Vector[sbt.internal.worker.FilePath], outputs: Vector[sbt.internal.worker.FilePath]): JvmRunInfo = new JvmRunInfo(args, classpath, mainClass, connectInput, javaHome, outputStrategy, workingDirectory, jvmOptions, environmentVariables, inputs, outputs) + def apply(args: Vector[String], classpath: Vector[sbt.internal.worker.FilePath], mainClass: String, connectInput: Boolean, javaHome: java.net.URI, outputStrategy: String, workingDirectory: java.net.URI, jvmOptions: Vector[String], environmentVariables: scala.collection.immutable.Map[String, String], inputs: Vector[sbt.internal.worker.FilePath], outputs: Vector[sbt.internal.worker.FilePath]): JvmRunInfo = new JvmRunInfo(args, classpath, mainClass, connectInput, Option(javaHome), Option(outputStrategy), Option(workingDirectory), jvmOptions, environmentVariables, inputs, outputs) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/worker/NativeRunInfo.scala b/protocol/src/main/contraband-scala/sbt/internal/worker/NativeRunInfo.scala new file mode 100644 index 0000000000..5caffe8fd4 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/worker/NativeRunInfo.scala @@ -0,0 +1,69 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.worker +final class NativeRunInfo private ( + val cmd: String, + val args: Vector[String], + val connectInput: Boolean, + val outputStrategy: Option[String], + val workingDirectory: Option[java.net.URI], + val environmentVariables: scala.collection.immutable.Map[String, String], + val inputs: Vector[sbt.internal.worker.FilePath], + val outputs: Vector[sbt.internal.worker.FilePath]) extends Serializable { + + private def this(cmd: String, args: Vector[String], connectInput: Boolean, outputStrategy: Option[String], workingDirectory: Option[java.net.URI], environmentVariables: scala.collection.immutable.Map[String, String]) = this(cmd, args, connectInput, outputStrategy, workingDirectory, environmentVariables, Vector(), Vector()) + + override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { + case x: NativeRunInfo => (this.cmd == x.cmd) && (this.args == x.args) && (this.connectInput == x.connectInput) && (this.outputStrategy == x.outputStrategy) && (this.workingDirectory == x.workingDirectory) && (this.environmentVariables == x.environmentVariables) && (this.inputs == x.inputs) && (this.outputs == x.outputs) + case _ => false + }) + override def hashCode: Int = { + 37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.worker.NativeRunInfo".##) + cmd.##) + args.##) + connectInput.##) + outputStrategy.##) + workingDirectory.##) + environmentVariables.##) + inputs.##) + outputs.##) + } + override def toString: String = { + "NativeRunInfo(" + cmd + ", " + args + ", " + connectInput + ", " + outputStrategy + ", " + workingDirectory + ", " + environmentVariables + ", " + inputs + ", " + outputs + ")" + } + private[this] def copy(cmd: String = cmd, args: Vector[String] = args, connectInput: Boolean = connectInput, outputStrategy: Option[String] = outputStrategy, workingDirectory: Option[java.net.URI] = workingDirectory, environmentVariables: scala.collection.immutable.Map[String, String] = environmentVariables, inputs: Vector[sbt.internal.worker.FilePath] = inputs, outputs: Vector[sbt.internal.worker.FilePath] = outputs): NativeRunInfo = { + new NativeRunInfo(cmd, args, connectInput, outputStrategy, workingDirectory, environmentVariables, inputs, outputs) + } + def withCmd(cmd: String): NativeRunInfo = { + copy(cmd = cmd) + } + def withArgs(args: Vector[String]): NativeRunInfo = { + copy(args = args) + } + def withConnectInput(connectInput: Boolean): NativeRunInfo = { + copy(connectInput = connectInput) + } + def withOutputStrategy(outputStrategy: Option[String]): NativeRunInfo = { + copy(outputStrategy = outputStrategy) + } + def withOutputStrategy(outputStrategy: String): NativeRunInfo = { + copy(outputStrategy = Option(outputStrategy)) + } + def withWorkingDirectory(workingDirectory: Option[java.net.URI]): NativeRunInfo = { + copy(workingDirectory = workingDirectory) + } + def withWorkingDirectory(workingDirectory: java.net.URI): NativeRunInfo = { + copy(workingDirectory = Option(workingDirectory)) + } + def withEnvironmentVariables(environmentVariables: scala.collection.immutable.Map[String, String]): NativeRunInfo = { + copy(environmentVariables = environmentVariables) + } + def withInputs(inputs: Vector[sbt.internal.worker.FilePath]): NativeRunInfo = { + copy(inputs = inputs) + } + def withOutputs(outputs: Vector[sbt.internal.worker.FilePath]): NativeRunInfo = { + copy(outputs = outputs) + } +} +object NativeRunInfo { + + def apply(cmd: String, args: Vector[String], connectInput: Boolean, outputStrategy: Option[String], workingDirectory: Option[java.net.URI], environmentVariables: scala.collection.immutable.Map[String, String]): NativeRunInfo = new NativeRunInfo(cmd, args, connectInput, outputStrategy, workingDirectory, environmentVariables) + def apply(cmd: String, args: Vector[String], connectInput: Boolean, outputStrategy: String, workingDirectory: java.net.URI, environmentVariables: scala.collection.immutable.Map[String, String]): NativeRunInfo = new NativeRunInfo(cmd, args, connectInput, Option(outputStrategy), Option(workingDirectory), environmentVariables) + def apply(cmd: String, args: Vector[String], connectInput: Boolean, outputStrategy: Option[String], workingDirectory: Option[java.net.URI], environmentVariables: scala.collection.immutable.Map[String, String], inputs: Vector[sbt.internal.worker.FilePath], outputs: Vector[sbt.internal.worker.FilePath]): NativeRunInfo = new NativeRunInfo(cmd, args, connectInput, outputStrategy, workingDirectory, environmentVariables, inputs, outputs) + def apply(cmd: String, args: Vector[String], connectInput: Boolean, outputStrategy: String, workingDirectory: java.net.URI, environmentVariables: scala.collection.immutable.Map[String, String], inputs: Vector[sbt.internal.worker.FilePath], outputs: Vector[sbt.internal.worker.FilePath]): NativeRunInfo = new NativeRunInfo(cmd, args, connectInput, Option(outputStrategy), Option(workingDirectory), environmentVariables, inputs, outputs) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/worker/RunInfo.scala b/protocol/src/main/contraband-scala/sbt/internal/worker/RunInfo.scala new file mode 100644 index 0000000000..855bd06d38 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/worker/RunInfo.scala @@ -0,0 +1,49 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.worker +final class RunInfo private ( + val jvm: Boolean, + val jvmRunInfo: Option[sbt.internal.worker.JvmRunInfo], + val nativeRunInfo: Option[sbt.internal.worker.NativeRunInfo]) extends Serializable { + + private def this(jvm: Boolean, jvmRunInfo: Option[sbt.internal.worker.JvmRunInfo]) = this(jvm, jvmRunInfo, None) + + override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { + case x: RunInfo => (this.jvm == x.jvm) && (this.jvmRunInfo == x.jvmRunInfo) && (this.nativeRunInfo == x.nativeRunInfo) + case _ => false + }) + override def hashCode: Int = { + 37 * (37 * (37 * (37 * (17 + "sbt.internal.worker.RunInfo".##) + jvm.##) + jvmRunInfo.##) + nativeRunInfo.##) + } + override def toString: String = { + "RunInfo(" + jvm + ", " + jvmRunInfo + ", " + nativeRunInfo + ")" + } + private[this] def copy(jvm: Boolean = jvm, jvmRunInfo: Option[sbt.internal.worker.JvmRunInfo] = jvmRunInfo, nativeRunInfo: Option[sbt.internal.worker.NativeRunInfo] = nativeRunInfo): RunInfo = { + new RunInfo(jvm, jvmRunInfo, nativeRunInfo) + } + def withJvm(jvm: Boolean): RunInfo = { + copy(jvm = jvm) + } + def withJvmRunInfo(jvmRunInfo: Option[sbt.internal.worker.JvmRunInfo]): RunInfo = { + copy(jvmRunInfo = jvmRunInfo) + } + def withJvmRunInfo(jvmRunInfo: sbt.internal.worker.JvmRunInfo): RunInfo = { + copy(jvmRunInfo = Option(jvmRunInfo)) + } + def withNativeRunInfo(nativeRunInfo: Option[sbt.internal.worker.NativeRunInfo]): RunInfo = { + copy(nativeRunInfo = nativeRunInfo) + } + def withNativeRunInfo(nativeRunInfo: sbt.internal.worker.NativeRunInfo): RunInfo = { + copy(nativeRunInfo = Option(nativeRunInfo)) + } +} +object RunInfo { + + def apply(jvm: Boolean, jvmRunInfo: Option[sbt.internal.worker.JvmRunInfo]): RunInfo = new RunInfo(jvm, jvmRunInfo) + def apply(jvm: Boolean, jvmRunInfo: sbt.internal.worker.JvmRunInfo): RunInfo = new RunInfo(jvm, Option(jvmRunInfo)) + def apply(jvm: Boolean, jvmRunInfo: Option[sbt.internal.worker.JvmRunInfo], nativeRunInfo: Option[sbt.internal.worker.NativeRunInfo]): RunInfo = new RunInfo(jvm, jvmRunInfo, nativeRunInfo) + def apply(jvm: Boolean, jvmRunInfo: sbt.internal.worker.JvmRunInfo, nativeRunInfo: sbt.internal.worker.NativeRunInfo): RunInfo = new RunInfo(jvm, Option(jvmRunInfo), Option(nativeRunInfo)) +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/worker/codec/ClientJobParamsFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/worker/codec/ClientJobParamsFormats.scala new file mode 100644 index 0000000000..e045d628ca --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/worker/codec/ClientJobParamsFormats.scala @@ -0,0 +1,27 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.worker.codec +import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } +trait ClientJobParamsFormats { self: sbt.internal.worker.codec.RunInfoFormats with sbt.internal.worker.codec.JvmRunInfoFormats with sbt.internal.worker.codec.FilePathFormats with sjsonnew.BasicJsonProtocol with sbt.internal.worker.codec.NativeRunInfoFormats => +implicit lazy val ClientJobParamsFormat: JsonFormat[sbt.internal.worker.ClientJobParams] = new JsonFormat[sbt.internal.worker.ClientJobParams] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.worker.ClientJobParams = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val runInfo = unbuilder.readField[Option[sbt.internal.worker.RunInfo]]("runInfo") + unbuilder.endObject() + sbt.internal.worker.ClientJobParams(runInfo) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.worker.ClientJobParams, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("runInfo", obj.runInfo) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/worker/codec/FilePathFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/worker/codec/FilePathFormats.scala new file mode 100644 index 0000000000..ebbac551ff --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/worker/codec/FilePathFormats.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.worker.codec +import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } +trait FilePathFormats { self: sjsonnew.BasicJsonProtocol => +implicit lazy val FilePathFormat: JsonFormat[sbt.internal.worker.FilePath] = new JsonFormat[sbt.internal.worker.FilePath] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.worker.FilePath = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val path = unbuilder.readField[java.net.URI]("path") + val digest = unbuilder.readField[String]("digest") + unbuilder.endObject() + sbt.internal.worker.FilePath(path, digest) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.worker.FilePath, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("path", obj.path) + builder.addField("digest", obj.digest) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/worker/codec/JsonProtocol.scala b/protocol/src/main/contraband-scala/sbt/internal/worker/codec/JsonProtocol.scala new file mode 100644 index 0000000000..fa29c174cd --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/worker/codec/JsonProtocol.scala @@ -0,0 +1,13 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.worker.codec +trait JsonProtocol extends sjsonnew.BasicJsonProtocol + with sbt.internal.worker.codec.FilePathFormats + with sbt.internal.worker.codec.JvmRunInfoFormats + with sbt.internal.worker.codec.NativeRunInfoFormats + with sbt.internal.worker.codec.RunInfoFormats + with sbt.internal.worker.codec.ClientJobParamsFormats +object JsonProtocol extends JsonProtocol \ No newline at end of file diff --git a/protocol/src/main/contraband-scala/sbt/internal/worker/codec/JvmRunInfoFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/worker/codec/JvmRunInfoFormats.scala new file mode 100644 index 0000000000..793828b5ef --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/worker/codec/JvmRunInfoFormats.scala @@ -0,0 +1,47 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.worker.codec +import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } +trait JvmRunInfoFormats { self: sbt.internal.worker.codec.FilePathFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val JvmRunInfoFormat: JsonFormat[sbt.internal.worker.JvmRunInfo] = new JsonFormat[sbt.internal.worker.JvmRunInfo] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.worker.JvmRunInfo = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val args = unbuilder.readField[Vector[String]]("args") + val classpath = unbuilder.readField[Vector[sbt.internal.worker.FilePath]]("classpath") + val mainClass = unbuilder.readField[String]("mainClass") + val connectInput = unbuilder.readField[Boolean]("connectInput") + val javaHome = unbuilder.readField[Option[java.net.URI]]("javaHome") + val outputStrategy = unbuilder.readField[Option[String]]("outputStrategy") + val workingDirectory = unbuilder.readField[Option[java.net.URI]]("workingDirectory") + val jvmOptions = unbuilder.readField[Vector[String]]("jvmOptions") + val environmentVariables = unbuilder.readField[scala.collection.immutable.Map[String, String]]("environmentVariables") + val inputs = unbuilder.readField[Vector[sbt.internal.worker.FilePath]]("inputs") + val outputs = unbuilder.readField[Vector[sbt.internal.worker.FilePath]]("outputs") + unbuilder.endObject() + sbt.internal.worker.JvmRunInfo(args, classpath, mainClass, connectInput, javaHome, outputStrategy, workingDirectory, jvmOptions, environmentVariables, inputs, outputs) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.worker.JvmRunInfo, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("args", obj.args) + builder.addField("classpath", obj.classpath) + builder.addField("mainClass", obj.mainClass) + builder.addField("connectInput", obj.connectInput) + builder.addField("javaHome", obj.javaHome) + builder.addField("outputStrategy", obj.outputStrategy) + builder.addField("workingDirectory", obj.workingDirectory) + builder.addField("jvmOptions", obj.jvmOptions) + builder.addField("environmentVariables", obj.environmentVariables) + builder.addField("inputs", obj.inputs) + builder.addField("outputs", obj.outputs) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/worker/codec/NativeRunInfoFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/worker/codec/NativeRunInfoFormats.scala new file mode 100644 index 0000000000..73588aa9f3 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/worker/codec/NativeRunInfoFormats.scala @@ -0,0 +1,41 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.worker.codec +import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } +trait NativeRunInfoFormats { self: sbt.internal.worker.codec.FilePathFormats with sjsonnew.BasicJsonProtocol => +implicit lazy val NativeRunInfoFormat: JsonFormat[sbt.internal.worker.NativeRunInfo] = new JsonFormat[sbt.internal.worker.NativeRunInfo] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.worker.NativeRunInfo = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val cmd = unbuilder.readField[String]("cmd") + val args = unbuilder.readField[Vector[String]]("args") + val connectInput = unbuilder.readField[Boolean]("connectInput") + val outputStrategy = unbuilder.readField[Option[String]]("outputStrategy") + val workingDirectory = unbuilder.readField[Option[java.net.URI]]("workingDirectory") + val environmentVariables = unbuilder.readField[scala.collection.immutable.Map[String, String]]("environmentVariables") + val inputs = unbuilder.readField[Vector[sbt.internal.worker.FilePath]]("inputs") + val outputs = unbuilder.readField[Vector[sbt.internal.worker.FilePath]]("outputs") + unbuilder.endObject() + sbt.internal.worker.NativeRunInfo(cmd, args, connectInput, outputStrategy, workingDirectory, environmentVariables, inputs, outputs) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.worker.NativeRunInfo, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("cmd", obj.cmd) + builder.addField("args", obj.args) + builder.addField("connectInput", obj.connectInput) + builder.addField("outputStrategy", obj.outputStrategy) + builder.addField("workingDirectory", obj.workingDirectory) + builder.addField("environmentVariables", obj.environmentVariables) + builder.addField("inputs", obj.inputs) + builder.addField("outputs", obj.outputs) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/internal/worker/codec/RunInfoFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/worker/codec/RunInfoFormats.scala new file mode 100644 index 0000000000..16e66747ea --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/internal/worker/codec/RunInfoFormats.scala @@ -0,0 +1,31 @@ +/** + * This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.internal.worker.codec +import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } +trait RunInfoFormats { self: sbt.internal.worker.codec.JvmRunInfoFormats with sbt.internal.worker.codec.FilePathFormats with sjsonnew.BasicJsonProtocol with sbt.internal.worker.codec.NativeRunInfoFormats => +implicit lazy val RunInfoFormat: JsonFormat[sbt.internal.worker.RunInfo] = new JsonFormat[sbt.internal.worker.RunInfo] { + override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.worker.RunInfo = { + __jsOpt match { + case Some(__js) => + unbuilder.beginObject(__js) + val jvm = unbuilder.readField[Boolean]("jvm") + val jvmRunInfo = unbuilder.readField[Option[sbt.internal.worker.JvmRunInfo]]("jvmRunInfo") + val nativeRunInfo = unbuilder.readField[Option[sbt.internal.worker.NativeRunInfo]]("nativeRunInfo") + unbuilder.endObject() + sbt.internal.worker.RunInfo(jvm, jvmRunInfo, nativeRunInfo) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.internal.worker.RunInfo, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("jvm", obj.jvm) + builder.addField("jvmRunInfo", obj.jvmRunInfo) + builder.addField("nativeRunInfo", obj.nativeRunInfo) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/protocol/InitCommand.scala b/protocol/src/main/contraband-scala/sbt/protocol/InitCommand.scala index ddfe85f45f..26511bc1ee 100644 --- a/protocol/src/main/contraband-scala/sbt/protocol/InitCommand.scala +++ b/protocol/src/main/contraband-scala/sbt/protocol/InitCommand.scala @@ -7,22 +7,24 @@ package sbt.protocol final class InitCommand private ( val token: Option[String], val execId: Option[String], - val skipAnalysis: Option[Boolean]) extends sbt.protocol.CommandMessage() with Serializable { + val skipAnalysis: Option[Boolean], + val initializationOptions: Option[sbt.internal.protocol.InitializeOption]) extends sbt.protocol.CommandMessage() with Serializable { - private def this(token: Option[String], execId: Option[String]) = this(token, execId, None) + private def this(token: Option[String], execId: Option[String]) = this(token, execId, None, None) + private def this(token: Option[String], execId: Option[String], skipAnalysis: Option[Boolean]) = this(token, execId, skipAnalysis, None) override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { - case x: InitCommand => (this.token == x.token) && (this.execId == x.execId) && (this.skipAnalysis == x.skipAnalysis) + case x: InitCommand => (this.token == x.token) && (this.execId == x.execId) && (this.skipAnalysis == x.skipAnalysis) && (this.initializationOptions == x.initializationOptions) case _ => false }) override def hashCode: Int = { - 37 * (37 * (37 * (37 * (17 + "sbt.protocol.InitCommand".##) + token.##) + execId.##) + skipAnalysis.##) + 37 * (37 * (37 * (37 * (37 * (17 + "sbt.protocol.InitCommand".##) + token.##) + execId.##) + skipAnalysis.##) + initializationOptions.##) } override def toString: String = { - "InitCommand(" + token + ", " + execId + ", " + skipAnalysis + ")" + "InitCommand(" + token + ", " + execId + ", " + skipAnalysis + ", " + initializationOptions + ")" } - private[this] def copy(token: Option[String] = token, execId: Option[String] = execId, skipAnalysis: Option[Boolean] = skipAnalysis): InitCommand = { - new InitCommand(token, execId, skipAnalysis) + private[this] def copy(token: Option[String] = token, execId: Option[String] = execId, skipAnalysis: Option[Boolean] = skipAnalysis, initializationOptions: Option[sbt.internal.protocol.InitializeOption] = initializationOptions): InitCommand = { + new InitCommand(token, execId, skipAnalysis, initializationOptions) } def withToken(token: Option[String]): InitCommand = { copy(token = token) @@ -42,6 +44,12 @@ final class InitCommand private ( def withSkipAnalysis(skipAnalysis: Boolean): InitCommand = { copy(skipAnalysis = Option(skipAnalysis)) } + def withInitializationOptions(initializationOptions: Option[sbt.internal.protocol.InitializeOption]): InitCommand = { + copy(initializationOptions = initializationOptions) + } + def withInitializationOptions(initializationOptions: sbt.internal.protocol.InitializeOption): InitCommand = { + copy(initializationOptions = Option(initializationOptions)) + } } object InitCommand { @@ -49,4 +57,6 @@ object InitCommand { def apply(token: String, execId: String): InitCommand = new InitCommand(Option(token), Option(execId)) def apply(token: Option[String], execId: Option[String], skipAnalysis: Option[Boolean]): InitCommand = new InitCommand(token, execId, skipAnalysis) def apply(token: String, execId: String, skipAnalysis: Boolean): InitCommand = new InitCommand(Option(token), Option(execId), Option(skipAnalysis)) + def apply(token: Option[String], execId: Option[String], skipAnalysis: Option[Boolean], initializationOptions: Option[sbt.internal.protocol.InitializeOption]): InitCommand = new InitCommand(token, execId, skipAnalysis, initializationOptions) + def apply(token: String, execId: String, skipAnalysis: Boolean, initializationOptions: sbt.internal.protocol.InitializeOption): InitCommand = new InitCommand(Option(token), Option(execId), Option(skipAnalysis), Option(initializationOptions)) } diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/CommandMessageFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/CommandMessageFormats.scala index 6f95b6f48a..a705d1b488 100644 --- a/protocol/src/main/contraband-scala/sbt/protocol/codec/CommandMessageFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/CommandMessageFormats.scala @@ -6,6 +6,6 @@ package sbt.protocol.codec import _root_.sjsonnew.JsonFormat -trait CommandMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.InitCommandFormats with sbt.protocol.codec.ExecCommandFormats with sbt.protocol.codec.SettingQueryFormats with sbt.protocol.codec.AttachFormats with sbt.protocol.codec.TerminalCapabilitiesQueryFormats with sbt.protocol.codec.TerminalSetAttributesCommandFormats with sbt.protocol.codec.TerminalAttributesQueryFormats with sbt.protocol.codec.TerminalGetSizeQueryFormats with sbt.protocol.codec.TerminalSetSizeCommandFormats with sbt.protocol.codec.TerminalSetEchoCommandFormats with sbt.protocol.codec.TerminalSetRawModeCommandFormats => +trait CommandMessageFormats { self: sbt.internal.protocol.codec.InitializeOptionFormats with sjsonnew.BasicJsonProtocol with sbt.protocol.codec.InitCommandFormats with sbt.protocol.codec.ExecCommandFormats with sbt.protocol.codec.SettingQueryFormats with sbt.protocol.codec.AttachFormats with sbt.protocol.codec.TerminalCapabilitiesQueryFormats with sbt.protocol.codec.TerminalSetAttributesCommandFormats with sbt.protocol.codec.TerminalAttributesQueryFormats with sbt.protocol.codec.TerminalGetSizeQueryFormats with sbt.protocol.codec.TerminalSetSizeCommandFormats with sbt.protocol.codec.TerminalSetEchoCommandFormats with sbt.protocol.codec.TerminalSetRawModeCommandFormats => implicit lazy val CommandMessageFormat: JsonFormat[sbt.protocol.CommandMessage] = flatUnionFormat11[sbt.protocol.CommandMessage, sbt.protocol.InitCommand, sbt.protocol.ExecCommand, sbt.protocol.SettingQuery, sbt.protocol.Attach, sbt.protocol.TerminalCapabilitiesQuery, sbt.protocol.TerminalSetAttributesCommand, sbt.protocol.TerminalAttributesQuery, sbt.protocol.TerminalGetSizeQuery, sbt.protocol.TerminalSetSizeCommand, sbt.protocol.TerminalSetEchoCommand, sbt.protocol.TerminalSetRawModeCommand]("type") } diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/InitCommandFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/InitCommandFormats.scala index 827b6dc7c0..7d552b17b3 100644 --- a/protocol/src/main/contraband-scala/sbt/protocol/codec/InitCommandFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/InitCommandFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.protocol.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait InitCommandFormats { self: sjsonnew.BasicJsonProtocol => +trait InitCommandFormats { self: sbt.internal.protocol.codec.InitializeOptionFormats with sjsonnew.BasicJsonProtocol => implicit lazy val InitCommandFormat: JsonFormat[sbt.protocol.InitCommand] = new JsonFormat[sbt.protocol.InitCommand] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.InitCommand = { __jsOpt match { @@ -14,8 +14,9 @@ implicit lazy val InitCommandFormat: JsonFormat[sbt.protocol.InitCommand] = new val token = unbuilder.readField[Option[String]]("token") val execId = unbuilder.readField[Option[String]]("execId") val skipAnalysis = unbuilder.readField[Option[Boolean]]("skipAnalysis") + val initializationOptions = unbuilder.readField[Option[sbt.internal.protocol.InitializeOption]]("initializationOptions") unbuilder.endObject() - sbt.protocol.InitCommand(token, execId, skipAnalysis) + sbt.protocol.InitCommand(token, execId, skipAnalysis, initializationOptions) case None => deserializationError("Expected JsObject but found None") } @@ -25,6 +26,7 @@ implicit lazy val InitCommandFormat: JsonFormat[sbt.protocol.InitCommand] = new builder.addField("token", obj.token) builder.addField("execId", obj.execId) builder.addField("skipAnalysis", obj.skipAnalysis) + builder.addField("initializationOptions", obj.initializationOptions) builder.endObject() } } diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/JsonProtocol.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/JsonProtocol.scala index 2df56d1ad3..32852fe440 100644 --- a/protocol/src/main/contraband-scala/sbt/protocol/codec/JsonProtocol.scala +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/JsonProtocol.scala @@ -4,7 +4,8 @@ // DO NOT EDIT MANUALLY package sbt.protocol.codec -trait JsonProtocol extends sjsonnew.BasicJsonProtocol +trait JsonProtocol extends sbt.internal.protocol.codec.InitializeOptionFormats + with sjsonnew.BasicJsonProtocol with sbt.protocol.codec.InitCommandFormats with sbt.protocol.codec.ExecCommandFormats with sbt.protocol.codec.SettingQueryFormats diff --git a/protocol/src/main/contraband/bsp.contra b/protocol/src/main/contraband/bsp.contra index 0331430058..1956cf4404 100644 --- a/protocol/src/main/contraband/bsp.contra +++ b/protocol/src/main/contraband/bsp.contra @@ -530,6 +530,9 @@ type CompileReport { ## The total number of milliseconds it took to compile the target. time: Int + + ## The compilation was a noOp compilation. + noOp: Boolean } ## Test Request @@ -602,6 +605,17 @@ type RunResult { } +# JVM extension + +## Contains jvm-specific metadata, specifically JDK reference +type JvmBuildTarget { + ## Uri representing absolute path to jdk + javaHome: java.net.URI + + ## The java version this target is supposed to use (can be set using javac `-target` flag) + javaVersion: String +} + # Scala Extension ## Contains scala-specific metadata for compiling a target containing Scala sources. @@ -623,31 +637,9 @@ type ScalaBuildTarget { ## A sequence of Scala jars such as scala-library, scala-compiler and scala-reflect. jars: [String]! -} - -# sbt Extension - -## Contains sbt-specific metadata for providing editor support for sbt build files. -## This metadata is embedded in the data: Option[Json] field of the BuildTarget definition -## when the dataKind field contains "sbt". -type SbtBuildTarget { - ## The sbt version. Useful to support version-dependent syntax. - sbtVersion: String! - ## A sequence of Scala imports that are automatically imported in the sbt build files. - autoImports: [String]! - - ## The Scala build target describing the scala - ## version and scala jars used by this sbt version. - scalaBuildTarget: sbt.internal.bsp.ScalaBuildTarget! - - ## An optional parent if the target has an sbt meta project. - parent: sbt.internal.bsp.BuildTargetIdentifier - - ## The inverse of parent, list of targets that have this build target - ## defined as their parent. It can contain normal project targets or - ## sbt build targets if this target represents an sbt meta-meta build. - children: [sbt.internal.bsp.BuildTargetIdentifier]! + ## The jvm build target describing jdk to be used + jvmBuildTarget: sbt.internal.bsp.JvmBuildTarget } ## Scalac options @@ -678,6 +670,61 @@ type ScalacOptionsItem { classDirectory: java.net.URI } +# Java Extension + +## Javac options +## The build target javac options request is sent from the client to the server +## to query for the list of compiler options necessary to compile in a given list of targets. +type JavacOptionsParams { + targets: [sbt.internal.bsp.BuildTargetIdentifier] +} + +type JavacOptionsResult { + items: [sbt.internal.bsp.JavacOptionsItem] +} + +type JavacOptionsItem { + target: sbt.internal.bsp.BuildTargetIdentifier! + + ## Additional arguments to the compiler. + ## For example, -deprecation. + options: [String] + + ## The dependency classpath for this target, must be + ## identical to what is passed as arguments to + ## the -classpath flag in the command line interface + ## of scalac. + classpath: [java.net.URI] + + ## The output directory for classfiles produced by this target + classDirectory: java.net.URI +} + +# sbt Extension + +## Contains sbt-specific metadata for providing editor support for sbt build files. +## This metadata is embedded in the data: Option[Json] field of the BuildTarget definition +## when the dataKind field contains "sbt". +type SbtBuildTarget { + ## The sbt version. Useful to support version-dependent syntax. + sbtVersion: String! + + ## A sequence of Scala imports that are automatically imported in the sbt build files. + autoImports: [String]! + + ## The Scala build target describing the scala + ## version and scala jars used by this sbt version. + scalaBuildTarget: sbt.internal.bsp.ScalaBuildTarget! + + ## An optional parent if the target has an sbt meta project. + parent: sbt.internal.bsp.BuildTargetIdentifier + + ## The inverse of parent, list of targets that have this build target + ## defined as their parent. It can contain normal project targets or + ## sbt build targets if this target represents an sbt meta-meta build. + children: [sbt.internal.bsp.BuildTargetIdentifier]! +} + ## https://build-server-protocol.github.io/docs/server-discovery.html type BspConnectionDetails { ## The name of the build tool diff --git a/protocol/src/main/contraband/portfile.contra b/protocol/src/main/contraband/portfile.contra index 2e138c3159..ffd5dc6c9a 100644 --- a/protocol/src/main/contraband/portfile.contra +++ b/protocol/src/main/contraband/portfile.contra @@ -16,7 +16,10 @@ type TokenFile { token: String! } +## Passed into InitializeParams as part of "initialize" request as the user-defined option. +## https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize type InitializeOption { token: String skipAnalysis: Boolean @since("1.4.0") + canWork: Boolean @since("1.10.8") } diff --git a/protocol/src/main/contraband/server.contra b/protocol/src/main/contraband/server.contra index 18ec0a0d2f..176db450a7 100644 --- a/protocol/src/main/contraband/server.contra +++ b/protocol/src/main/contraband/server.contra @@ -11,6 +11,7 @@ type InitCommand implements CommandMessage { token: String execId: String skipAnalysis: Boolean @since("1.4.0") + initializationOptions: sbt.internal.protocol.InitializeOption @since("1.10.8") } ## Command to execute sbt command. diff --git a/protocol/src/main/contraband/worker.contra b/protocol/src/main/contraband/worker.contra new file mode 100644 index 0000000000..45a68eb741 --- /dev/null +++ b/protocol/src/main/contraband/worker.contra @@ -0,0 +1,51 @@ +package sbt.internal.worker +@target(Scala) +@codecPackage("sbt.internal.worker.codec") +@fullCodec("JsonProtocol") + +type FilePath { + path: java.net.URI! + digest: String! +} + +type JvmRunInfo { + args: [String], + classpath: [sbt.internal.worker.FilePath], + mainClass: String! + connectInput: Boolean! + javaHome: java.net.URI + outputStrategy: String + workingDirectory: java.net.URI + jvmOptions: [String] + environmentVariables: StringStringMap! + inputs: [sbt.internal.worker.FilePath] @since("0.1.0"), + outputs: [sbt.internal.worker.FilePath] @since("0.1.0"), +} + +type NativeRunInfo { + cmd: String!, + args: [String], + connectInput: Boolean! + outputStrategy: String + workingDirectory: java.net.URI + environmentVariables: StringStringMap! + inputs: [sbt.internal.worker.FilePath] @since("0.1.0"), + outputs: [sbt.internal.worker.FilePath] @since("0.1.0"), +} + +type RunInfo { + jvm: Boolean! + jvmRunInfo: sbt.internal.worker.JvmRunInfo, + nativeRunInfo: sbt.internal.worker.NativeRunInfo @since("0.1.0"), +} + +## Client-side job support. +## +## Notification: sbt/clientJob +## +## Parameter for the sbt/clientJob notification. +## A client-side job represents a unit of work that sbt server +## can outsourse back to the client, for example for run task. +type ClientJobParams { + runInfo: sbt.internal.worker.RunInfo +} diff --git a/protocol/src/main/scala/sbt/internal/bsp/BuildServerConnection.scala b/protocol/src/main/scala/sbt/internal/bsp/BuildServerConnection.scala index e03b67eae5..ba9042dde8 100644 --- a/protocol/src/main/scala/sbt/internal/bsp/BuildServerConnection.scala +++ b/protocol/src/main/scala/sbt/internal/bsp/BuildServerConnection.scala @@ -9,6 +9,7 @@ package sbt.internal.bsp import sbt.internal.bsp.codec.JsonProtocol.BspConnectionDetailsFormat +import sbt.internal.util.Util import sbt.io.IO import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter } @@ -25,7 +26,7 @@ object BuildServerConnection { private[sbt] def writeConnectionFile(sbtVersion: String, baseDir: File): Unit = { val bspConnectionFile = new File(baseDir, ".bsp/sbt.json") - val javaHome = System.getProperty("java.home") + val javaHome = Util.javaHome val classPath = System.getProperty("java.class.path") val sbtScript = Option(System.getProperty("sbt.script")) diff --git a/protocol/src/main/scala/sbt/protocol/Serialization.scala b/protocol/src/main/scala/sbt/protocol/Serialization.scala index 2dcd5ae981..1384fb4f24 100644 --- a/protocol/src/main/scala/sbt/protocol/Serialization.scala +++ b/protocol/src/main/scala/sbt/protocol/Serialization.scala @@ -27,6 +27,7 @@ object Serialization { private[sbt] val VsCode = "application/vscode-jsonrpc; charset=utf-8" val readSystemIn = "sbt/readSystemIn" val cancelReadSystemIn = "sbt/cancelReadSystemIn" + val clientJob = "sbt/clientJob" val systemIn = "sbt/systemIn" val systemOut = "sbt/systemOut" val systemErr = "sbt/systemErr" @@ -67,15 +68,10 @@ object Serialization { command match { case x: InitCommand => val execId = x.execId.getOrElse(UUID.randomUUID.toString) - val analysis = s""""skipAnalysis" : ${x.skipAnalysis.getOrElse(false)}""" - val opt = x.token match { - case Some(t) => - val json: JValue = Converter.toJson[String](t).get - val v = CompactPrinter(json) - s"""{ "token": $v, $analysis }""" - case None => s"{ $analysis }" - } - s"""{ "jsonrpc": "2.0", "id": "$execId", "method": "initialize", "params": { "initializationOptions": $opt } }""" + val opts = x.initializationOptions.getOrElse(sys.error("expected initializationOptions")) + import sbt.protocol.codec.JsonProtocol._ + val optsJson = CompactPrinter(Converter.toJson(opts).get) + s"""{ "jsonrpc": "2.0", "id": "$execId", "method": "initialize", "params": { "initializationOptions": $optsJson } }""" case x: ExecCommand => val execId = x.execId.getOrElse(UUID.randomUUID.toString) val json: JValue = Converter.toJson[String](x.commandLine).get diff --git a/run/src/main/scala/sbt/Fork.scala b/run/src/main/scala/sbt/Fork.scala index 3335f53f33..07fc33924b 100644 --- a/run/src/main/scala/sbt/Fork.scala +++ b/run/src/main/scala/sbt/Fork.scala @@ -33,15 +33,8 @@ 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 = { - val p = fork(config, arguments) - RunningProcesses.add(p) - try p.exitValue() - finally { - if (p.isAlive()) p.destroy() - RunningProcesses.remove(p) - } - } + def apply(config: ForkOptions, arguments: Seq[String]): Int = + Fork.blockForExitCode(fork(config, arguments)) /** * Forks the configured process and returns a `Process` that can be used to wait for completion or to terminate the forked process. @@ -50,37 +43,22 @@ final class Fork(val commandName: String, val runnerClass: Option[String]) { * If `runnerClass` is defined for this Fork instance, it is prepended to `arguments` to define the arguments passed to the forked command. */ def fork(config: ForkOptions, arguments: Seq[String]): Process = { - import config.{ envVars => env, _ } + import config._ val executable = Fork.javaCommand(javaHome, commandName).getAbsolutePath val preOptions = makeOptions(runJVMOptions, bootJars, arguments) val (classpathEnv, options) = Fork.fitClasspath(preOptions) val command = executable +: options - - val environment: List[(String, String)] = env.toList ++ - (classpathEnv map { value => - Fork.ClasspathEnvKey -> value - }) val jpb = if (Fork.shouldUseArgumentsFile(options)) new JProcessBuilder(executable, Fork.createArgumentsFile(options)) else new JProcessBuilder(command.toArray: _*) - workingDirectory foreach (jpb directory _) - environment foreach { case (k, v) => jpb.environment.put(k, v) } - if (connectInput) { - jpb.redirectInput(Redirect.INHERIT) - () - } - val process = Process(jpb) - - outputStrategy.getOrElse(StdoutOutput: OutputStrategy) match { - case StdoutOutput => process.run(connectInput = false) - case out: BufferedOutput => - out.logger.buffer { process.run(out.logger, connectInput = false) } - case out: LoggedOutput => process.run(out.logger, connectInput = false) - case out: CustomOutput => (process #> out.output).run(connectInput = false) + val extraEnv = classpathEnv.toList.map { value => + Fork.ClasspathEnvKey -> value } + Fork.forkInternal(config, extraEnv, jpb) } + private[this] def makeOptions( jvmOptions: Seq[String], bootJars: Iterable[File], @@ -185,4 +163,36 @@ object Fork { pw.close() s"@${file.getAbsolutePath}" } + + private[sbt] def forkInternal( + config: ForkOptions, + extraEnv: List[(String, String)], + jpb: JProcessBuilder + ): Process = { + import config.{ envVars => env, _ } + val environment: List[(String, String)] = env.toList ++ extraEnv + workingDirectory.foreach(jpb directory _) + environment.foreach { case (k, v) => jpb.environment.put(k, v) } + if (connectInput) { + jpb.redirectInput(Redirect.INHERIT) + () + } + val process = Process(jpb) + outputStrategy.getOrElse(StdoutOutput: OutputStrategy) match { + case StdoutOutput => process.run(connectInput = false) + case out: BufferedOutput => + out.logger.buffer { process.run(out.logger, connectInput = false) } + case out: LoggedOutput => process.run(out.logger, connectInput = false) + case out: CustomOutput => (process #> out.output).run(connectInput = false) + } + } + + private[sbt] def blockForExitCode(p: Process): Int = { + RunningProcesses.add(p) + try p.exitValue() + finally { + if (p.isAlive()) p.destroy() + RunningProcesses.remove(p) + } + } } diff --git a/run/src/main/scala/sbt/Run.scala b/run/src/main/scala/sbt/Run.scala index 9e46f2b46c..cda9e58884 100644 --- a/run/src/main/scala/sbt/Run.scala +++ b/run/src/main/scala/sbt/Run.scala @@ -26,25 +26,16 @@ sealed trait ScalaRun { } class ForkRun(config: ForkOptions) extends ScalaRun { def run(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Try[Unit] = { - def processExitCode(exitCode: Int, label: String): Try[Unit] = - if (exitCode == 0) Success(()) - else - Failure( - new MessageOnlyException( - s"""Nonzero exit code returned from $label: $exitCode""".stripMargin - ) - ) - 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.") + log.warn("run canceled") 1 } - processExitCode(exitCode, "runner") + Run.processExitCode(exitCode, "runner") } def fork(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Process = { @@ -58,7 +49,7 @@ class ForkRun(config: ForkOptions) extends ScalaRun { } private def configLogged(log: Logger): ForkOptions = { - if (config.outputStrategy.isDefined) config + if (config.outputStrategy.isDefined || config.connectInput) config else config.withOutputStrategy(OutputStrategy.LoggedOutput(log)) } @@ -195,4 +186,13 @@ object Run { case str if str.contains(" ") => "\"" + str + "\"" case str => str }).mkString(" ") + + private[sbt] def processExitCode(exitCode: Int, label: String): Try[Unit] = + if (exitCode == 0) Success(()) + else + Failure( + new MessageOnlyException( + s"""nonzero exit code returned from $label: $exitCode""".stripMargin + ) + ) } diff --git a/sbt b/sbt index 01bdeb220d..46894c7b61 100755 --- a/sbt +++ b/sbt @@ -1,7 +1,7 @@ #!/usr/bin/env bash set +e -declare builtin_sbt_version="1.9.6" +declare builtin_sbt_version="1.11.2" declare -a residual_args declare -a java_args declare -a scalac_args @@ -24,7 +24,9 @@ declare build_props_sbt_version= declare use_sbtn= declare no_server= declare sbtn_command="$SBTN_CMD" -declare sbtn_version="1.9.0" +declare sbtn_version="1.10.8" +declare use_colors=1 +declare is_this_dir_sbt="" ### ------------------------------- ### ### Helper methods for BASH scripts ### @@ -89,6 +91,14 @@ cygwinpath() { fi } +# Trim leading and trailing spaces from a string. +# Echos the new trimmed string. +trimString() { + local inputStr="$*" + local modStr="${inputStr#"${inputStr%%[![:space:]]*}"}" + modStr="${modStr%"${modStr##*[![:space:]]}"}" + echo "$modStr" +} declare -r sbt_bin_dir="$(dirname "$(realpathish "$0")")" declare -r sbt_home="$(dirname "$sbt_bin_dir")" @@ -96,6 +106,15 @@ declare -r sbt_home="$(dirname "$sbt_bin_dir")" echoerr () { echo 1>&2 "$@" } +RED='\033[0;31m' +NC='\033[0m' # No Color +echoerr_error () { + if [[ $use_colors == "1" ]]; then + echoerr -e "[${RED}error${NC}] $@" + else + echoerr "[error] $@" + fi +} vlog () { [[ $sbt_verbose || $sbt_debug ]] && echoerr "$@" } @@ -142,7 +161,7 @@ acquire_sbt_jar () { sbt_jar="$download_jar" else sbt_url=$(jar_url "$launcher_sv") - echoerr "downloading sbt launcher $launcher_sv" + dlog "downloading sbt launcher $launcher_sv" download_url "$sbt_url" "${download_jar}.temp" download_url "${sbt_url}.sha1" "${download_jar}.sha1" if command -v shasum > /dev/null; then @@ -172,31 +191,32 @@ acquire_sbtn () { local archive_target= local url= local arch="x86_64" - if [[ "$OSTYPE" == "linux-gnu"* ]]; then + if [[ "$OSTYPE" == "linux"* ]]; then arch=$(uname -m) if [[ "$arch" == "aarch64" ]] || [[ "$arch" == "x86_64" ]]; then archive_target="$p/sbtn-${arch}-pc-linux-${sbtn_v}.tar.gz" url="https://github.com/sbt/sbtn-dist/releases/download/v${sbtn_v}/sbtn-${arch}-pc-linux-${sbtn_v}.tar.gz" else - echoerr "sbtn is not supported on $arch" + echoerr_error "sbtn is not supported on $arch" exit 2 fi elif [[ "$OSTYPE" == "darwin"* ]]; then - archive_target="$p/sbtn-x86_64-apple-darwin-${sbtn_v}.tar.gz" - url="https://github.com/sbt/sbtn-dist/releases/download/v${sbtn_v}/sbtn-x86_64-apple-darwin-${sbtn_v}.tar.gz" + arch="universal" + archive_target="$p/sbtn-universal-apple-darwin-${sbtn_v}.tar.gz" + url="https://github.com/sbt/sbtn-dist/releases/download/v${sbtn_v}/sbtn-universal-apple-darwin-${sbtn_v}.tar.gz" elif [[ "$OSTYPE" == "cygwin" ]] || [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then target="$p/sbtn.exe" archive_target="$p/sbtn-x86_64-pc-win32-${sbtn_v}.zip" url="https://github.com/sbt/sbtn-dist/releases/download/v${sbtn_v}/sbtn-x86_64-pc-win32-${sbtn_v}.zip" else - echoerr "sbtn is not supported on $OSTYPE" + echoerr_error "sbtn is not supported on $OSTYPE" exit 2 fi if [[ -f "$target" ]]; then sbtn_command="$target" else - echoerr "downloading sbtn ${sbtn_v} for ${arch}" + dlog "downloading sbtn ${sbtn_v} for ${arch}" download_url "$url" "$archive_target" if [[ "$OSTYPE" == "linux-gnu"* ]] || [[ "$OSTYPE" == "darwin"* ]]; then tar zxf "$archive_target" --directory "$p" @@ -331,7 +351,7 @@ require_arg () { local opt="$2" local arg="$3" if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then - echo "$opt requires <$type> argument" + echoerr "$opt requires <$type> argument" exit 1 fi } @@ -435,19 +455,19 @@ checkJava() { # Now check to see if it's a good enough version local good_enough="$(expr $java_version ">=" $required_version)" if [[ "$java_version" == "" ]]; then - echo - echo "No Java Development Kit (JDK) installation was detected." - echo Please go to http://www.oracle.com/technetwork/java/javase/downloads/ and download. - echo + echoerr + echoerr "No Java Development Kit (JDK) installation was detected." + echoerr Go to https://adoptium.net/ etc and download. + echoerr exit 1 elif [[ "$good_enough" != "1" ]]; then - echo - echo "The Java Development Kit (JDK) installation you have is not up to date." - echo $script_name requires at least version $required_version+, you have - echo version $java_version - echo - echo Please go to http://www.oracle.com/technetwork/java/javase/downloads/ and download - echo a valid JDK and install before running $script_name. + echoerr + echoerr "The Java Development Kit (JDK) installation you have is not up to date." + echoerr $script_name requires at least version $required_version+, you have + echoerr version $java_version + echoerr + echoerr Go to https://adoptium.net/ etc and download + echoerr a valid JDK and install before running $script_name. echo exit 1 fi @@ -462,7 +482,6 @@ copyRt() { java9_rt=$(echo "$java9_ext/rt.jar") vlog "[copyRt] java9_rt = '$java9_rt'" if [[ ! -f "$java9_rt" ]]; then - echo copying runtime jar... mkdir -p "$java9_ext" "$java_cmd" \ "${sbt_options[@]}" \ @@ -475,6 +494,27 @@ copyRt() { fi } +detect_working_directory() { + if [[ -f ./build.sbt || -f ./project/build.properties ]]; then + is_this_dir_sbt=1 + fi +} + +# Confirm a user's intent if the current directory does not look like an sbt +# top-level directory and neither the --allow-empty option nor the "new" command was given. +checkWorkingDirectory() { + if [[ ! -n "$allow_empty" ]]; then + [[ -n "$is_this_dir_sbt" || -n "$sbt_new" ]] || { + echoerr_error "Neither build.sbt nor a 'project' directory in the current directory: $(pwd)" + echoerr_error "run 'sbt new', touch build.sbt, or run 'sbt --allow-empty'." + echoerr_error "" + echoerr_error "To opt out of this check, create ${config_home}/sbtopts with:" + echoerr_error "--allow-empty" + exit 1 + } + fi +} + run() { # Copy preloaded repo to user's preloaded directory syncPreloaded @@ -485,7 +525,7 @@ run() { } # TODO - java check should be configurable... - checkJava "6" + checkJava "8" # Java 9 support copyRt @@ -497,24 +537,32 @@ run() { addJava "-Dsbt.cygwin=true" fi + detect_working_directory if [[ $print_sbt_version ]]; then execRunner "$java_cmd" -jar "$sbt_jar" "sbtVersion" | tail -1 | sed -e 's/\[info\]//g' elif [[ $print_sbt_script_version ]]; then echo "$init_sbt_version" 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" + if [[ -n "$is_this_dir_sbt" ]]; then + execRunner "$java_cmd" -jar "$sbt_jar" "sbtVersion" | tail -1 | sed -e 's/\[info\]/sbt version in this project:/g' + fi + echo "sbt runner version: $init_sbt_version" + echoerr "" + echoerr "[info] sbt runner (sbt-the-shell-script) is a runner to run any declared version of sbt." + echoerr "[info] Actual version of the sbt is declared using project/build.properties for each build." 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" + echoerr "shutdown ${#sbt_processes[@]} sbt processes" else + checkWorkingDirectory # run sbt execRunner "$java_cmd" \ "${java_args[@]}" \ "${sbt_options[@]}" \ + "${java_tool_options[@]}" \ -jar "$sbt_jar" \ "${sbt_commands[@]}" \ "${residual_args[@]}" @@ -534,7 +582,10 @@ declare -r sbt_opts_file=".sbtopts" declare -r build_props_file="$(pwd)/project/build.properties" declare -r etc_sbt_opts_file="/etc/sbt/sbtopts" # this allows /etc/sbt/sbtopts location to be changed -declare -r etc_file="${SBT_ETC_FILE:-$etc_sbt_opts_file}" +declare machine_sbt_opts_file="${etc_sbt_opts_file}" +declare config_home="${XDG_CONFIG_HOME:-$HOME/.config}/sbt" +[[ -f "${config_home}/sbtopts" ]] && machine_sbt_opts_file="${config_home}/sbtopts" +[[ -f "$SBT_ETC_FILE" ]] && machine_sbt_opts_file="$SBT_ETC_FILE" declare -r dist_sbt_opts_file="${sbt_home}/conf/sbtopts" declare -r win_sbt_opts_file="${sbt_home}/conf/sbtconfig.txt" declare sbt_jar="$(jar_file)" @@ -559,7 +610,7 @@ Usage: `basename "$0"` [options] enable or disable supershell (sbt 1.3 and above) --traces generate Trace Event report on shutdown (sbt 1.3 and above) --timings display task timings report on shutdown - --sbt-create start sbt even if current directory contains no sbt project + --allow-empty start sbt even if current directory contains no sbt project --sbt-dir path to global settings/plugins directory (default: ~/.sbt) --sbt-boot path to shared boot directory (default: ~/.sbt/boot in 0.11 series) --sbt-cache path to global cache directory (default: operating system specific) @@ -598,9 +649,9 @@ process_my_args () { case "$1" in -batch|--batch) exec - -sbt-create|--sbt-create) sbt_create=true && shift ;; + -allow-empty|--allow-empty|-sbt-create|--sbt-create) allow_empty=true && shift ;; - new) sbt_new=true && addResidual "$1" && shift ;; + new|init) sbt_new=true && addResidual "$1" && shift ;; *) addResidual "$1" && shift ;; esac @@ -608,23 +659,6 @@ process_my_args () { # Now, ensure sbt version is used. [[ "${sbt_version}XXX" != "XXX" ]] && addJava "-Dsbt.version=$sbt_version" - - # Confirm a user's intent if the current directory does not look like an sbt - # top-level directory and neither the -sbt-create option nor the "new" - # command was given. - [[ -f ./build.sbt || -d ./project || -n "$sbt_create" || -n "$sbt_new" ]] || { - echo "[warn] Neither build.sbt nor a 'project' directory in the current directory: $(pwd)" - while true; do - echo 'c) continue' - echo 'q) quit' - - read -p '? ' || exit 1 - case "$REPLY" in - c|C) break ;; - q|Q) exit 1 ;; - esac - done - } } ## map over argument array. this is used to process both command line arguments and SBT_OPTS @@ -685,6 +719,7 @@ process_args () { export PATH="$2/bin:$PATH" && shift 2 ;; + -Dsbt.color=never|-Dsbt.log.noformat=true) addJava "$1" && use_colors=0 && shift ;; "-D*"|-D*) addJava "$1" && shift ;; -J*) addJava "${1:2}" && shift ;; *) addResidual "$1" && shift ;; @@ -706,6 +741,9 @@ loadConfigFile() { } loadPropFile() { + # trim key and value so as to be more forgiving with spaces around the '=': + k=$(trimString $k) + v=$(trimString $v) while IFS='=' read -r k v; do if [[ "$k" == "sbt.version" ]]; then build_props_sbt_version="$v" @@ -737,7 +775,14 @@ isRunNativeClient() { [[ "$sbtV" == "" ]] && sbtV="0.0.0" sbtBinaryV_1=$(echo "$sbtV" | sed 's/^\([0-9]*\)\.\([0-9]*\).*$/\1/') sbtBinaryV_2=$(echo "$sbtV" | sed 's/^\([0-9]*\)\.\([0-9]*\).*$/\2/') - if (( $sbtBinaryV_1 >= 2 )) || ( (( $sbtBinaryV_1 >= 1 )) && (( $sbtBinaryV_2 >= 4 )) ); then + # Default to true for sbt 2.x + if (( $sbtBinaryV_1 >= 2 )); then + if [[ "$use_sbtn" == "0" ]]; then + echo "false" + else + echo "true" + fi + elif ( (( $sbtBinaryV_1 >= 1 )) && (( $sbtBinaryV_2 >= 4 )) ); then if [[ "$use_sbtn" == "1" ]]; then echo "true" else @@ -759,18 +804,25 @@ runNativeClient() { unset 'original_args[i]' fi done - sbt_script=$0 + + if [[ "$OSTYPE" == "cygwin" ]] || [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then + sbt_script="$0.bat" + else + sbt_script="$0" + fi sbt_script=${sbt_script/ /%20} execRunner "$sbtn_command" "--sbt-script=$sbt_script" "${original_args[@]}" } original_args=("$@") -# Here we pull in the default settings configuration. -[[ -f "$dist_sbt_opts_file" ]] && set -- $(loadConfigFile "$dist_sbt_opts_file") "$@" - -# Here we pull in the global settings configuration. -[[ -f "$etc_file" ]] && set -- $(loadConfigFile "$etc_file") "$@" +# Pull in the machine-wide settings configuration. +if [[ -f "$machine_sbt_opts_file" ]]; then + set -- $(loadConfigFile "$machine_sbt_opts_file") "$@" +else + # Otherwise pull in the default settings configuration. + [[ -f "$dist_sbt_opts_file" ]] && set -- $(loadConfigFile "$dist_sbt_opts_file") "$@" +fi # Pull in the project-level config file, if it exists. [[ -f "$sbt_opts_file" ]] && set -- $(loadConfigFile "$sbt_opts_file") "$@" @@ -785,6 +837,7 @@ original_args=("$@") java_args=($JAVA_OPTS) sbt_options0=(${SBT_OPTS:-$default_sbt_opts}) +java_tool_options=($JAVA_TOOL_OPTIONS) if [[ "$SBT_NATIVE_CLIENT" == "true" ]]; then use_sbtn=1 fi diff --git a/sbt-app/src/sbt-test/actions/add-alias/test b/sbt-app/src/sbt-test/actions/add-alias/test index 0269ab4f88..9a7db37822 100644 --- a/sbt-app/src/sbt-test/actions/add-alias/test +++ b/sbt-app/src/sbt-test/actions/add-alias/test @@ -2,3 +2,5 @@ -> demo-failure > +z > z + +$ absent /tmp/non-existent diff --git a/sbt-app/src/sbt-test/actions/clean-managed/test b/sbt-app/src/sbt-test/actions/clean-managed/test index f6fd6ce8f8..5023111888 100644 --- a/sbt-app/src/sbt-test/actions/clean-managed/test +++ b/sbt-app/src/sbt-test/actions/clean-managed/test @@ -1,6 +1,6 @@ > compile -$ exists target/scala-2.12/src_managed/foo.txt target/scala-2.12/src_managed/bar.txt +$ exists target/**/src_managed/foo.txt target/**/src_managed/bar.txt > clean -$ absent target/scala-2.12/src_managed/foo.txt -$ exists target/scala-2.12/src_managed/bar.txt +$ absent target/**/src_managed/foo.txt +$ exists target/**/src_managed/bar.txt diff --git a/sbt-app/src/sbt-test/actions/compile-clean/test b/sbt-app/src/sbt-test/actions/compile-clean/test index 2e805ffd03..7d983f7cae 100644 --- a/sbt-app/src/sbt-test/actions/compile-clean/test +++ b/sbt-app/src/sbt-test/actions/compile-clean/test @@ -1,22 +1,22 @@ $ touch target/cant-touch-this > Test/compile -$ exists target/scala-2.12/classes/A.class -$ exists target/scala-2.12/test-classes/B.class +$ exists target/**/classes/A.class +$ exists target/**/test-classes/B.class > Test/clean $ exists target/cant-touch-this # it should clean only compile classes -$ exists target/scala-2.12/classes/A.class -$ exists target/scala-2.12/classes/X.class -$ absent target/scala-2.12/test-classes/B.class +$ exists target/**/classes/A.class +$ exists target/**/classes/X.class +$ absent target/**/test-classes/B.class # compiling everything again, but now cleaning only compile classes > Test/compile > Compile/clean $ exists target/cant-touch-this # it should clean only compile classes -$ absent target/scala-2.12/classes/A.class -$ exists target/scala-2.12/test-classes/B.class +$ absent target/**/classes/A.class +$ exists target/**/test-classes/B.class # and X has to be kept, because of the cleanKeepFiles override -$ exists target/scala-2.12/classes/X.class +$ exists target/**/classes/X.class 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 e84586e99c..703f9b26a6 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.18" +lazy val scala212 = "2.12.20" lazy val scala213 = "2.13.12" lazy val root = (project in file(".")) diff --git a/sbt-app/src/sbt-test/actions/cross-advanced/test b/sbt-app/src/sbt-test/actions/cross-advanced/test index 15c90ead43..47912f43c1 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.18", "2.13.12") +> set root/crossScalaVersions := Seq("2.12.20", "2.13.12") > + build $ exists foo/target/scala-2.12 $ exists foo/target/scala-2.13 diff --git a/sbt-app/src/sbt-test/actions/cross-incremental/build.sbt b/sbt-app/src/sbt-test/actions/cross-incremental/build.sbt index 8176b96da6..79b7bdf275 100644 --- a/sbt-app/src/sbt-test/actions/cross-incremental/build.sbt +++ b/sbt-app/src/sbt-test/actions/cross-incremental/build.sbt @@ -1,5 +1,5 @@ -scalaVersion := "2.12.18" -crossScalaVersions := List("2.12.18", "2.13.12") +scalaVersion := "2.12.20" +crossScalaVersions := List("2.12.20", "2.13.12") val setLastModified = taskKey[Unit]("Sets the last modified time for classfiles") setLastModified := { diff --git a/sbt-app/src/sbt-test/actions/cross-multi-parser/build.sbt b/sbt-app/src/sbt-test/actions/cross-multi-parser/build.sbt index 5b20f8f5f8..bb4a1ec0e7 100644 --- a/sbt-app/src/sbt-test/actions/cross-multi-parser/build.sbt +++ b/sbt-app/src/sbt-test/actions/cross-multi-parser/build.sbt @@ -1 +1 @@ -crossScalaVersions := Seq[String]("2.11.12", "2.12.18") +crossScalaVersions := Seq[String]("2.11.12", "2.12.20") diff --git a/sbt-app/src/sbt-test/actions/cross-multi-parser/test b/sbt-app/src/sbt-test/actions/cross-multi-parser/test index cd87f4b85e..0055182917 100644 --- a/sbt-app/src/sbt-test/actions/cross-multi-parser/test +++ b/sbt-app/src/sbt-test/actions/cross-multi-parser/test @@ -1,5 +1,5 @@ > ++2.11.12; compile -> ++ 2.12.18 ; compile; +> ++ 2.12.20 ; compile; -> ++ 2.12.18 ; compile +> ++ 2.12.20 ; compile diff --git a/sbt-app/src/sbt-test/actions/cross-multiproject/build.sbt b/sbt-app/src/sbt-test/actions/cross-multiproject/build.sbt index 1b411f1dbf..4f2d58d77c 100644 --- a/sbt-app/src/sbt-test/actions/cross-multiproject/build.sbt +++ b/sbt-app/src/sbt-test/actions/cross-multiproject/build.sbt @@ -1,11 +1,11 @@ -lazy val scala212 = "2.12.18" +lazy val scala212 = "2.12.20" lazy val scala213 = "2.13.12" ThisBuild / crossScalaVersions := Seq(scala212, scala213) ThisBuild / scalaVersion := scala212 lazy val rootProj = (project in file(".")) - .aggregate(libProj, fooPlugin) + .aggregate(libProj, fooPlugin, externalProj) .settings( crossScalaVersions := Nil, addCommandAlias("build", "compile") @@ -27,3 +27,5 @@ lazy val extrasProj = (project in file("extras")) .settings( name := "foo-extras", ) + +lazy val externalProj = ProjectRef(file("ref"), "external") diff --git a/sbt-app/src/sbt-test/actions/cross-multiproject/lib/A.scala b/sbt-app/src/sbt-test/actions/cross-multiproject/lib/src/main/scala/A.scala similarity index 100% rename from sbt-app/src/sbt-test/actions/cross-multiproject/lib/A.scala rename to sbt-app/src/sbt-test/actions/cross-multiproject/lib/src/main/scala/A.scala diff --git a/sbt-app/src/sbt-test/actions/cross-multiproject/ref/build.sbt b/sbt-app/src/sbt-test/actions/cross-multiproject/ref/build.sbt new file mode 100644 index 0000000000..32936b7d0a --- /dev/null +++ b/sbt-app/src/sbt-test/actions/cross-multiproject/ref/build.sbt @@ -0,0 +1,4 @@ +lazy val external = (project in file(".")) + .settings( + scalaVersion := "2.12.20" + ) diff --git a/sbt-app/src/sbt-test/actions/cross-multiproject/ref/src/main/scala/C.scala b/sbt-app/src/sbt-test/actions/cross-multiproject/ref/src/main/scala/C.scala new file mode 100644 index 0000000000..2fb50c68d4 --- /dev/null +++ b/sbt-app/src/sbt-test/actions/cross-multiproject/ref/src/main/scala/C.scala @@ -0,0 +1,5 @@ +package ref.sbt + +object Ref { + +} \ No newline at end of file diff --git a/sbt-app/src/sbt-test/actions/cross-multiproject/sbt-foo/B.scala b/sbt-app/src/sbt-test/actions/cross-multiproject/sbt-foo/src/main/scala/B.scala similarity index 100% rename from sbt-app/src/sbt-test/actions/cross-multiproject/sbt-foo/B.scala rename to sbt-app/src/sbt-test/actions/cross-multiproject/sbt-foo/src/main/scala/B.scala diff --git a/sbt-app/src/sbt-test/actions/cross-multiproject/test b/sbt-app/src/sbt-test/actions/cross-multiproject/test index 699f1221ce..03a96cb834 100644 --- a/sbt-app/src/sbt-test/actions/cross-multiproject/test +++ b/sbt-app/src/sbt-test/actions/cross-multiproject/test @@ -13,11 +13,19 @@ $ exists lib/target/scala-2.13 # test safe switching > clean -> ++ 2.12.18 -v compile +> ++ 2.12.20 -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 +$ exists ref/target/scala-2.12 +-$ exists ref/target/scala-2.13 + +# test safe switching +> clean +> ++ 2.12.20 -v libProj/compile +$ exists lib/target/scala-2.12 +-$ exists lib/target/scala-2.13 # Test legacy cross build with command support # > clean @@ -29,7 +37,7 @@ $ exists sbt-foo/target/scala-2.12 # Test ++ leaves crossScalaVersions unchanged > clean -> ++2.12.18 +> ++2.12.20 > +extrasProj/compile $ exists extras/target/scala-2.13 $ exists extras/target/scala-2.12 @@ -39,8 +47,11 @@ $ exists extras/target/scala-2.12 > ++ 2.13.12 -v compile $ exists lib/target/scala-2.13 -$ exists lib/target/scala-2.12 -# -$ exists sbt-foo/target/scala-2.12 +# sbt-foo and ref do not cross-build 2.13 +-$ exists sbt-foo/target/scala-2.12 -$ exists sbt-foo/target/scala-2.13 +-$ exists ref/target/scala-2.12 +-$ exists ref/target/scala-2.13 # test wildcard switching (2.12) > clean @@ -49,14 +60,19 @@ $ 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 +$ exists ref/target/scala-2.12 +-$ exists ref/target/scala-2.13 # test wildcard switching (2.13) > clean > ++ 2.13.x -v compile $ exists lib/target/scala-2.13 -$ exists lib/target/scala-2.12 -# -$ exists sbt-foo/target/scala-2.12 +# sbt-foo and ref do not cross-build 2.13 +-$ exists sbt-foo/target/scala-2.12 -$ exists sbt-foo/target/scala-2.13 +-$ exists ref/target/scala-2.12 +-$ exists ref/target/scala-2.13 # test wildcard switching (no matches) -> ++ 3.* diff --git a/sbt-app/src/sbt-test/actions/cross-strict-aggregation-scala-3/build.sbt b/sbt-app/src/sbt-test/actions/cross-strict-aggregation-scala-3/build.sbt index 53d1c72348..6e5d27874a 100644 --- a/sbt-app/src/sbt-test/actions/cross-strict-aggregation-scala-3/build.sbt +++ b/sbt-app/src/sbt-test/actions/cross-strict-aggregation-scala-3/build.sbt @@ -1,14 +1,14 @@ -scalaVersion := "2.12.18" +scalaVersion := "2.12.20" lazy val core = project .settings( - crossScalaVersions := Seq("2.12.18", "3.0.2", "3.1.2") + crossScalaVersions := Seq("2.12.20", "3.3.1", "3.5.1") ) lazy val subproj = project .dependsOn(core) .settings( - crossScalaVersions := Seq("2.12.18", "3.1.2"), + crossScalaVersions := Seq("2.12.20", "3.5.1"), // a random library compiled against Scala 3.1 - libraryDependencies += "org.http4s" %% "http4s-core" % "0.23.12" + libraryDependencies += "org.http4s" %% "http4s-core" % "0.23.28" ) diff --git a/sbt-app/src/sbt-test/actions/cross-strict-aggregation-scala-3/test b/sbt-app/src/sbt-test/actions/cross-strict-aggregation-scala-3/test index ea7afbf93a..9e96b0f495 100644 --- a/sbt-app/src/sbt-test/actions/cross-strict-aggregation-scala-3/test +++ b/sbt-app/src/sbt-test/actions/cross-strict-aggregation-scala-3/test @@ -1,14 +1,14 @@ -> ++3.0.2 compile +> ++3.3.1 compile -$ exists core/target/scala-3.0.2 --$ exists core/target/scala-3.1.2 --$ exists subproj/target/scala-3.0.2 --$ exists subproj/target/scala-3.1.2 +$ exists core/target/scala-3.3.1 +-$ exists core/target/scala-3.5.1 +-$ exists subproj/target/scala-3.3.1 +-$ exists subproj/target/scala-3.5.1 > clean -> ++3.1.2 compile +> ++3.5.1 compile --$ exists core/target/scala-3.0.2 -$ exists core/target/scala-3.1.2 --$ exists subproj/target/scala-3.0.2 -$ exists subproj/target/scala-3.1.2 +-$ exists core/target/scala-3.3.1 +$ exists core/target/scala-3.5.1 +-$ exists subproj/target/scala-3.3.1 +$ exists subproj/target/scala-3.5.1 diff --git a/sbt-app/src/sbt-test/actions/cross-strict-aggregation/build.sbt b/sbt-app/src/sbt-test/actions/cross-strict-aggregation/build.sbt index 7eb67145a0..d5b43e3211 100644 --- a/sbt-app/src/sbt-test/actions/cross-strict-aggregation/build.sbt +++ b/sbt-app/src/sbt-test/actions/cross-strict-aggregation/build.sbt @@ -1,4 +1,4 @@ -lazy val scala212 = "2.12.18" +lazy val scala212 = "2.12.20" lazy val scala213 = "2.13.12" ThisBuild / scalaVersion := scala212 diff --git a/sbt-app/src/sbt-test/actions/cross-strict-aggregation/module/B.scala b/sbt-app/src/sbt-test/actions/cross-strict-aggregation/module/src/main/scala/B.scala similarity index 100% rename from sbt-app/src/sbt-test/actions/cross-strict-aggregation/module/B.scala rename to sbt-app/src/sbt-test/actions/cross-strict-aggregation/module/src/main/scala/B.scala diff --git a/sbt-app/src/sbt-test/actions/doc-file-options/build.sbt b/sbt-app/src/sbt-test/actions/doc-file-options/build.sbt index b79ed67a78..a8b68c48ab 100644 --- a/sbt-app/src/sbt-test/actions/doc-file-options/build.sbt +++ b/sbt-app/src/sbt-test/actions/doc-file-options/build.sbt @@ -2,7 +2,7 @@ val newContents = "bbbbbbbbb" val rootContentFile = "root.txt" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" lazy val root = (project in file(".")) .settings( diff --git a/sbt-app/src/sbt-test/actions/doc-scala3/m3/src/main/scala/foo/A.scala b/sbt-app/src/sbt-test/actions/doc-scala3/a/src/main/scala/foo/A.scala similarity index 100% rename from sbt-app/src/sbt-test/actions/doc-scala3/m3/src/main/scala/foo/A.scala rename to sbt-app/src/sbt-test/actions/doc-scala3/a/src/main/scala/foo/A.scala diff --git a/sbt-app/src/sbt-test/actions/doc-scala3/build.sbt b/sbt-app/src/sbt-test/actions/doc-scala3/build.sbt index 583a736e07..daa9348785 100644 --- a/sbt-app/src/sbt-test/actions/doc-scala3/build.sbt +++ b/sbt-app/src/sbt-test/actions/doc-scala3/build.sbt @@ -1,11 +1,9 @@ -lazy val m3 = (project in file("m3")) - .settings( - scalaVersion := "3.0.0-M3", - resolvers += Resolver.JCenterRepository - ) - lazy val rc1 = (project in file("rc1")) .settings( scalaVersion := "3.0.0-RC1" ) +lazy val a = project + .settings( + scalaVersion := "3.4.2", + ) diff --git a/sbt-app/src/sbt-test/actions/doc-scala3/test b/sbt-app/src/sbt-test/actions/doc-scala3/test index c13e97789c..064d840f40 100644 --- a/sbt-app/src/sbt-test/actions/doc-scala3/test +++ b/sbt-app/src/sbt-test/actions/doc-scala3/test @@ -6,10 +6,10 @@ $ exists rc1/target/scala-3.0.0-RC1/api/api/index.html $ exists rc1/target/scala-3.0.0-RC1/api/api/foo/A$.html $ exists rc1/target/scala-3.0.0-RC1/api/api/foo.html -> m3 / doc +> a / doc # there shouldn't be two api/ directories # see https://github.com/lampepfl/dotty/issues/11412 -$ exists m3/target/scala-3.0.0-M3/api/index.html -$ exists m3/target/scala-3.0.0-M3/api/api/foo/A$.html -$ exists m3/target/scala-3.0.0-M3/api/api/foo.html +$ exists a/target/scala-3.4.2/api/index.html +$ exists a/target/scala-3.4.2/api/foo/A$.html +$ exists a/target/scala-3.4.2/api/foo.html diff --git a/sbt-app/src/sbt-test/actions/doc/build.sbt b/sbt-app/src/sbt-test/actions/doc/build.sbt index fbf26e66f5..da46ccd465 100644 --- a/sbt-app/src/sbt-test/actions/doc/build.sbt +++ b/sbt-app/src/sbt-test/actions/doc/build.sbt @@ -5,8 +5,8 @@ import Parsers._ lazy val root = (project in file(".")) .settings( crossPaths := false, - crossScalaVersions := Seq("2.12.18", "2.13.12"), - scalaVersion := "2.12.18", + crossScalaVersions := Seq("2.12.20", "2.13.12"), + scalaVersion := "2.12.20", Compile / doc / scalacOptions += "-Xfatal-warnings", commands += Command.command("excludeB") { s => val impl = """val src = (sources in Compile).value; src.filterNot(_.getName.contains("B"))""" diff --git a/sbt-app/src/sbt-test/actions/generator/build.sbt b/sbt-app/src/sbt-test/actions/generator/build.sbt index af23bebf72..3537ae7b09 100644 --- a/sbt-app/src/sbt-test/actions/generator/build.sbt +++ b/sbt-app/src/sbt-test/actions/generator/build.sbt @@ -1,6 +1,6 @@ val buildInfo = taskKey[Seq[File]]("generates the build info") -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" lazy val root = (project in file(".")) .settings( diff --git a/sbt-app/src/sbt-test/actions/multi-command/build.sbt b/sbt-app/src/sbt-test/actions/multi-command/build.sbt index 08909f5585..606ba16573 100644 --- a/sbt-app/src/sbt-test/actions/multi-command/build.sbt +++ b/sbt-app/src/sbt-test/actions/multi-command/build.sbt @@ -19,4 +19,4 @@ val dynamicTask = taskKey[Unit]("dynamic input task") dynamicTask := { println("not yet et") } -crossScalaVersions := "2.11.12" :: "2.12.18" :: Nil +crossScalaVersions := "2.11.12" :: "2.12.20" :: Nil diff --git a/sbt-app/src/sbt-test/actions/multi-command/test b/sbt-app/src/sbt-test/actions/multi-command/test index dc657b5537..323702a6d5 100644 --- a/sbt-app/src/sbt-test/actions/multi-command/test +++ b/sbt-app/src/sbt-test/actions/multi-command/test @@ -37,4 +37,4 @@ > ++ 2.11.12 compile; setStringValue bar; checkStringValue bar -> ++2.12.18 compile; setStringValue foo; checkStringValue foo +> ++2.12.20 compile; setStringValue foo; checkStringValue foo diff --git a/sbt-app/src/sbt-test/actions/package-delete-target/build.sbt b/sbt-app/src/sbt-test/actions/package-delete-target/build.sbt index 521d0c6da4..01163a404f 100644 --- a/sbt-app/src/sbt-test/actions/package-delete-target/build.sbt +++ b/sbt-app/src/sbt-test/actions/package-delete-target/build.sbt @@ -1,5 +1,5 @@ lazy val root = (project in file(".")) .settings( name := "delete-target", - scalaVersion := "2.12.1" + scalaVersion := "2.12.20" ) diff --git a/sbt-app/src/sbt-test/actions/remote-cache-semanticdb/build.sbt b/sbt-app/src/sbt-test/actions/remote-cache-semanticdb/build.sbt index 501054f781..3153cb66db 100644 --- a/sbt-app/src/sbt-test/actions/remote-cache-semanticdb/build.sbt +++ b/sbt-app/src/sbt-test/actions/remote-cache-semanticdb/build.sbt @@ -1,6 +1,6 @@ name := "my-project" -scalaVersion := "2.12.18" +scalaVersion := "2.12.20" semanticdbIncludeInJar := true diff --git a/sbt-app/src/sbt-test/actions/remote-cache/build.sbt b/sbt-app/src/sbt-test/actions/remote-cache/build.sbt index e4e2c175be..e99d9a8548 100644 --- a/sbt-app/src/sbt-test/actions/remote-cache/build.sbt +++ b/sbt-app/src/sbt-test/actions/remote-cache/build.sbt @@ -8,7 +8,7 @@ lazy val CustomArtifact = config("custom-artifact") val recordPreviousIterations = taskKey[Unit]("Record previous iterations.") val checkIterations = inputKey[Unit]("Verifies the accumulated number of iterations of incremental compilation.") -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" ThisBuild / pushRemoteCacheTo := Some( MavenCache("local-cache", (ThisBuild / baseDirectory).value / "r") ) diff --git a/sbt-app/src/sbt-test/actions/task-map/test b/sbt-app/src/sbt-test/actions/task-map/test index 3a88d40a75..a3bec9fa79 100644 --- a/sbt-app/src/sbt-test/actions/task-map/test +++ b/sbt-app/src/sbt-test/actions/task-map/test @@ -1,7 +1,7 @@ > taskB -$ exists target/b -$ exists target/a +$ exists target/**/b +$ exists target/**/a > taskF -$ exists target/e -$ exists target/f +$ exists target/**/e +$ exists target/**/f diff --git a/sbt-app/src/sbt-test/classloader-cache/akka-actor-system/build.sbt b/sbt-app/src/sbt-test/classloader-cache/akka-actor-system/build.sbt index 7ddb5c706f..f2309bb9b6 100644 --- a/sbt-app/src/sbt-test/classloader-cache/akka-actor-system/build.sbt +++ b/sbt-app/src/sbt-test/classloader-cache/akka-actor-system/build.sbt @@ -2,7 +2,7 @@ ThisBuild / turbo := true val akkaTest = (project in file(".")).settings( name := "akka-test", - scalaVersion := "2.12.18", + scalaVersion := "2.12.20", libraryDependencies ++= Seq( "com.typesafe.akka" %% "akka-actor" % "2.5.16", "com.lihaoyi" %% "utest" % "0.6.6" % "test" diff --git a/sbt-app/src/sbt-test/classloader-cache/jni/build.sbt b/sbt-app/src/sbt-test/classloader-cache/jni/build.sbt index 7015d24ed4..abbc5b082c 100644 --- a/sbt-app/src/sbt-test/classloader-cache/jni/build.sbt +++ b/sbt-app/src/sbt-test/classloader-cache/jni/build.sbt @@ -13,7 +13,7 @@ def wrap(task: InputKey[Unit]): Def.Initialize[Task[Unit]] = ThisBuild / turbo := true val root = (project in file(".")).settings( - scalaVersion := "2.12.18", + scalaVersion := "2.12.20", javacOptions ++= Seq("-source", "1.8", "-target", "1.8", "-h", sourceDirectory.value.toPath.resolve("main/native/include").toString), libraryDependencies += "com.lihaoyi" %% "utest" % "0.6.6" % "test", diff --git a/sbt-app/src/sbt-test/classloader-cache/library-mismatch/build.sbt b/sbt-app/src/sbt-test/classloader-cache/library-mismatch/build.sbt index 4c0050eb42..fb6fcaac00 100644 --- a/sbt-app/src/sbt-test/classloader-cache/library-mismatch/build.sbt +++ b/sbt-app/src/sbt-test/classloader-cache/library-mismatch/build.sbt @@ -2,7 +2,7 @@ ThisBuild / turbo := true val snapshot = (project in file(".")).settings( name := "mismatched-libraries", - scalaVersion := "2.12.18", + scalaVersion := "2.12.20", libraryDependencies ++= Seq("com.lihaoyi" %% "utest" % "0.6.6" % "test"), testFrameworks := Seq(TestFramework("utest.runner.Framework")), resolvers += "Local Maven" at file("libraries/ivy").toURI.toURL.toString, diff --git a/sbt-app/src/sbt-test/classloader-cache/runtime-layers/build.sbt b/sbt-app/src/sbt-test/classloader-cache/runtime-layers/build.sbt index 8410b25edb..215014a80f 100644 --- a/sbt-app/src/sbt-test/classloader-cache/runtime-layers/build.sbt +++ b/sbt-app/src/sbt-test/classloader-cache/runtime-layers/build.sbt @@ -1,6 +1,6 @@ val layeringStrategyTest = (project in file(".")).settings( name := "layering-strategy-test", - scalaVersion := "2.12.18", + scalaVersion := "2.12.20", organization := "sbt", libraryDependencies += "com.typesafe.akka" %% "akka-actor" % "2.5.16", ) diff --git a/sbt-app/src/sbt-test/classloader-cache/snapshot/build.sbt b/sbt-app/src/sbt-test/classloader-cache/snapshot/build.sbt index d0b5cdfeb9..3087df245e 100644 --- a/sbt-app/src/sbt-test/classloader-cache/snapshot/build.sbt +++ b/sbt-app/src/sbt-test/classloader-cache/snapshot/build.sbt @@ -9,7 +9,7 @@ ThisBuild / useCoursier := false val snapshot = (project in file(".")).settings( name := "akka-test", - scalaVersion := "2.12.18", + scalaVersion := "2.12.20", libraryDependencies ++= Seq( "com.lihaoyi" %% "utest" % "0.6.6" % "test" ), diff --git a/sbt-app/src/sbt-test/classloader-cache/snapshot/libraries/library-1/ivy/sbt/foo-lib_2.12/0.1.0-SNAPSHOT/foo-lib_2.12-0.1.0-SNAPSHOT.pom b/sbt-app/src/sbt-test/classloader-cache/snapshot/libraries/library-1/ivy/sbt/foo-lib_2.12/0.1.0-SNAPSHOT/foo-lib_2.12-0.1.0-SNAPSHOT.pom index 39146c2600..f358077c95 100644 --- a/sbt-app/src/sbt-test/classloader-cache/snapshot/libraries/library-1/ivy/sbt/foo-lib_2.12/0.1.0-SNAPSHOT/foo-lib_2.12-0.1.0-SNAPSHOT.pom +++ b/sbt-app/src/sbt-test/classloader-cache/snapshot/libraries/library-1/ivy/sbt/foo-lib_2.12/0.1.0-SNAPSHOT/foo-lib_2.12-0.1.0-SNAPSHOT.pom @@ -14,7 +14,7 @@ org.scala-lang scala-library - 2.12.18 + 2.12.20 diff --git a/sbt-app/src/sbt-test/classloader-cache/snapshot/libraries/library-2/ivy/sbt/foo-lib_2.12/0.1.0-SNAPSHOT/foo-lib_2.12-0.1.0-SNAPSHOT.pom b/sbt-app/src/sbt-test/classloader-cache/snapshot/libraries/library-2/ivy/sbt/foo-lib_2.12/0.1.0-SNAPSHOT/foo-lib_2.12-0.1.0-SNAPSHOT.pom index 39146c2600..f358077c95 100644 --- a/sbt-app/src/sbt-test/classloader-cache/snapshot/libraries/library-2/ivy/sbt/foo-lib_2.12/0.1.0-SNAPSHOT/foo-lib_2.12-0.1.0-SNAPSHOT.pom +++ b/sbt-app/src/sbt-test/classloader-cache/snapshot/libraries/library-2/ivy/sbt/foo-lib_2.12/0.1.0-SNAPSHOT/foo-lib_2.12-0.1.0-SNAPSHOT.pom @@ -14,7 +14,7 @@ org.scala-lang scala-library - 2.12.18 + 2.12.20 diff --git a/sbt-app/src/sbt-test/classloader-cache/spark/build.sbt b/sbt-app/src/sbt-test/classloader-cache/spark/build.sbt index 88af78b3c8..b105446d8f 100644 --- a/sbt-app/src/sbt-test/classloader-cache/spark/build.sbt +++ b/sbt-app/src/sbt-test/classloader-cache/spark/build.sbt @@ -2,7 +2,7 @@ name := "Simple Project" version := "1.0" -scalaVersion := "2.12.18" +scalaVersion := "2.12.20" libraryDependencies += "org.apache.spark" %% "spark-sql" % "2.4.3" diff --git a/sbt-app/src/sbt-test/classloader-cache/utest/build.sbt b/sbt-app/src/sbt-test/classloader-cache/utest/build.sbt index 6762ba8584..3691603200 100644 --- a/sbt-app/src/sbt-test/classloader-cache/utest/build.sbt +++ b/sbt-app/src/sbt-test/classloader-cache/utest/build.sbt @@ -2,7 +2,7 @@ ThisBuild / turbo := true val utestTest = (project in file(".")).settings( name := "utest-test", - scalaVersion := "2.12.18", + scalaVersion := "2.12.20", libraryDependencies ++= Seq( "com.lihaoyi" %% "utest" % "0.6.6" % "test" ), diff --git a/sbt-app/src/sbt-test/compiler-project/error-in-invalidated/build.sbt b/sbt-app/src/sbt-test/compiler-project/error-in-invalidated/build.sbt index fbf51533b4..442a697f7c 100644 --- a/sbt-app/src/sbt-test/compiler-project/error-in-invalidated/build.sbt +++ b/sbt-app/src/sbt-test/compiler-project/error-in-invalidated/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" lazy val root = (project in file(".")). settings( diff --git a/sbt-app/src/sbt-test/compiler-project/macro-config/build.sbt b/sbt-app/src/sbt-test/compiler-project/macro-config/build.sbt index 248b7cb45d..114444c6fd 100644 --- a/sbt-app/src/sbt-test/compiler-project/macro-config/build.sbt +++ b/sbt-app/src/sbt-test/compiler-project/macro-config/build.sbt @@ -5,7 +5,7 @@ val Macro = config("macro").hide.extend(Compile) lazy val root = (project in file(".")) .settings( - scalaVersion := "2.12.2", + scalaVersion := "2.12.20", // Adds a "macro" configuration for macro dependencies. ivyConfigurations.value += Macro, diff --git a/sbt-app/src/sbt-test/compiler-project/no-bootclasspath/build.sbt b/sbt-app/src/sbt-test/compiler-project/no-bootclasspath/build.sbt new file mode 100644 index 0000000000..edd8707536 --- /dev/null +++ b/sbt-app/src/sbt-test/compiler-project/no-bootclasspath/build.sbt @@ -0,0 +1,9 @@ +TaskKey[Unit]("check212") := checkCp(true) +TaskKey[Unit]("check213") := checkCp(false) + +def checkCp(auto: Boolean) = Def.task { + val opts = compilers.value.scalac.classpathOptions + assert(opts.autoBoot == auto, opts) + assert(opts.filterLibrary == auto, opts) + () +} diff --git a/sbt-app/src/sbt-test/compiler-project/no-bootclasspath/test b/sbt-app/src/sbt-test/compiler-project/no-bootclasspath/test new file mode 100644 index 0000000000..59b1e34b35 --- /dev/null +++ b/sbt-app/src/sbt-test/compiler-project/no-bootclasspath/test @@ -0,0 +1,4 @@ +> set scalaVersion := "2.13.12" +> check213 +> set scalaVersion := "2.12.20" +> check212 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 78c29bca55..60bfe1f866 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.18" +ThisBuild / scalaVersion := "2.12.20" libraryDependencies ++= Seq( "com.novocode" % "junit-interface" % "0.5" % Test, diff --git a/sbt-app/src/sbt-test/compiler-project/semantic-errors/build.sbt b/sbt-app/src/sbt-test/compiler-project/semantic-errors/build.sbt index 019d06f898..c861b159be 100644 --- a/sbt-app/src/sbt-test/compiler-project/semantic-errors/build.sbt +++ b/sbt-app/src/sbt-test/compiler-project/semantic-errors/build.sbt @@ -1,7 +1,7 @@ TaskKey[Unit]("checkJavaFailures") := { val reporter = savedReporter.value val ignore = (compile in Compile).failure.value - val ps = reporter.problems + val ps = reporter.problems.filter(_.severity() != xsbti.Severity.Info) assert(!ps.isEmpty, "Failed to report any problems!") // First error should be on a specific line/file val first = ps(0) diff --git a/sbt-app/src/sbt-test/compiler-project/separate-analysis-per-scala/build.sbt b/sbt-app/src/sbt-test/compiler-project/separate-analysis-per-scala/build.sbt index 33657a7c46..bab8177e6d 100644 --- a/sbt-app/src/sbt-test/compiler-project/separate-analysis-per-scala/build.sbt +++ b/sbt-app/src/sbt-test/compiler-project/separate-analysis-per-scala/build.sbt @@ -1,4 +1,4 @@ -lazy val scala212 = "2.12.18" +lazy val scala212 = "2.12.20" lazy val scala213 = "2.13.12" ThisBuild / scalaVersion := scala212 diff --git a/sbt-app/src/sbt-test/console/project-compiler-bridge/project/build.sbt b/sbt-app/src/sbt-test/console/project-compiler-bridge/project/build.sbt index 2405c81d50..76f36acc28 100644 --- a/sbt-app/src/sbt-test/console/project-compiler-bridge/project/build.sbt +++ b/sbt-app/src/sbt-test/console/project-compiler-bridge/project/build.sbt @@ -1 +1 @@ -scalaVersion := "2.12.18" +scalaVersion := "2.12.20" diff --git a/sbt-app/src/sbt-test/dependency-graph/cachedResolution/build.sbt b/sbt-app/src/sbt-test/dependency-graph/cachedResolution/build.sbt index e4d1990a8e..4ecda4a163 100644 --- a/sbt-app/src/sbt-test/dependency-graph/cachedResolution/build.sbt +++ b/sbt-app/src/sbt-test/dependency-graph/cachedResolution/build.sbt @@ -1,4 +1,4 @@ -scalaVersion := "2.12.9" +scalaVersion := "2.12.20" libraryDependencies += "org.slf4j" % "slf4j-api" % "1.7.28" updateOptions := updateOptions.value.withCachedResolution(true) 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 3fce907914..c05b42e715 100644 --- a/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/build.sbt +++ b/sbt-app/src/sbt-test/dependency-graph/ignoreScalaLibrary/build.sbt @@ -1,14 +1,27 @@ -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" libraryDependencies ++= Seq( "org.slf4j" % "slf4j-api" % "1.7.2", "ch.qos.logback" % "logback-classic" % "1.0.7" ) +csrMavenDependencyOverride := false TaskKey[Unit]("check") := { val report = updateFull.value val graph = (Test / dependencyTree / asString).value def sanitize(str: String): String = str.split('\n').drop(1).map(_.trim).mkString("\n") + +/* +Started to return: + +ch.qos.logback:logback-core:1.0.7 +default:sbt_8ae1da13_2.12:0.1.0-SNAPSHOT [S] + +-ch.qos.logback:logback-classic:1.0.7 + | +-org.slf4j:slf4j-api:1.6.6 (evicted by: 1.7.2) + | + +-org.slf4j:slf4j-api:1.7.2 +*/ + val expectedGraph = """default:default-e95e05_2.12:0.1-SNAPSHOT [S] | +-ch.qos.logback:logback-classic:1.0.7 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 693894d506..66a6c1bff7 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.18" +ThisBuild / scalaVersion := "2.12.20" ThisBuild / organization := "org.example" ThisBuild / version := "0.1" 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 a2be213feb..077b2a06be 100644 --- a/sbt-app/src/sbt-test/dependency-management/artifact/build.sbt +++ b/sbt-app/src/sbt-test/dependency-management/artifact/build.sbt @@ -6,7 +6,7 @@ lazy val check = taskKey[Unit]("") lazy val checkArtifact = taskKey[Unit]("") ThisBuild / useCoursier := false -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" ThisBuild / version := "0.1.0-SNAPSHOT" ThisBuild / organization := "com.example" ThisBuild / organizationName := "example" diff --git a/sbt-app/src/sbt-test/dependency-management/cache-classifiers/multi.sbt b/sbt-app/src/sbt-test/dependency-management/cache-classifiers/multi.sbt index 910baa6b03..f84a40e2b4 100644 --- a/sbt-app/src/sbt-test/dependency-management/cache-classifiers/multi.sbt +++ b/sbt-app/src/sbt-test/dependency-management/cache-classifiers/multi.sbt @@ -1,6 +1,6 @@ import xsbti.AppConfiguration -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" // TTL of Coursier is 24h ThisBuild / useCoursier := false diff --git a/sbt-app/src/sbt-test/dependency-management/cached-resolution-interproj/multi.sbt b/sbt-app/src/sbt-test/dependency-management/cached-resolution-interproj/multi.sbt index 8c978bd973..2ea6028a3e 100644 --- a/sbt-app/src/sbt-test/dependency-management/cached-resolution-interproj/multi.sbt +++ b/sbt-app/src/sbt-test/dependency-management/cached-resolution-interproj/multi.sbt @@ -3,7 +3,7 @@ lazy val check = taskKey[Unit]("Runs the check") val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" val junit = "junit" % "junit" % "4.13.1" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" ThisBuild / csrCacheDirectory := (ThisBuild / baseDirectory).value / "coursier-cache" def commonSettings: Seq[Def.Setting[_]] = diff --git a/sbt-app/src/sbt-test/dependency-management/compiler-bridge-binary/build.sbt b/sbt-app/src/sbt-test/dependency-management/compiler-bridge-binary/build.sbt index ef4e1834c3..f9cf8fddd8 100644 --- a/sbt-app/src/sbt-test/dependency-management/compiler-bridge-binary/build.sbt +++ b/sbt-app/src/sbt-test/dependency-management/compiler-bridge-binary/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" lazy val check = taskKey[Unit]("") diff --git a/sbt-app/src/sbt-test/dependency-management/conflict-coursier/build.sbt b/sbt-app/src/sbt-test/dependency-management/conflict-coursier/build.sbt index fed0b72ff2..19d25ed0b1 100644 --- a/sbt-app/src/sbt-test/dependency-management/conflict-coursier/build.sbt +++ b/sbt-app/src/sbt-test/dependency-management/conflict-coursier/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" libraryDependencies ++= List( "org.webjars.npm" % "randomatic" % "1.1.7", "org.webjars.npm" % "is-odd" % "2.0.0", diff --git a/sbt-app/src/sbt-test/dependency-management/ext-pom-classifier/build.sbt b/sbt-app/src/sbt-test/dependency-management/ext-pom-classifier/build.sbt index d3253a4831..9ac56bac55 100644 --- a/sbt-app/src/sbt-test/dependency-management/ext-pom-classifier/build.sbt +++ b/sbt-app/src/sbt-test/dependency-management/ext-pom-classifier/build.sbt @@ -2,6 +2,6 @@ ThisBuild / useCoursier := false lazy val root = (project in file(".")) .settings( - scalaVersion := "2.12.6", + scalaVersion := "2.12.20", externalPom() ) diff --git a/sbt-app/src/sbt-test/dependency-management/force-update-period/build.sbt b/sbt-app/src/sbt-test/dependency-management/force-update-period/build.sbt index 11bcdf94e1..a6a4664652 100644 --- a/sbt-app/src/sbt-test/dependency-management/force-update-period/build.sbt +++ b/sbt-app/src/sbt-test/dependency-management/force-update-period/build.sbt @@ -1,17 +1,19 @@ ThisBuild / useCoursier := false +name := "force-update-period" +scalaVersion := "2.12.20" libraryDependencies += "log4j" % "log4j" % "1.2.16" % "compile" - autoScalaLibrary := false -crossPaths := false - -TaskKey[Unit]("check-last-update-time") := (streams map { (s) => - val fullUpdateOutput = s.cacheDirectory / "out" - val timeDiff = System.currentTimeMillis()-fullUpdateOutput.lastModified() - val exists = fullUpdateOutput.exists() +TaskKey[Unit]("check-last-update-time") := { + val s = streams.value + val updateOutput = crossTarget.value / "update" / updateCacheName.value / "output" + if (!updateOutput.exists()) { + sys.error("Update cache does not exist") + } + val timeDiff = System.currentTimeMillis() - updateOutput.lastModified() s.log.info(s"Amount of time since last full update: $timeDiff") - if (exists && timeDiff > 5000) { - sys.error("Full update not performed") + if (timeDiff > 5000) { + sys.error("Update not performed") } -}).value +} diff --git a/sbt-app/src/sbt-test/dependency-management/force-update-period/test b/sbt-app/src/sbt-test/dependency-management/force-update-period/test index 39e776d097..17ae82b98a 100644 --- a/sbt-app/src/sbt-test/dependency-management/force-update-period/test +++ b/sbt-app/src/sbt-test/dependency-management/force-update-period/test @@ -1,11 +1,11 @@ -$ absent target/resolution-cache +$ absent target/scala-2.12/resolution-cache > compile -$ exists target/resolution-cache +$ exists target/scala-2.12/resolution-cache > checkLastUpdateTime -$ sleep 10000 +$ sleep 5000 > compile # This is expected to fail -> checkLastUpdateTime > set forceUpdatePeriod := Some(new scala.concurrent.duration.FiniteDuration(5000, java.util.concurrent.TimeUnit.MILLISECONDS)) > compile -> checkLastUpdateTime \ No newline at end of file +> checkLastUpdateTime diff --git a/sbt-app/src/sbt-test/dependency-management/global-plugins/build.sbt b/sbt-app/src/sbt-test/dependency-management/global-plugins/build.sbt index 2405c81d50..76f36acc28 100644 --- a/sbt-app/src/sbt-test/dependency-management/global-plugins/build.sbt +++ b/sbt-app/src/sbt-test/dependency-management/global-plugins/build.sbt @@ -1 +1 @@ -scalaVersion := "2.12.18" +scalaVersion := "2.12.20" diff --git a/sbt-app/src/sbt-test/dependency-management/sbt-plugin-publish/build.sbt b/sbt-app/src/sbt-test/dependency-management/sbt-plugin-publish/build.sbt index b49b1a248d..bd97aa61d0 100644 --- a/sbt-app/src/sbt-test/dependency-management/sbt-plugin-publish/build.sbt +++ b/sbt-app/src/sbt-test/dependency-management/sbt-plugin-publish/build.sbt @@ -10,8 +10,8 @@ lazy val sbtPlugin1 = project.in(file("sbt-plugin-1")) name := "sbt-plugin-1", addSbtPlugin("ch.epfl.scala" % "sbt-plugin-example-diamond" % "0.5.0"), publishTo := Some(resolver), - checkPackagedArtifacts := checkPackagedArtifactsDef("sbt-plugin-1", true).value, - checkPublish := checkPublishDef("sbt-plugin-1", true).value + checkPackagedArtifacts := checkPackagedArtifactsDef("sbt-plugin-1", false).value, + checkPublish := checkPublishDef("sbt-plugin-1", false).value ) lazy val testMaven1 = project.in(file("test-maven-1")) diff --git a/sbt-app/src/sbt-test/dependency-management/snapshot-local/build.sbt b/sbt-app/src/sbt-test/dependency-management/snapshot-local/build.sbt index 607bbacc11..15ac28821b 100644 --- a/sbt-app/src/sbt-test/dependency-management/snapshot-local/build.sbt +++ b/sbt-app/src/sbt-test/dependency-management/snapshot-local/build.sbt @@ -1,5 +1,5 @@ ThisBuild / organization := "com.example" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" ThisBuild / csrCacheDirectory := (ThisBuild / baseDirectory).value / "coursier-cache" def customIvyPaths: Seq[Def.Setting[_]] = Seq( diff --git a/sbt-app/src/sbt-test/dependency-management/snapshot-resolution/build.sbt b/sbt-app/src/sbt-test/dependency-management/snapshot-resolution/build.sbt index 4b9b319450..a129fcdf30 100644 --- a/sbt-app/src/sbt-test/dependency-management/snapshot-resolution/build.sbt +++ b/sbt-app/src/sbt-test/dependency-management/snapshot-resolution/build.sbt @@ -1,5 +1,5 @@ ThisBuild / organization := "com.example" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" // TTL is 24h so we can't detect the change ThisBuild / useCoursier := false diff --git a/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze-update/build.sbt b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze-update/build.sbt new file mode 100644 index 0000000000..ed6887bc4f --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze-update/build.sbt @@ -0,0 +1,4 @@ +lazy val a = project.settings( + scalaVersion := "2.13.4", + libraryDependencies += "org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.4", // depends on library 2.13.6 +) diff --git a/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze-update/test b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze-update/test new file mode 100644 index 0000000000..7f263ab0b6 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze-update/test @@ -0,0 +1,3 @@ +-> a/scalaInstance +> set a/scalaVersion := "2.13.6" +> a/scalaInstance diff --git a/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze-warn/a/A.scala b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze-warn/a/A.scala new file mode 100644 index 0000000000..c66551e1bd --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze-warn/a/A.scala @@ -0,0 +1,27 @@ +import scala.language.reflectiveCalls + + +package scala.collection.immutable { + object Exp { + // Access RedBlackTree.validate added in Scala 2.13.13 + def v = RedBlackTree.validate(null)(null) + } +} + + +object A extends App { + println(scala.util.Properties.versionString) +} + +object AMacro { + import scala.language.experimental.macros + import scala.reflect.macros.blackbox.Context + + def m(x: Int): Int = macro impl + + def impl(c: Context)(x: c.Expr[Int]): c.Expr[Int] = { + import c.universe._ + println(scala.collection.immutable.Exp.v) + c.Expr(q"2 + $x") + } +} diff --git a/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze-warn/b/B.scala b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze-warn/b/B.scala new file mode 100644 index 0000000000..f75b7905ef --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze-warn/b/B.scala @@ -0,0 +1,7 @@ +import java.nio.file.{Paths, Files} +import java.nio.charset.StandardCharsets + +object B extends App { + println(AMacro.m(33)) // fails + Files.write(Paths.get(s"s${scala.util.Properties.versionNumberString}.txt"), "nix".getBytes) +} diff --git a/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze-warn/build.sbt b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze-warn/build.sbt new file mode 100644 index 0000000000..c757f26481 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze-warn/build.sbt @@ -0,0 +1,39 @@ +import sbt.librarymanagement.InclExclRule + +lazy val a = project.settings( + scalaVersion := "2.13.13", + libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value, + TaskKey[Unit]("checkLibs") := checkLibs("2.13.13", (Compile/dependencyClasspath).value, ".*scala-(library|reflect).*"), +) + +lazy val b = project.dependsOn(a).settings( + allowUnsafeScalaLibUpgrade := true, + scalaVersion := "2.13.12", + + // dependencies are upgraded to 2.13.13 + TaskKey[Unit]("checkLibs") := checkLibs("2.13.13", (Compile/dependencyClasspath).value, ".*scala-(library|reflect).*"), + + // check the compiler uses the 2.13.12 library on its runtime classpath + TaskKey[Unit]("checkScala") := { + val i = scalaInstance.value + i.libraryJars.filter(_.toString.contains("scala-library")).toList match { + case List(l) => assert(l.toString.contains("2.13.12"), i.toString) + } + assert(i.compilerJars.filter(_.toString.contains("scala-library")).isEmpty, i.toString) + assert(i.otherJars.filter(_.toString.contains("scala-library")).isEmpty, i.toString) + }, +) + +lazy val c = project.dependsOn(a).settings( + allowUnsafeScalaLibUpgrade := true, + scalaVersion := "2.13.12", + TaskKey[Unit]("checkLibs") := checkLibs("2.13.13", (Compile/dependencyClasspath).value, ".*scala-(library|reflect).*"), +) + +def checkLibs(v: String, cp: Classpath, filter: String): Unit = { + for (p <- cp) + if (p.toString.matches(filter)) { + println(s"$p -- $v") + assert(p.toString.contains(v), p) + } +} diff --git a/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze-warn/c/C.scala b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze-warn/c/C.scala new file mode 100644 index 0000000000..de0f7c0848 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze-warn/c/C.scala @@ -0,0 +1,7 @@ +import java.nio.file.{Paths, Files} +import java.nio.charset.StandardCharsets + +object C extends App { + assert(scala.collection.immutable.Exp.v == null) + Files.write(Paths.get(s"s${scala.util.Properties.versionNumberString}.txt"), "nix".getBytes) +} diff --git a/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze-warn/test b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze-warn/test new file mode 100644 index 0000000000..f347b35342 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze-warn/test @@ -0,0 +1,11 @@ +> a/checkLibs +> b/checkLibs +> b/checkScala +> c/checkLibs + +# macro expansion fails +-> b/compile + +> c/run +$ exists s2.13.13.txt +$ delete s2.13.13.txt diff --git a/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze/a/A.scala b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze/a/A.scala new file mode 100644 index 0000000000..d1522c579c --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze/a/A.scala @@ -0,0 +1,20 @@ +import scala.language.reflectiveCalls + +object A extends App { + println(scala.util.Properties.versionString) +} + +object AMacro { + import scala.language.experimental.macros + import scala.reflect.macros.blackbox.Context + + def m(x: Int): Int = macro impl + + def impl(c: Context)(x: c.Expr[Int]): c.Expr[Int] = { + import c.universe._ + // added in 2.13.4 + val ec = (scala.concurrent.ExecutionContext: {def opportunistic: scala.concurrent.ExecutionContextExecutor}).opportunistic + println(ec) + c.Expr(q"2 + $x") + } +} diff --git a/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze/a3/A.scala b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze/a3/A.scala new file mode 100644 index 0000000000..600c113791 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze/a3/A.scala @@ -0,0 +1,25 @@ +import scala.quoted.* // imports Quotes, Expr + +package scala.collection.immutable { + object Exp { + // Access RedBlackTree.validate and Tree class added in Scala 2.13.13 + // Scala 3.3.4 uses Scala 2.13.14 library + // Scala 3.3.2 uses Scala 2.13.12 library + // Hence RedBlackTree.validate is available in 3.3.4 but not in 3.3.2 + // c.c. https://mvnrepository.com/artifact/org.scala-lang/scala3-library_3/3.3.2 + // c.c. https://mvnrepository.com/artifact/org.scala-lang/scala3-library_3/3.3.4 + def validateTree[A](tree: RedBlackTree.Tree[A, _])(implicit ordering: Ordering[A]): tree.type = { + RedBlackTree.validate(tree) + } + } +} + +object Mac: + inline def inspect(inline x: Any): Any = ${ inspectCode('x) } + + def inspectCode(x: Expr[Any])(using Quotes): Expr[Any] = + scala.collection.immutable.Exp.validateTree(null)(null) + println(x.show) + x + +@main def huhu = println(scala.util.Properties.versionString) diff --git a/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze/b/B.scala b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze/b/B.scala new file mode 100644 index 0000000000..e24101062e --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze/b/B.scala @@ -0,0 +1,7 @@ +import java.nio.file.{Paths, Files} +import java.nio.charset.StandardCharsets + +object B extends App { + println(AMacro.m(33)) + Files.write(Paths.get(s"s${scala.util.Properties.versionNumberString}.txt"), "nix".getBytes) +} diff --git a/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze/b3/B.scala b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze/b3/B.scala new file mode 100644 index 0000000000..a011c5c957 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze/b3/B.scala @@ -0,0 +1,6 @@ +import java.nio.file.{Paths, Files} +import java.nio.charset.StandardCharsets + +@main def hubu = + Mac.inspect(println("hai")) + Files.write(Paths.get(s"s${scala.util.Properties.versionNumberString}.txt"), "nix".getBytes) diff --git a/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze/build.sbt b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze/build.sbt new file mode 100644 index 0000000000..ab04ff427d --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze/build.sbt @@ -0,0 +1,46 @@ +import sbt.librarymanagement.InclExclRule + +lazy val a = project.settings( + scalaVersion := "2.13.11", + libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value, + TaskKey[Unit]("checkLibs") := checkLibs("2.13.11", (Compile/dependencyClasspath).value, ".*scala-(library|reflect).*"), +) + +lazy val b = project.dependsOn(a).settings( + scalaVersion := "2.13.12", + TaskKey[Unit]("checkLibs") := checkLibs("2.13.12", (Compile/dependencyClasspath).value, ".*scala-(library|reflect).*"), +) + +lazy val a3 = project.settings( + scalaVersion := "3.3.4", // 2.13.14 library +) + +lazy val b3 = project.dependsOn(a3).settings( + scalaVersion := "3.3.2", // 2.13.12 library + TaskKey[Unit]("checkScala") := { + val i = scalaInstance.value + i.libraryJars.filter(_.toString.contains("scala-library")).toList match { + case List(l) => assert(l.toString.contains("2.13.14"), i.toString) + } + assert(i.compilerJars.filter(_.toString.contains("scala-library")).isEmpty, i.toString) + assert(i.otherJars.filter(_.toString.contains("scala-library")).isEmpty, i.toString) + }, +) + +lazy val ak = project.settings( + scalaVersion := "2.13.12", + csrSameVersions += Set[InclExclRule]("com.typesafe.akka" % "akka-*"), + libraryDependencies ++= Seq( + "com.typesafe.akka" %% "akka-remote" % "2.6.5", + "com.typesafe.akka" %% "akka-actor" % "2.6.2", + ), + TaskKey[Unit]("checkLibs") := checkLibs("2.6.5", (Compile/dependencyClasspath).value, ".*akka-.*"), +) + +def checkLibs(v: String, cp: Classpath, filter: String): Unit = { + for (p <- cp) + if (p.toString.matches(filter)) { + println(s"$p -- $v") + assert(p.toString.contains(v), p) + } +} diff --git a/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze/test b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze/test new file mode 100644 index 0000000000..8eb1734632 --- /dev/null +++ b/sbt-app/src/sbt-test/dependency-management/stdlib-unfreeze/test @@ -0,0 +1,20 @@ +> a/checkLibs +> b/checkLibs + +> b/run +$ exists s2.13.12.txt +$ delete s2.13.12.txt + +# don't crash when expanding the macro +> b3/run +$ exists s2.13.14.txt +$ delete s2.13.14.txt + +> b3/checkScala + +# without the default `csrSameVersions`, scala-reflect in b stays at 2.13.11 +> set b/csrSameVersions := Nil +> b/update +-> b/checkLibs + +> ak/checkLibs diff --git a/sbt-app/src/sbt-test/java/cross/build.sbt b/sbt-app/src/sbt-test/java/cross/build.sbt index c71bd179e9..cbb55bd7df 100644 --- a/sbt-app/src/sbt-test/java/cross/build.sbt +++ b/sbt-app/src/sbt-test/java/cross/build.sbt @@ -4,7 +4,7 @@ val check = inputKey[Unit]("Runs the check") lazy val root = (project in file(".")) .settings( - ThisBuild / scalaVersion := "2.12.6", + ThisBuild / scalaVersion := "2.12.20", crossJavaVersions := List("1.8"), // read out.txt and see if it starts with the passed in number diff --git a/sbt-app/src/sbt-test/java/cross/changes/build.sbt b/sbt-app/src/sbt-test/java/cross/changes/build.sbt index 2457459122..2d09de654d 100644 --- a/sbt-app/src/sbt-test/java/cross/changes/build.sbt +++ b/sbt-app/src/sbt-test/java/cross/changes/build.sbt @@ -4,7 +4,7 @@ val check = inputKey[Unit]("Runs the check") lazy val root = (project in file(".")) .settings( - ThisBuild / scalaVersion := "2.12.6", + ThisBuild / scalaVersion := "2.12.20", crossJavaVersions := List("1.8", "10"), // read out.txt and see if it starts with the passed in number diff --git a/sbt-app/src/sbt-test/plugins/doc-scala3-js/build.sbt b/sbt-app/src/sbt-test/plugins/doc-scala3-js/build.sbt index af0c707973..177518aa87 100644 --- a/sbt-app/src/sbt-test/plugins/doc-scala3-js/build.sbt +++ b/sbt-app/src/sbt-test/plugins/doc-scala3-js/build.sbt @@ -1,4 +1,4 @@ -val scala3Version = "3.0.1-RC1-bin-20210525-8f3fdf5-NIGHTLY" +val scala3Version = "3.3.4" enablePlugins(ScalaJSPlugin) diff --git a/sbt-app/src/sbt-test/plugins/doc-scala3-js/project/plugins.sbt b/sbt-app/src/sbt-test/plugins/doc-scala3-js/project/plugins.sbt index c16aefc537..a42ace5f7d 100644 --- a/sbt-app/src/sbt-test/plugins/doc-scala3-js/project/plugins.sbt +++ b/sbt-app/src/sbt-test/plugins/doc-scala3-js/project/plugins.sbt @@ -1 +1 @@ -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.5.1") \ No newline at end of file +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.17.0") \ No newline at end of file diff --git a/sbt-app/src/sbt-test/plugins/dotty-sandwich-sjs/build.sbt b/sbt-app/src/sbt-test/plugins/dotty-sandwich-sjs/build.sbt index 5fd5c06b81..aa25b680ea 100644 --- a/sbt-app/src/sbt-test/plugins/dotty-sandwich-sjs/build.sbt +++ b/sbt-app/src/sbt-test/plugins/dotty-sandwich-sjs/build.sbt @@ -1,15 +1,15 @@ -ThisBuild / scalaVersion := "2.13.12" +ThisBuild / scalaVersion := "2.13.15" ThisBuild / scalacOptions += "-Ytasty-reader" lazy val scala3code = project .enablePlugins(ScalaJSPlugin) - .settings(scalaVersion := "3.1.3") + .settings(scalaVersion := "3.3.4") lazy val app = project .enablePlugins(ScalaJSPlugin) .dependsOn(scala3code) .settings( libraryDependencies ~= (_.filterNot(_.name.contains("scalajs-compiler"))), - addCompilerPlugin("org.scala-js" % "scalajs-compiler_2.13.8" % scalaJSVersion), + addCompilerPlugin("org.scala-js" % "scalajs-compiler_2.13.15" % scalaJSVersion), scalaJSUseMainModuleInitializer := true, ) diff --git a/sbt-app/src/sbt-test/plugins/dotty-sandwich-sjs/project/plugins.sbt b/sbt-app/src/sbt-test/plugins/dotty-sandwich-sjs/project/plugins.sbt index 4c620dc0af..b3f269753d 100644 --- a/sbt-app/src/sbt-test/plugins/dotty-sandwich-sjs/project/plugins.sbt +++ b/sbt-app/src/sbt-test/plugins/dotty-sandwich-sjs/project/plugins.sbt @@ -1 +1 @@ -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.10.1") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.17.0") diff --git a/sbt-app/src/sbt-test/plugins/dotty-sandwich/build.sbt b/sbt-app/src/sbt-test/plugins/dotty-sandwich/build.sbt index a8e2060071..f3ee7e93e8 100644 --- a/sbt-app/src/sbt-test/plugins/dotty-sandwich/build.sbt +++ b/sbt-app/src/sbt-test/plugins/dotty-sandwich/build.sbt @@ -1,7 +1,7 @@ -ThisBuild / scalaVersion := "3.1.3" +ThisBuild / scalaVersion := "3.3.4" ThisBuild / scalacOptions += "-Ytasty-reader" -lazy val scala213 = "2.13.12" +lazy val scala213 = "2.13.15" lazy val root = (project in file(".")) .aggregate(fooApp, fooCore, barApp, barCore) diff --git a/sbt-app/src/sbt-test/plugins/dotty/build.sbt b/sbt-app/src/sbt-test/plugins/dotty/build.sbt index e46913668d..48a4f88fe0 100644 --- a/sbt-app/src/sbt-test/plugins/dotty/build.sbt +++ b/sbt-app/src/sbt-test/plugins/dotty/build.sbt @@ -1 +1 @@ -ThisBuild / scalaVersion := "3.1.3" +ThisBuild / scalaVersion := "3.3.4" diff --git a/sbt-app/src/sbt-test/plugins/hydra/build.sbt b/sbt-app/src/sbt-test/plugins/hydra/build.sbt index 4fbd558b63..9ee405caa0 100644 --- a/sbt-app/src/sbt-test/plugins/hydra/build.sbt +++ b/sbt-app/src/sbt-test/plugins/hydra/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" lazy val check = taskKey[Unit]("") diff --git a/sbt-app/src/sbt-test/plugins/sbt-native-packager/build.sbt b/sbt-app/src/sbt-test/plugins/sbt-native-packager/build.sbt index 6203106e29..7783ed396b 100644 --- a/sbt-app/src/sbt-test/plugins/sbt-native-packager/build.sbt +++ b/sbt-app/src/sbt-test/plugins/sbt-native-packager/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" name := "hello" enablePlugins(JavaAppPackaging) diff --git a/sbt-app/src/sbt-test/plugins/scala-js-macro/build.sbt b/sbt-app/src/sbt-test/plugins/scala-js-macro/build.sbt index ff4eb16f37..1039576e3e 100644 --- a/sbt-app/src/sbt-test/plugins/scala-js-macro/build.sbt +++ b/sbt-app/src/sbt-test/plugins/scala-js-macro/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.13.1" +ThisBuild / scalaVersion := "2.13.15" lazy val root = (project in file(".")) .aggregate(macroProvider, macroClient) diff --git a/sbt-app/src/sbt-test/plugins/scala-js-macro/project/plugins.sbt b/sbt-app/src/sbt-test/plugins/scala-js-macro/project/plugins.sbt index 8365c11cc3..b3f269753d 100644 --- a/sbt-app/src/sbt-test/plugins/scala-js-macro/project/plugins.sbt +++ b/sbt-app/src/sbt-test/plugins/scala-js-macro/project/plugins.sbt @@ -1 +1 @@ -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.0.0") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.17.0") diff --git a/sbt-app/src/sbt-test/plugins/unidoc/build.sbt b/sbt-app/src/sbt-test/plugins/unidoc/build.sbt index 3aa46422f6..afa976179c 100644 --- a/sbt-app/src/sbt-test/plugins/unidoc/build.sbt +++ b/sbt-app/src/sbt-test/plugins/unidoc/build.sbt @@ -1,4 +1,4 @@ -scalaVersion := "2.12.1" +scalaVersion := "2.12.20" scalacOptions += "-Xfatal-warnings" // required for the test enablePlugins(ScalaUnidocPlugin) diff --git a/sbt-app/src/sbt-test/project/aggregate/projA/build.sbt b/sbt-app/src/sbt-test/project/aggregate/projA/build.sbt index be91e40d78..af30eb1d06 100644 --- a/sbt-app/src/sbt-test/project/aggregate/projA/build.sbt +++ b/sbt-app/src/sbt-test/project/aggregate/projA/build.sbt @@ -1,3 +1,3 @@ name := "projA" -scalaVersion := "2.12.18" +scalaVersion := "2.12.20" diff --git a/sbt-app/src/sbt-test/project/auto-plugins/build.sbt b/sbt-app/src/sbt-test/project/auto-plugins/build.sbt index f564d48797..99b366f08b 100644 --- a/sbt-app/src/sbt-test/project/auto-plugins/build.sbt +++ b/sbt-app/src/sbt-test/project/auto-plugins/build.sbt @@ -29,37 +29,38 @@ lazy val projI = project.enablePlugins(TopC) lazy val disableAutoNoRequirePlugin = project.disablePlugins(OrgPlugin) check := { - // Ensure organization on root is overridable. - val rorg = (organization).value // Should be None - same(rorg, "override", "organization") - // this will pass when the raw disablePlugin works. - val dversion = (projectID in projD).?.value // Should be None - same(dversion, None, "projectID in projD") - -// Ensure with multiple .sbt files that disabling/enabling works across them - val fDel = (del in Quux in projF).?.value - same(fDel, Some(" Q"), "del in Quux in projF") -// - val adel = (del in projA).?.value // should be None - same(adel, None, "del in projA") - val bdel = (del in projB).?.value // should be None - same(bdel, None, "del in projB") - val ddel = (del in projD).?.value // should be None - same(ddel, None, "del in projD") -// - val buildValue = (demo in ThisBuild).value - same(buildValue, "build 0", "demo in ThisBuild") - val globalValue = (demo in Global).value - same(globalValue, "global 0", "demo in Global") - val projValue = (demo in projC).?.value - same(projValue, Some("project projC Q R"), "demo in projC") - val qValue = (del in projC in Quux).?.value - same(qValue, Some(" Q R"), "del in projC in Quux") - val optInValue = (del in projE in Quux).value - same(optInValue, " Q S R", "del in projE in Quux") - val overrideOrgValue = (organization in projE).value - same(overrideOrgValue, "S", "organization in projE") -// tests for top level plugins + // Ensure organization on root is overridable. + val rorg = (organization).value // Should be None + same(rorg, "override", "organization") + // this will pass when the raw disablePlugin works. + val dversion = (projectID in projD).?.value // Should be None + same(dversion, None, "projectID in projD") + + // Ensure with multiple .sbt files that disabling/enabling works across them + val fDel = (del in Quux in projF).?.value + same(fDel, Some(" Q"), "del in Quux in projF") + + val adel = (del in projA).?.value // should be None + same(adel, None, "del in projA") + val bdel = (del in projB).?.value // should be None + same(bdel, None, "del in projB") + val ddel = (del in projD).?.value // should be None + same(ddel, None, "del in projD") + + val buildValue = (demo in ThisBuild).value + same(buildValue, "build 0", "demo in ThisBuild") + val globalValue = (demo in Global).value + same(globalValue, "global 0", "demo in Global") + val projValue = (demo in projC).?.value + same(projValue, Some("project projC Q R"), "demo in projC") + val qValue = (del in projC in Quux).?.value + same(qValue, Some(" Q R"), "del in projC in Quux") + val optInValue = (del in projE in Quux).value + same(optInValue, " Q R S", "del in projE in Quux") + val overrideOrgValue = (organization in projE).value + same(overrideOrgValue, "S", "organization in projE") + + // tests for top level plugins val topLevelAValueG = (topLevelDemo in projG).value same(topLevelAValueG, "TopA: topLevelDemo project projG", "topLevelDemo in projG") val demoValueG = (demo in projG).value @@ -75,5 +76,5 @@ keyTest := "foo" topLevelKeyTest := "bar" def same[T](actual: T, expected: T, label: String): Unit = { - assert(actual == expected, s"Expected '$expected' for `$label`, got '$actual'") + assert(actual == expected, s"Expected '$expected' for `$label`, got '$actual'") } diff --git a/sbt-app/src/sbt-test/project/cross-plugins-defaults/build.sbt b/sbt-app/src/sbt-test/project/cross-plugins-defaults/build.sbt index 7f712f97e9..d0b8f19125 100644 --- a/sbt-app/src/sbt-test/project/cross-plugins-defaults/build.sbt +++ b/sbt-app/src/sbt-test/project/cross-plugins-defaults/build.sbt @@ -1,7 +1,7 @@ val baseSbt = "1." -val buildCrossList = List("2.10.7", "2.11.12", "2.12.18") -scalaVersion in ThisBuild := "2.12.18" +val buildCrossList = List("2.10.7", "2.11.12", "2.12.20") +scalaVersion in ThisBuild := "2.12.20" crossScalaVersions in ThisBuild := buildCrossList addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.7.0") diff --git a/sbt-app/src/sbt-test/project/flatten/build.sbt b/sbt-app/src/sbt-test/project/flatten/build.sbt index 96b4f560b2..518c06e194 100644 --- a/sbt-app/src/sbt-test/project/flatten/build.sbt +++ b/sbt-app/src/sbt-test/project/flatten/build.sbt @@ -1,6 +1,6 @@ val unpackage = TaskKey[Unit]("unpackage") -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" lazy val root = (project in file(".")) .settings( diff --git a/sbt-app/src/sbt-test/project/internal-dependency-configurations/build.sbt b/sbt-app/src/sbt-test/project/internal-dependency-configurations/build.sbt index 06a8556dd0..f65b347107 100644 --- a/sbt-app/src/sbt-test/project/internal-dependency-configurations/build.sbt +++ b/sbt-app/src/sbt-test/project/internal-dependency-configurations/build.sbt @@ -23,31 +23,31 @@ def getConfigs(key: SettingKey[Seq[(ProjectRef, Set[String])]]): val checkA = taskKey[Unit]("Verify that project a's internal dependencies are as expected") checkA := { val compileDeps = getConfigs(a / Compile / internalDependencyConfigurations).value - assert(compileDeps == Map("a" -> Set("compile"))) + assert(compileDeps == Map("a" -> Set("compile", "optional", "provided", "compile-internal"))) val testDeps = getConfigs(a / Test / internalDependencyConfigurations).value - assert(testDeps == Map("a" -> Set("compile", "runtime", "test"))) + assert(testDeps == Map("a" -> Set("compile", "optional", "provided", "runtime", "test", "test-internal")), testDeps.mkString(", ")) } val checkB = taskKey[Unit]("Verify that project b's internal dependencies are as expected") checkB := { val compileDeps = getConfigs(b / Compile / internalDependencyConfigurations).value - assert(compileDeps == Map("b" -> Set("compile"), "a" -> Set("compile"))) + assert(compileDeps == Map("b" -> Set("compile", "optional", "provided", "compile-internal"), "a" -> Set("compile"))) val testDeps = getConfigs(b / Test / internalDependencyConfigurations).value - assert(testDeps == Map("b" -> Set("compile", "runtime", "test"), "a" -> Set("compile"))) + assert(testDeps == Map("b" -> Set("compile", "optional", "provided", "runtime", "test", "test-internal"), "a" -> Set("compile"))) } val checkC = taskKey[Unit]("Verify that project c's internal dependencies are as expected") checkC := { val compileDeps = getConfigs(c / Compile / internalDependencyConfigurations).value - assert(compileDeps == Map("c" -> Set("compile"))) + assert(compileDeps == Map("c" -> Set("compile", "optional", "provided", "compile-internal"))) val testDeps = getConfigs(c / Test / internalDependencyConfigurations).value - assert(testDeps == Map("c" -> Set("compile", "runtime", "test"))) + assert(testDeps == Map("c" -> Set("compile", "optional", "provided", "runtime", "test", "test-internal"))) } val checkD = taskKey[Unit]("Verify that project d's internal dependencies are as expected") checkD := { val compileDeps = getConfigs(d / Compile / internalDependencyConfigurations).value - assert(compileDeps == Map("d" -> Set("compile"), "c" -> Set("compile"))) + assert(compileDeps == Map("d" -> Set("compile", "optional", "provided", "compile-internal"), "c" -> Set("compile"))) val testDeps = getConfigs(d / Test / internalDependencyConfigurations).value - assert(testDeps == Map("d" -> Set("compile", "runtime", "test"), "c" -> Set("compile", "test"))) + assert(testDeps == Map("d" -> Set("compile", "optional", "provided", "runtime", "test", "test-internal"), "c" -> Set("compile", "test"))) } diff --git a/sbt-app/src/sbt-test/project/internal-tracking/build.sbt b/sbt-app/src/sbt-test/project/internal-tracking/build.sbt index bbc127ca0a..c34f007b56 100644 --- a/sbt-app/src/sbt-test/project/internal-tracking/build.sbt +++ b/sbt-app/src/sbt-test/project/internal-tracking/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" ThisBuild / trackInternalDependencies := TrackLevel.NoTracking lazy val root = (project in file(".")) 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 fead2a619a..3b1ec53286 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.18", + scalaVersion := "2.12.20", 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 242db6841a..1738ee9a5c 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.18", + scalaVersion := "2.12.20", sbtPlugin := true, scalacOptions ++= Seq("-Xfatal-warnings", "-Xlint") ) diff --git a/sbt-app/src/sbt-test/project/scala3-cross-target/build.sbt b/sbt-app/src/sbt-test/project/scala3-cross-target/build.sbt index d87ccac1b6..4550b3c07a 100644 --- a/sbt-app/src/sbt-test/project/scala3-cross-target/build.sbt +++ b/sbt-app/src/sbt-test/project/scala3-cross-target/build.sbt @@ -1,3 +1,3 @@ -scalaVersion := "3.0.0-RC2-bin-20210328-cca5f8f-NIGHTLY" +scalaVersion := "3.3.4" name := "foo" version := "1.0.0" diff --git a/sbt-app/src/sbt-test/project/scala3-cross-target/test b/sbt-app/src/sbt-test/project/scala3-cross-target/test index ff09e0f19e..5509f79c25 100644 --- a/sbt-app/src/sbt-test/project/scala3-cross-target/test +++ b/sbt-app/src/sbt-test/project/scala3-cross-target/test @@ -1,7 +1,7 @@ > compile -$ exists target/scala-3.0.0-RC2-bin-20210328-cca5f8f-NIGHTLY/classes/Foo$.class +$ exists target/scala-3.3.4/classes/Foo$.class > package -$ exists target/scala-3.0.0-RC2-bin-20210328-cca5f8f-NIGHTLY/foo_3.0.0-RC2-1.0.0.jar +$ exists target/scala-3.3.4/foo_3-1.0.0.jar diff --git a/sbt-app/src/sbt-test/project/scala3-sandwich-sjs/build.sbt b/sbt-app/src/sbt-test/project/scala3-sandwich-sjs/build.sbt index 661ac82a1a..88ccf3d59f 100644 --- a/sbt-app/src/sbt-test/project/scala3-sandwich-sjs/build.sbt +++ b/sbt-app/src/sbt-test/project/scala3-sandwich-sjs/build.sbt @@ -1,15 +1,15 @@ -ThisBuild / scalaVersion := "2.13.6" +ThisBuild / scalaVersion := "2.13.15" ThisBuild / scalacOptions += "-Ytasty-reader" lazy val scala3code = project .enablePlugins(ScalaJSPlugin) - .settings(scalaVersion := "3.0.0") + .settings(scalaVersion := "3.3.4") lazy val app = project .enablePlugins(ScalaJSPlugin) .dependsOn(scala3code) .settings( - scalaVersion := "2.13.6", + scalaVersion := "2.13.15", scalacOptions += "-Ytasty-reader", scalaJSUseMainModuleInitializer := true ) diff --git a/sbt-app/src/sbt-test/project/scala3-sandwich-sjs/project/plugins.sbt b/sbt-app/src/sbt-test/project/scala3-sandwich-sjs/project/plugins.sbt index 56abd248d0..b3f269753d 100644 --- a/sbt-app/src/sbt-test/project/scala3-sandwich-sjs/project/plugins.sbt +++ b/sbt-app/src/sbt-test/project/scala3-sandwich-sjs/project/plugins.sbt @@ -1 +1 @@ -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.6.0") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.17.0") diff --git a/sbt-app/src/sbt-test/project/scala3-sandwich/build.sbt b/sbt-app/src/sbt-test/project/scala3-sandwich/build.sbt index 75988d69da..79f714e288 100644 --- a/sbt-app/src/sbt-test/project/scala3-sandwich/build.sbt +++ b/sbt-app/src/sbt-test/project/scala3-sandwich/build.sbt @@ -1,6 +1,6 @@ -ThisBuild / scalaVersion := "3.0.0" +ThisBuild / scalaVersion := "3.3.4" -lazy val scala213 = "2.13.6" +lazy val scala213 = "2.13.15" lazy val root = (project in file(".")) .aggregate(fooApp, fooCore, barApp, barCore) diff --git a/sbt-app/src/sbt-test/project/scripted13/test b/sbt-app/src/sbt-test/project/scripted13/test index 1617a03a4a..2fd6077fb1 100644 --- a/sbt-app/src/sbt-test/project/scripted13/test +++ b/sbt-app/src/sbt-test/project/scripted13/test @@ -1,6 +1,6 @@ # This tests that this sbt scripted plugin can launch the previous one -> ^^0.13.18 +> ^^1.10.7 $ copy-file changes/A.scala src/sbt-test/a/b/A.scala > scripted diff --git a/sbt-app/src/sbt-test/project/semanticdb/build.sbt b/sbt-app/src/sbt-test/project/semanticdb/build.sbt index 8c8a5b3d23..03ad56d268 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.16" +ThisBuild / scalaVersion := "2.12.20" 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 c4a3982749..cbb5585729 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.18" +ThisBuild / scalaVersion := "2.12.20" import sbt.internal.CommandStrings.{ inspectBrief, inspectDetailed } import sbt.internal.Inspect diff --git a/sbt-app/src/sbt-test/reporter/command-progress/build.sbt b/sbt-app/src/sbt-test/reporter/command-progress/build.sbt new file mode 100644 index 0000000000..08f7a74926 --- /dev/null +++ b/sbt-app/src/sbt-test/reporter/command-progress/build.sbt @@ -0,0 +1,39 @@ +commandProgress += new ExecuteProgress2 { + override def beforeCommand(cmd: String, state: State): Unit = { + EventLog.logs += (s"BEFORE: $cmd") + // assert that `state` is the current state indeed + assert(state.currentCommand.isDefined) + assert(state.currentCommand.get.commandLine == cmd) + } + override def afterCommand(cmd: String, result: Either[Throwable, State]): Unit = { + EventLog.logs += (s"AFTER: $cmd") + result.left.foreach(EventLog.errors +=) + } + override def initial(): Unit = {} + override def afterRegistered( + task: Task[_], + allDeps: Iterable[Task[_]], + pendingDeps: Iterable[Task[_]] + ): Unit = {} + override def afterReady(task: Task[_]): Unit = {} + override def beforeWork(task: Task[_]): Unit = {} + override def afterWork[A](task: Task[A], result: Either[Task[A], Result[A]]): Unit = {} + override def afterCompleted[A](task: Task[A], result: Result[A]): Unit = {} + override def afterAllCompleted(results: RMap[Task, Result]): Unit = {} + override def stop(): Unit = {} +} + +val check = taskKey[Unit]("Check basic command events") +val checkParseError = taskKey[Unit]("Check that parse error is recorded") + +check := { + def hasEvent(cmd: String): Boolean = + EventLog.logs.contains(s"BEFORE: $cmd") && EventLog.logs.contains(s"AFTER: $cmd") + + assert(hasEvent("compile"), "Missing command event `compile`") + assert(hasEvent("+compile"), "Missing command event `+compile`") +} + +checkParseError := { + assert(EventLog.errors.exists(_.getMessage.toLowerCase.contains("not a valid command"))) +} diff --git a/sbt-app/src/sbt-test/reporter/command-progress/project/EventLog.scala b/sbt-app/src/sbt-test/reporter/command-progress/project/EventLog.scala new file mode 100644 index 0000000000..88b00c491b --- /dev/null +++ b/sbt-app/src/sbt-test/reporter/command-progress/project/EventLog.scala @@ -0,0 +1,8 @@ +import scala.collection.mutable.ListBuffer + +object EventLog { + val logs = ListBuffer.empty[String] + val errors = ListBuffer.empty[Throwable] + + def reset(): Unit = logs.clear() +} diff --git a/sbt-app/src/sbt-test/reporter/command-progress/test b/sbt-app/src/sbt-test/reporter/command-progress/test new file mode 100644 index 0000000000..9011065e16 --- /dev/null +++ b/sbt-app/src/sbt-test/reporter/command-progress/test @@ -0,0 +1,5 @@ +> compile +> +compile +> check +-> asdf +> checkParseError diff --git a/sbt-app/src/sbt-test/run/fork-loader/build.sbt b/sbt-app/src/sbt-test/run/fork-loader/build.sbt index 9ffe20e132..b440e9f310 100644 --- a/sbt-app/src/sbt-test/run/fork-loader/build.sbt +++ b/sbt-app/src/sbt-test/run/fork-loader/build.sbt @@ -1,6 +1,6 @@ val scalcheck = "org.scalacheck" %% "scalacheck" % "1.14.0" -ThisBuild / scalaVersion := "2.12.12" +ThisBuild / scalaVersion := "2.12.20" lazy val root = (project in file(".")) .settings( diff --git a/sbt-app/src/sbt-test/run/fork-loader/test b/sbt-app/src/sbt-test/run/fork-loader/disabled similarity index 100% rename from sbt-app/src/sbt-test/run/fork-loader/test rename to sbt-app/src/sbt-test/run/fork-loader/disabled diff --git a/sbt-app/src/sbt-test/run/non-local-main/test b/sbt-app/src/sbt-test/run/non-local-main/disabled similarity index 100% rename from sbt-app/src/sbt-test/run/non-local-main/test rename to sbt-app/src/sbt-test/run/non-local-main/disabled diff --git a/sbt-app/src/sbt-test/source-dependencies/binary/build.sbt b/sbt-app/src/sbt-test/source-dependencies/binary/build.sbt index 61b35df9c1..dbf8e86104 100644 --- a/sbt-app/src/sbt-test/source-dependencies/binary/build.sbt +++ b/sbt-app/src/sbt-test/source-dependencies/binary/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" lazy val dep = project diff --git a/sbt-app/src/sbt-test/source-dependencies/constants/test b/sbt-app/src/sbt-test/source-dependencies/constants/test index f3da8143a3..8f027dbf23 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.18! +> ++2.12.20! $ copy-file changes/B.scala B.scala diff --git a/sbt-app/src/sbt-test/source-dependencies/cross-source/test b/sbt-app/src/sbt-test/source-dependencies/cross-source/test index b491e62d38..21b560de7a 100644 --- a/sbt-app/src/sbt-test/source-dependencies/cross-source/test +++ b/sbt-app/src/sbt-test/source-dependencies/cross-source/test @@ -1,3 +1,3 @@ # A.scala needs B.scala, it would be in source list -> ++2.12.18! +> ++2.12.20! > compile diff --git a/sbt-app/src/sbt-test/source-dependencies/export-jars/changes/B.scala b/sbt-app/src/sbt-test/source-dependencies/export-jars/changes/B.scala index 0585279932..1e08585d1e 100644 --- a/sbt-app/src/sbt-test/source-dependencies/export-jars/changes/B.scala +++ b/sbt-app/src/sbt-test/source-dependencies/export-jars/changes/B.scala @@ -1,4 +1,3 @@ -object B -{ - def main(args: Array[String]) = assert(args(0).toInt == A.x ) -} \ No newline at end of file +object B { + def main(args: Array[String]) = assert(args(0).toInt == A.x, s"actual A.x is ${A.x}") +} diff --git a/sbt-app/src/sbt-test/source-dependencies/export-jars/test b/sbt-app/src/sbt-test/source-dependencies/export-jars/test index f1a605f00f..3aff93a7df 100644 --- a/sbt-app/src/sbt-test/source-dependencies/export-jars/test +++ b/sbt-app/src/sbt-test/source-dependencies/export-jars/test @@ -1,12 +1,16 @@ $ copy-file changes/B.scala B.scala $ copy-file changes/A1.scala a/A.scala +$ sleep 1000 + > run 1 $ copy-file changes/A2.scala a/A.scala +$ sleep 1000 # done this way because last modified times often have ~1s resolution > run 2 $ copy-file changes/A3.scala a/A.scala +$ sleep 1000 > run 3 @@ -14,10 +18,14 @@ $ copy-file changes/build2.sbt build2.sbt > reload $ copy-file changes/A1.scala a/A.scala +$ sleep 1000 + > run 1 $ copy-file changes/A2.scala a/A.scala +$ sleep 1000 > run 2 $ copy-file changes/A3.scala a/A.scala +$ sleep 1000 > run 3 diff --git a/sbt-app/src/sbt-test/source-dependencies/implicit-search/test b/sbt-app/src/sbt-test/source-dependencies/implicit-search/test index 91f01259f0..18d69f6b88 100644 --- a/sbt-app/src/sbt-test/source-dependencies/implicit-search/test +++ b/sbt-app/src/sbt-test/source-dependencies/implicit-search/test @@ -5,5 +5,7 @@ $ copy-file changes/C.scala C.scala -> run $ copy-file changes/A2.scala A.scala +$ sleep 1000 + > compile > run diff --git a/sbt-app/src/sbt-test/source-dependencies/linearization/test b/sbt-app/src/sbt-test/source-dependencies/linearization/test index e6078ebfb1..22f17664a4 100644 --- a/sbt-app/src/sbt-test/source-dependencies/linearization/test +++ b/sbt-app/src/sbt-test/source-dependencies/linearization/test @@ -5,6 +5,8 @@ # modify D.scala so that the linearization changes $ copy-file changes/D.scala D.scala +$ sleep 1000 + # F.x becomes 11 > compile diff --git a/sbt-app/src/sbt-test/source-dependencies/macro-annotation/build.sbt b/sbt-app/src/sbt-test/source-dependencies/macro-annotation/build.sbt index a3c6c66d8c..45d6045fe6 100644 --- a/sbt-app/src/sbt-test/source-dependencies/macro-annotation/build.sbt +++ b/sbt-app/src/sbt-test/source-dependencies/macro-annotation/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" val paradiseVersion = "2.1.1" val commonSettings = Seq( diff --git a/sbt-app/src/sbt-test/source-dependencies/macro-arg-dep-nested/build.sbt b/sbt-app/src/sbt-test/source-dependencies/macro-arg-dep-nested/build.sbt index da3c591b8b..cd1f0fda46 100644 --- a/sbt-app/src/sbt-test/source-dependencies/macro-arg-dep-nested/build.sbt +++ b/sbt-app/src/sbt-test/source-dependencies/macro-arg-dep-nested/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" val defaultSettings = Seq( libraryDependencies += scalaVersion("org.scala-lang" % "scala-reflect" % _ ).value diff --git a/sbt-app/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/build.sbt b/sbt-app/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/build.sbt index da3c591b8b..cd1f0fda46 100644 --- a/sbt-app/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/build.sbt +++ b/sbt-app/src/sbt-test/source-dependencies/macro-arg-dep-stackoverflow/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" val defaultSettings = Seq( libraryDependencies += scalaVersion("org.scala-lang" % "scala-reflect" % _ ).value diff --git a/sbt-app/src/sbt-test/source-dependencies/macro-arg-dep/build.sbt b/sbt-app/src/sbt-test/source-dependencies/macro-arg-dep/build.sbt index da3c591b8b..cd1f0fda46 100644 --- a/sbt-app/src/sbt-test/source-dependencies/macro-arg-dep/build.sbt +++ b/sbt-app/src/sbt-test/source-dependencies/macro-arg-dep/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" val defaultSettings = Seq( libraryDependencies += scalaVersion("org.scala-lang" % "scala-reflect" % _ ).value diff --git a/sbt-app/src/sbt-test/source-dependencies/macro/build.sbt b/sbt-app/src/sbt-test/source-dependencies/macro/build.sbt index da3c591b8b..cd1f0fda46 100644 --- a/sbt-app/src/sbt-test/source-dependencies/macro/build.sbt +++ b/sbt-app/src/sbt-test/source-dependencies/macro/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" val defaultSettings = Seq( libraryDependencies += scalaVersion("org.scala-lang" % "scala-reflect" % _ ).value diff --git a/sbt-app/src/sbt-test/source-dependencies/pipelining/build.sbt b/sbt-app/src/sbt-test/source-dependencies/pipelining/build.sbt index 96b5b5a17a..25dfae3964 100644 --- a/sbt-app/src/sbt-test/source-dependencies/pipelining/build.sbt +++ b/sbt-app/src/sbt-test/source-dependencies/pipelining/build.sbt @@ -17,6 +17,6 @@ lazy val use = project val x = (dep / Compile / compile).value val picklePath = (Compile / internalDependencyPicklePath).value assert(picklePath.size == 1 && - picklePath.head.data.name == "dep_2.13-0.1.0-SNAPSHOT.jar", s"picklePath = ${picklePath}") + picklePath.head.data.name == "early.jar", s"picklePath = ${picklePath}") }, ) diff --git a/sbt-app/src/sbt-test/tests/arguments/build.sbt b/sbt-app/src/sbt-test/tests/arguments/build.sbt index c7cce39100..93cde9171d 100644 --- a/sbt-app/src/sbt-test/tests/arguments/build.sbt +++ b/sbt-app/src/sbt-test/tests/arguments/build.sbt @@ -1,6 +1,6 @@ val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" val foo = settingKey[Seq[String]]("foo") val checkFoo = inputKey[Unit]("check contents of foo") diff --git a/sbt-app/src/sbt-test/tests/do-not-discover/build.sbt b/sbt-app/src/sbt-test/tests/do-not-discover/build.sbt index 967047a916..d05f234305 100644 --- a/sbt-app/src/sbt-test/tests/do-not-discover/build.sbt +++ b/sbt-app/src/sbt-test/tests/do-not-discover/build.sbt @@ -1,6 +1,6 @@ val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" lazy val root = (project in file(".")) .settings( diff --git a/sbt-app/src/sbt-test/tests/done/build.sbt b/sbt-app/src/sbt-test/tests/done/build.sbt index 967047a916..d05f234305 100644 --- a/sbt-app/src/sbt-test/tests/done/build.sbt +++ b/sbt-app/src/sbt-test/tests/done/build.sbt @@ -1,6 +1,6 @@ val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" lazy val root = (project in file(".")) .settings( diff --git a/sbt-app/src/sbt-test/tests/filter-runners/build.sbt b/sbt-app/src/sbt-test/tests/filter-runners/build.sbt index f9efce690f..e1aba618ab 100644 --- a/sbt-app/src/sbt-test/tests/filter-runners/build.sbt +++ b/sbt-app/src/sbt-test/tests/filter-runners/build.sbt @@ -1,7 +1,7 @@ val scalatest = "org.scalatest" %% "scalatest" % "3.2.2" val munit = "org.scalameta" %% "munit" % "0.7.22" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" libraryDependencies += scalatest % Test libraryDependencies += munit % Test diff --git a/sbt-app/src/sbt-test/tests/fork-async/build.sbt b/sbt-app/src/sbt-test/tests/fork-async/build.sbt index b761ffb50c..93256098a5 100644 --- a/sbt-app/src/sbt-test/tests/fork-async/build.sbt +++ b/sbt-app/src/sbt-test/tests/fork-async/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" lazy val root = (project in file(".")) .settings( diff --git a/sbt-app/src/sbt-test/tests/fork-parallel/build.sbt b/sbt-app/src/sbt-test/tests/fork-parallel/build.sbt index 55d88c1a27..68c177d330 100644 --- a/sbt-app/src/sbt-test/tests/fork-parallel/build.sbt +++ b/sbt-app/src/sbt-test/tests/fork-parallel/build.sbt @@ -1,7 +1,7 @@ import Tests._ import Defaults._ -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" val check = taskKey[Unit]("Check that tests are executed in parallel") lazy val root = (project in file(".")) diff --git a/sbt-app/src/sbt-test/tests/fork-test-group-parallel-custom-tags/build.sbt b/sbt-app/src/sbt-test/tests/fork-test-group-parallel-custom-tags/build.sbt index c148dc813e..d1e4492cf5 100644 --- a/sbt-app/src/sbt-test/tests/fork-test-group-parallel-custom-tags/build.sbt +++ b/sbt-app/src/sbt-test/tests/fork-test-group-parallel-custom-tags/build.sbt @@ -1,5 +1,5 @@ val specs = "org.specs2" %% "specs2-core" % "4.3.4" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" val TestATypeTag = Tags.Tag("TestA") val TestBTypeTag = Tags.Tag("TestB") diff --git a/sbt-app/src/sbt-test/tests/fork-test-group-parallel/build.sbt b/sbt-app/src/sbt-test/tests/fork-test-group-parallel/build.sbt index 4ff639b355..50f793d227 100644 --- a/sbt-app/src/sbt-test/tests/fork-test-group-parallel/build.sbt +++ b/sbt-app/src/sbt-test/tests/fork-test-group-parallel/build.sbt @@ -1,5 +1,5 @@ val specs = "org.specs2" %% "specs2-core" % "4.3.4" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" Global / concurrentRestrictions := Seq(Tags.limitAll(4)) libraryDependencies += specs % Test diff --git a/sbt-app/src/sbt-test/tests/fork-uncaught2/build.sbt b/sbt-app/src/sbt-test/tests/fork-uncaught2/build.sbt index 50ccf8d4b1..f13c4fbe62 100644 --- a/sbt-app/src/sbt-test/tests/fork-uncaught2/build.sbt +++ b/sbt-app/src/sbt-test/tests/fork-uncaught2/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" libraryDependencies += "org.scala-sbt" % "test-interface" % "1.0" diff --git a/sbt-app/src/sbt-test/tests/fork/build.sbt b/sbt-app/src/sbt-test/tests/fork/build.sbt index 8cef6ee264..65f33a5776 100644 --- a/sbt-app/src/sbt-test/tests/fork/build.sbt +++ b/sbt-app/src/sbt-test/tests/fork/build.sbt @@ -11,7 +11,7 @@ val scalaxml = "org.scala-lang.modules" %% "scala-xml" % "1.1.1" def groupId(idx: Int) = "group_" + (idx + 1) def groupPrefix(idx: Int) = groupId(idx) + "_file_" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" ThisBuild / organization := "org.example" lazy val root = (project in file(".")) diff --git a/sbt-app/src/sbt-test/tests/fork2/build.sbt b/sbt-app/src/sbt-test/tests/fork2/build.sbt index 11a9108de9..e70d963e1c 100644 --- a/sbt-app/src/sbt-test/tests/fork2/build.sbt +++ b/sbt-app/src/sbt-test/tests/fork2/build.sbt @@ -1,6 +1,6 @@ val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" fork := true libraryDependencies += scalatest % Test diff --git a/sbt-app/src/sbt-test/tests/incremental-object-deps/build.sbt b/sbt-app/src/sbt-test/tests/incremental-object-deps/build.sbt new file mode 100644 index 0000000000..d37f0123ee --- /dev/null +++ b/sbt-app/src/sbt-test/tests/incremental-object-deps/build.sbt @@ -0,0 +1,3 @@ +scalaVersion := "3.6.4" +libraryDependencies += "com.eed3si9n.verify" %% "verify" % "1.0.0" % Test +testFrameworks += new TestFramework("verify.runner.Framework") diff --git a/sbt-app/src/sbt-test/tests/incremental-object-deps/changes/B.scala b/sbt-app/src/sbt-test/tests/incremental-object-deps/changes/B.scala new file mode 100644 index 0000000000..6d58d7cdaa --- /dev/null +++ b/sbt-app/src/sbt-test/tests/incremental-object-deps/changes/B.scala @@ -0,0 +1,4 @@ +package example + +object B: + def bbb: String = "3" diff --git a/sbt-app/src/sbt-test/tests/incremental-object-deps/src/main/scala/example/A.scala b/sbt-app/src/sbt-test/tests/incremental-object-deps/src/main/scala/example/A.scala new file mode 100644 index 0000000000..e935ad4e3d --- /dev/null +++ b/sbt-app/src/sbt-test/tests/incremental-object-deps/src/main/scala/example/A.scala @@ -0,0 +1,4 @@ +package example + +object A: + def aaa: String = B.bbb diff --git a/sbt-app/src/sbt-test/tests/incremental-object-deps/src/main/scala/example/B.scala b/sbt-app/src/sbt-test/tests/incremental-object-deps/src/main/scala/example/B.scala new file mode 100644 index 0000000000..778ef9fddc --- /dev/null +++ b/sbt-app/src/sbt-test/tests/incremental-object-deps/src/main/scala/example/B.scala @@ -0,0 +1,4 @@ +package example + +object B: + def bbb: String = "2" diff --git a/sbt-app/src/sbt-test/tests/incremental-object-deps/src/test/scala/example/ATest.scala b/sbt-app/src/sbt-test/tests/incremental-object-deps/src/test/scala/example/ATest.scala new file mode 100644 index 0000000000..5852441555 --- /dev/null +++ b/sbt-app/src/sbt-test/tests/incremental-object-deps/src/test/scala/example/ATest.scala @@ -0,0 +1,7 @@ +package example + +object ATest extends verify.BasicTestSuite: + test("aaa ") { + assert(A.aaa == "2") + } +end ATest diff --git a/sbt-app/src/sbt-test/tests/incremental-object-deps/test b/sbt-app/src/sbt-test/tests/incremental-object-deps/test new file mode 100644 index 0000000000..1938dc3218 --- /dev/null +++ b/sbt-app/src/sbt-test/tests/incremental-object-deps/test @@ -0,0 +1,6 @@ +> debug +> testQuick +$ sleep 2000 +$ copy-file changes/B.scala src/main/scala/example/B.scala + +-> testQuick diff --git a/sbt-app/src/sbt-test/tests/incremental-object-sources/build.sbt b/sbt-app/src/sbt-test/tests/incremental-object-sources/build.sbt new file mode 100644 index 0000000000..5d582f2370 --- /dev/null +++ b/sbt-app/src/sbt-test/tests/incremental-object-sources/build.sbt @@ -0,0 +1,10 @@ +// Global / cacheStores := Seq.empty + +val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" +scalaVersion := "2.12.20" + +lazy val root = (project in file(".")) + .settings( + 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/incremental-object-sources/changed/MathFunction.scala similarity index 100% rename from sbt-app/src/sbt-test/tests/test-quick/changed/MathFunction.scala rename to sbt-app/src/sbt-test/tests/incremental-object-sources/changed/MathFunction.scala diff --git a/sbt-app/src/sbt-test/tests/test-quick/src/main/scala/MathFunction.scala b/sbt-app/src/sbt-test/tests/incremental-object-sources/src/main/scala/MathFunction.scala similarity index 100% rename from sbt-app/src/sbt-test/tests/test-quick/src/main/scala/MathFunction.scala rename to sbt-app/src/sbt-test/tests/incremental-object-sources/src/main/scala/MathFunction.scala diff --git a/sbt-app/src/sbt-test/tests/test-quick/src/test/scala/MathFunctionSpec.scala b/sbt-app/src/sbt-test/tests/incremental-object-sources/src/test/scala/MathFunctionSpec.scala similarity index 100% rename from sbt-app/src/sbt-test/tests/test-quick/src/test/scala/MathFunctionSpec.scala rename to sbt-app/src/sbt-test/tests/incremental-object-sources/src/test/scala/MathFunctionSpec.scala diff --git a/sbt-app/src/sbt-test/tests/incremental-object-sources/test b/sbt-app/src/sbt-test/tests/incremental-object-sources/test new file mode 100644 index 0000000000..b6c0cee18b --- /dev/null +++ b/sbt-app/src/sbt-test/tests/incremental-object-sources/test @@ -0,0 +1,8 @@ +> testQuick + +# https://github.com/sbt/sbt/issues/5504 +$ copy-file changed/MathFunction.scala src/test/scala/MathFunction.scala +> compile +$ sleep 2000 +> debug +-> testQuick MathFunctionTest diff --git a/sbt-app/src/sbt-test/tests/test-quick/build.sbt b/sbt-app/src/sbt-test/tests/incremental/build.sbt similarity index 83% rename from sbt-app/src/sbt-test/tests/test-quick/build.sbt rename to sbt-app/src/sbt-test/tests/incremental/build.sbt index d4d182588d..391cac0aef 100644 --- a/sbt-app/src/sbt-test/tests/test-quick/build.sbt +++ b/sbt-app/src/sbt-test/tests/incremental/build.sbt @@ -1,5 +1,5 @@ val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" lazy val root = (project in file(".")) .settings( diff --git a/sbt-app/src/sbt-test/tests/test-quick/changed/A.scala b/sbt-app/src/sbt-test/tests/incremental/changed/A.scala similarity index 100% rename from sbt-app/src/sbt-test/tests/test-quick/changed/A.scala rename to sbt-app/src/sbt-test/tests/incremental/changed/A.scala diff --git a/sbt-app/src/sbt-test/tests/test-quick/changed/B.scala b/sbt-app/src/sbt-test/tests/incremental/changed/B.scala similarity index 100% rename from sbt-app/src/sbt-test/tests/test-quick/changed/B.scala rename to sbt-app/src/sbt-test/tests/incremental/changed/B.scala diff --git a/sbt-app/src/sbt-test/tests/test-quick/changed/Base.scala b/sbt-app/src/sbt-test/tests/incremental/changed/Base.scala similarity index 100% rename from sbt-app/src/sbt-test/tests/test-quick/changed/Base.scala rename to sbt-app/src/sbt-test/tests/incremental/changed/Base.scala diff --git a/sbt-app/src/sbt-test/tests/test-quick/src/main/scala/A.scala b/sbt-app/src/sbt-test/tests/incremental/src/main/scala/A.scala similarity index 100% rename from sbt-app/src/sbt-test/tests/test-quick/src/main/scala/A.scala rename to sbt-app/src/sbt-test/tests/incremental/src/main/scala/A.scala diff --git a/sbt-app/src/sbt-test/tests/test-quick/src/main/scala/B.scala b/sbt-app/src/sbt-test/tests/incremental/src/main/scala/B.scala similarity index 100% rename from sbt-app/src/sbt-test/tests/test-quick/src/main/scala/B.scala rename to sbt-app/src/sbt-test/tests/incremental/src/main/scala/B.scala diff --git a/sbt-app/src/sbt-test/tests/test-quick/src/test/scala/Base.scala b/sbt-app/src/sbt-test/tests/incremental/src/test/scala/Base.scala similarity index 100% rename from sbt-app/src/sbt-test/tests/test-quick/src/test/scala/Base.scala rename to sbt-app/src/sbt-test/tests/incremental/src/test/scala/Base.scala diff --git a/sbt-app/src/sbt-test/tests/test-quick/src/test/scala/Create.scala b/sbt-app/src/sbt-test/tests/incremental/src/test/scala/Create.scala similarity index 100% rename from sbt-app/src/sbt-test/tests/test-quick/src/test/scala/Create.scala rename to sbt-app/src/sbt-test/tests/incremental/src/test/scala/Create.scala diff --git a/sbt-app/src/sbt-test/tests/test-quick/src/test/scala/Delete.scala b/sbt-app/src/sbt-test/tests/incremental/src/test/scala/Delete.scala similarity index 100% rename from sbt-app/src/sbt-test/tests/test-quick/src/test/scala/Delete.scala rename to sbt-app/src/sbt-test/tests/incremental/src/test/scala/Delete.scala diff --git a/sbt-app/src/sbt-test/tests/test-quick/test b/sbt-app/src/sbt-test/tests/incremental/test similarity index 80% rename from sbt-app/src/sbt-test/tests/test-quick/test rename to sbt-app/src/sbt-test/tests/incremental/test index c86f0276f0..72891032f7 100644 --- a/sbt-app/src/sbt-test/tests/test-quick/test +++ b/sbt-app/src/sbt-test/tests/incremental/test @@ -27,14 +27,8 @@ $ sleep 2000 # src/test compilation group change. $ copy-file changed/Base.scala src/test/scala/Base.scala -> test:compile +> Test/compile $ 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/tests/it/build.sbt b/sbt-app/src/sbt-test/tests/it/build.sbt index 283ac31328..c3c2347735 100644 --- a/sbt-app/src/sbt-test/tests/it/build.sbt +++ b/sbt-app/src/sbt-test/tests/it/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" val specs = "org.specs2" %% "specs2-core" % "4.3.4" diff --git a/sbt-app/src/sbt-test/tests/junit-xml-report/build.sbt b/sbt-app/src/sbt-test/tests/junit-xml-report/build.sbt index 378c6fe976..4d87f2ca2b 100644 --- a/sbt-app/src/sbt-test/tests/junit-xml-report/build.sbt +++ b/sbt-app/src/sbt-test/tests/junit-xml-report/build.sbt @@ -14,7 +14,7 @@ val nestedSuitesReportFile = "target/test-reports/TEST-my.scalatest.MyNestedSuit val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" val junitinterface = "com.novocode" % "junit-interface" % "0.11" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" lazy val root = (project in file(".")). settings( diff --git a/sbt-app/src/sbt-test/tests/munit/build.sbt b/sbt-app/src/sbt-test/tests/munit/build.sbt index e002165e65..e04350cbd4 100644 --- a/sbt-app/src/sbt-test/tests/munit/build.sbt +++ b/sbt-app/src/sbt-test/tests/munit/build.sbt @@ -1,4 +1,4 @@ -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" lazy val munit = "org.scalameta" %% "munit" % "0.7.22" diff --git a/sbt-app/src/sbt-test/tests/nested-inproc-par/build.sbt b/sbt-app/src/sbt-test/tests/nested-inproc-par/build.sbt index d4bbf800f0..68ae9ccf74 100644 --- a/sbt-app/src/sbt-test/tests/nested-inproc-par/build.sbt +++ b/sbt-app/src/sbt-test/tests/nested-inproc-par/build.sbt @@ -1,6 +1,6 @@ val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" lazy val root = (project in file(".")) .settings( diff --git a/sbt-app/src/sbt-test/tests/nested-inproc-seq/build.sbt b/sbt-app/src/sbt-test/tests/nested-inproc-seq/build.sbt index e7c35f4714..20bdacb98e 100644 --- a/sbt-app/src/sbt-test/tests/nested-inproc-seq/build.sbt +++ b/sbt-app/src/sbt-test/tests/nested-inproc-seq/build.sbt @@ -1,6 +1,6 @@ val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" lazy val root = (project in file(".")) .settings( diff --git a/sbt-app/src/sbt-test/tests/nested-subproc/build.sbt b/sbt-app/src/sbt-test/tests/nested-subproc/build.sbt index 514bb7f056..bb06cb73f0 100644 --- a/sbt-app/src/sbt-test/tests/nested-subproc/build.sbt +++ b/sbt-app/src/sbt-test/tests/nested-subproc/build.sbt @@ -1,7 +1,7 @@ val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" val scalaxml = "org.scala-lang.modules" %% "scala-xml" % "1.1.1" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" lazy val root = (project in file(".")) .settings( diff --git a/sbt-app/src/sbt-test/tests/nested-tests/build.sbt b/sbt-app/src/sbt-test/tests/nested-tests/build.sbt index 65eb9b8743..52030f3c9a 100644 --- a/sbt-app/src/sbt-test/tests/nested-tests/build.sbt +++ b/sbt-app/src/sbt-test/tests/nested-tests/build.sbt @@ -1,6 +1,6 @@ val scalcheck = "org.scalacheck" %% "scalacheck" % "1.14.0" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" ThisBuild / version := "0.0.1" ThisBuild / organization := "org.catastrophe" diff --git a/sbt-app/src/sbt-test/tests/one-class-multi-framework/build.sbt b/sbt-app/src/sbt-test/tests/one-class-multi-framework/build.sbt index c4bc7adcad..127d21076d 100644 --- a/sbt-app/src/sbt-test/tests/one-class-multi-framework/build.sbt +++ b/sbt-app/src/sbt-test/tests/one-class-multi-framework/build.sbt @@ -1,5 +1,5 @@ val specsJunit = "org.specs2" %% "specs2-junit" % "4.3.4" val junitinterface = "com.novocode" % "junit-interface" % "0.11" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" libraryDependencies += junitinterface % Test libraryDependencies += specsJunit % Test diff --git a/sbt-app/src/sbt-test/tests/order/build.sbt b/sbt-app/src/sbt-test/tests/order/build.sbt index 2dcb57b050..10db9515d4 100644 --- a/sbt-app/src/sbt-test/tests/order/build.sbt +++ b/sbt-app/src/sbt-test/tests/order/build.sbt @@ -1,5 +1,5 @@ val scalcheck = "org.scalacheck" %% "scalacheck" % "1.14.0" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" Test / parallelExecution := false libraryDependencies += scalcheck % Test diff --git a/sbt-app/src/sbt-test/tests/resources/build.sbt b/sbt-app/src/sbt-test/tests/resources/build.sbt index e4c5dd616c..5d64d55f45 100644 --- a/sbt-app/src/sbt-test/tests/resources/build.sbt +++ b/sbt-app/src/sbt-test/tests/resources/build.sbt @@ -1,3 +1,3 @@ val specs = "org.specs2" %% "specs2-core" % "4.3.4" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" libraryDependencies += specs % Test 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 8080681ba2..dcdf547a94 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.18" +ThisBuild / scalaVersion := "2.12.20" lazy val root = (project in file(".")) .configs(OtherScala) diff --git a/sbt-app/src/sbt-test/tests/serial/build.sbt b/sbt-app/src/sbt-test/tests/serial/build.sbt index a5a0baf713..2d7abec582 100644 --- a/sbt-app/src/sbt-test/tests/serial/build.sbt +++ b/sbt-app/src/sbt-test/tests/serial/build.sbt @@ -1,6 +1,6 @@ val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" ThisBuild / organization := "com.example" ThisBuild / version := "0.0.1-SNAPSHOT" diff --git a/sbt-app/src/sbt-test/tests/setup-cleanup/base.sbt b/sbt-app/src/sbt-test/tests/setup-cleanup/base.sbt index 69a28f0489..d5d9b25526 100644 --- a/sbt-app/src/sbt-test/tests/setup-cleanup/base.sbt +++ b/sbt-app/src/sbt-test/tests/setup-cleanup/base.sbt @@ -1,3 +1,3 @@ val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" libraryDependencies += scalatest diff --git a/sbt-app/src/sbt-test/tests/single-runner/build.sbt b/sbt-app/src/sbt-test/tests/single-runner/build.sbt index bc028ed12e..0547b74652 100644 --- a/sbt-app/src/sbt-test/tests/single-runner/build.sbt +++ b/sbt-app/src/sbt-test/tests/single-runner/build.sbt @@ -1,4 +1,4 @@ val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" libraryDependencies += scalatest Test / testOptions += Tests.Argument("-C", "custom.CustomReporter") diff --git a/sbt-app/src/sbt-test/tests/specs-run/build.sbt b/sbt-app/src/sbt-test/tests/specs-run/build.sbt index ddc7a90555..47774f80b6 100644 --- a/sbt-app/src/sbt-test/tests/specs-run/build.sbt +++ b/sbt-app/src/sbt-test/tests/specs-run/build.sbt @@ -1,4 +1,4 @@ val specs = "org.specs2" %% "specs2-core" % "4.3.4" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" libraryDependencies += specs % Test diff --git a/sbt-app/src/sbt-test/tests/t543/build.sbt b/sbt-app/src/sbt-test/tests/t543/build.sbt index 3ed2bb9436..944e65e4e2 100644 --- a/sbt-app/src/sbt-test/tests/t543/build.sbt +++ b/sbt-app/src/sbt-test/tests/t543/build.sbt @@ -7,7 +7,7 @@ val check = TaskKey[Unit]("check", "Check correct error has been returned.") val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" val scalaxml = "org.scala-lang.modules" %% "scala-xml" % "1.1.1" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" lazy val root = (project in file(".")). settings( diff --git a/sbt-app/src/sbt-test/tests/task/build.sbt b/sbt-app/src/sbt-test/tests/task/build.sbt index bc028ed12e..0547b74652 100644 --- a/sbt-app/src/sbt-test/tests/task/build.sbt +++ b/sbt-app/src/sbt-test/tests/task/build.sbt @@ -1,4 +1,4 @@ val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" libraryDependencies += scalatest Test / testOptions += Tests.Argument("-C", "custom.CustomReporter") diff --git a/sbt-app/src/sbt-test/tests/test-exclude/build.sbt b/sbt-app/src/sbt-test/tests/test-exclude/build.sbt index d4d182588d..391cac0aef 100644 --- a/sbt-app/src/sbt-test/tests/test-exclude/build.sbt +++ b/sbt-app/src/sbt-test/tests/test-exclude/build.sbt @@ -1,5 +1,5 @@ val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" -ThisBuild / scalaVersion := "2.12.18" +ThisBuild / scalaVersion := "2.12.20" lazy val root = (project in file(".")) .settings( diff --git a/sbt-app/src/sbt-test/watch/commands/build.sbt b/sbt-app/src/sbt-test/watch/commands/build.sbt index 662c5cc9ae..1250a85677 100644 --- a/sbt-app/src/sbt-test/watch/commands/build.sbt +++ b/sbt-app/src/sbt-test/watch/commands/build.sbt @@ -53,4 +53,4 @@ expectFailure / watchOnFileInputEvent := { (_, e) => } -crossScalaVersions := Seq("2.11.12", "2.12.18") +crossScalaVersions := Seq("2.11.12", "2.12.20") diff --git a/sbt-app/src/test/scala/sbt/internal/scriptedtest/ScriptedLauncher.java b/sbt-app/src/test/scala/sbt/internal/scriptedtest/ScriptedLauncher.java index 7e21f07950..5badb4a563 100644 --- a/sbt-app/src/test/scala/sbt/internal/scriptedtest/ScriptedLauncher.java +++ b/sbt-app/src/test/scala/sbt/internal/scriptedtest/ScriptedLauncher.java @@ -14,6 +14,8 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.FileAlreadyExistsException; @@ -41,11 +43,11 @@ public class ScriptedLauncher { private static URL URLForClass(final Class clazz) - throws MalformedURLException, ClassNotFoundException { + throws MalformedURLException, ClassNotFoundException, URISyntaxException { final String path = clazz.getCanonicalName().replace('.', '/') + ".class"; final URL url = clazz.getClassLoader().getResource(path); if (url == null) throw new ClassNotFoundException(clazz.getCanonicalName()); - return new URL(url.toString().replaceAll(path + "$", "")); + return new URI(url.toString().replaceAll(path + "$", "")).toURL(); } public static Optional launch( @@ -57,7 +59,7 @@ public static Optional launch( final File[] classpath, String[] arguments) throws InvocationTargetException, ClassNotFoundException, NoSuchMethodException, - IllegalAccessException, IOException { + IllegalAccessException, IOException, URISyntaxException { String[] args = arguments; Object appID = null; if (System.getProperty("sbt.launch.jar") == null) { @@ -136,7 +138,7 @@ public static Optional launch( swap(loader, previous); } } else { - final URL url = new URL("file:" + System.getProperty("sbt.launch.jar")); + final URL url = new URI("file:" + System.getProperty("sbt.launch.jar")).toURL(); final URLClassLoader loader = new URLClassLoader(new URL[] {url}, top()); final Class boot = loader.loadClass("xsbt.boot.Boot"); // If we don't initialize the arguments this way, then the call to invoke on @@ -569,7 +571,9 @@ public boolean allowInsecureProtocol() { @Override public URL url() { try { - return new URL(url); + return new URI(url).toURL(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); } catch (MalformedURLException e) { throw new RuntimeException(e); } diff --git a/server-test/src/server-test/buildserver/build.sbt b/server-test/src/server-test/buildserver/build.sbt index b5bd5d7495..1d31a2e617 100644 --- a/server-test/src/server-test/buildserver/build.sbt +++ b/server-test/src/server-test/buildserver/build.sbt @@ -12,7 +12,7 @@ lazy val runAndTest = project.in(file("run-and-test")) Test / javaOptions := Vector("Xmx512M"), Test / envVars := Map("KEY_TEST" -> "VALUE_TEST"), ) - .dependsOn(util) + .dependsOn(util % Optional) lazy val reportError = project.in(file("report-error")) @@ -42,6 +42,10 @@ lazy val javaProj = project javacOptions += "-Xlint:all" ) +lazy val twirlProj = project + .in(file("twirlProj")) + .enablePlugins(SbtTwirl) + 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/project/plugins.sbt b/server-test/src/server-test/buildserver/project/plugins.sbt new file mode 100644 index 0000000000..8ee7ebeaad --- /dev/null +++ b/server-test/src/server-test/buildserver/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("com.typesafe.play" % "sbt-twirl" % "1.5.2") \ No newline at end of file diff --git a/server-test/src/server-test/buildserver/twirlProj/src/main/twirl/main.scala.html b/server-test/src/server-test/buildserver/twirlProj/src/main/twirl/main.scala.html new file mode 100644 index 0000000000..ca8f069541 --- /dev/null +++ b/server-test/src/server-test/buildserver/twirlProj/src/main/twirl/main.scala.html @@ -0,0 +1,14 @@ +@(title: String, paragraphs: Seq[String]) + + + + + @title + + +

@tilte

+ @for(paragraph <- paragraphs) { +

@paragraph

+ } + + diff --git a/server-test/src/server-test/client/build.sbt b/server-test/src/server-test/client/build.sbt index 3225bd76da..686d2a7a8d 100644 --- a/server-test/src/server-test/client/build.sbt +++ b/server-test/src/server-test/client/build.sbt @@ -1,7 +1,9 @@ +scalaVersion := "3.6.3" + TaskKey[Unit]("willSucceed") := println("success") TaskKey[Unit]("willFail") := { throw new Exception("failed") } -libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.8" % "test" +libraryDependencies += "org.scalameta" %% "munit" % "1.0.4" % Test TaskKey[Unit]("fooBar") := { () } diff --git a/server-test/src/server-test/client/src/main/scala/A.scala b/server-test/src/server-test/client/src/main/scala/A.scala index 69c493db21..171b96e913 100644 --- a/server-test/src/server-test/client/src/main/scala/A.scala +++ b/server-test/src/server-test/client/src/main/scala/A.scala @@ -1 +1,3 @@ -object A +class A + +@main def hello() = println("Hello, World!") diff --git a/server-test/src/server-test/client/src/test/scala/FooSpec.scala b/server-test/src/server-test/client/src/test/scala/FooSpec.scala index 269be56244..fb5352fd9b 100644 --- a/server-test/src/server-test/client/src/test/scala/FooSpec.scala +++ b/server-test/src/server-test/client/src/test/scala/FooSpec.scala @@ -1,3 +1,3 @@ package test.pkg -class FooSpec extends org.scalatest.FlatSpec +class FooSpec extends munit.FunSuite diff --git a/server-test/src/server-test/response/build.sbt b/server-test/src/server-test/response/build.sbt index 8db1f47e68..5884663336 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.18" +ThisBuild / scalaVersion := "2.12.20" 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 c694f13340..0c0306fe9d 100644 --- a/server-test/src/test/scala/testpkg/BuildServerTest.scala +++ b/server-test/src/test/scala/testpkg/BuildServerTest.scala @@ -32,42 +32,50 @@ object BuildServerTest extends AbstractServerTest { private def nextId(): Int = idGen.getAndIncrement() test("build/initialize") { _ => - val id = nextId() - initializeRequest(id) - assert(svr.waitForString(10.seconds) { s => - (s contains s""""id":"${id}"""") && - (s contains """"resourcesProvider":true""") && - (s contains """"outputPathsProvider":true""") - }) + val id = initializeRequest() + assertMessage( + s""""id":"${id}"""", + """"resourcesProvider":true""", + """"outputPathsProvider":true""" + )() } test("workspace/buildTargets") { _ => - svr.sendJsonRpc( - s"""{ "jsonrpc": "2.0", "id": "${nextId()}", "method": "workspace/buildTargets", "params": {} }""" - ) - assert(processing("workspace/buildTargets")) + sendRequest("workspace/buildTargets") val result = svr.waitFor[WorkspaceBuildTargetsResult](10.seconds) - val utilTarget = result.targets.find(_.displayName.contains("util")).get + val utilTargetIdentifier = BuildTargetIdentifier(buildTargetUri("util", "Compile")) + val utilTarget = result.targets.find(_.id == utilTargetIdentifier).get assert(utilTarget.id.uri.toString.endsWith("#util/Compile")) + val runAndTestTarget = result.targets.find(_.displayName.contains("runAndTest")).get + // runAndTest should declare the dependency to util even if optional + assert(runAndTestTarget.dependencies.contains(utilTargetIdentifier)) val buildServerBuildTarget = result.targets.find(_.displayName.contains("buildserver-build")).get assert(buildServerBuildTarget.id.uri.toString.endsWith("#buildserver-build")) assert(!result.targets.exists(_.displayName.contains("badBuildTarget"))) + // Check for JVM based Scala Project, built target should contain Java version information + val scalaBuildTarget = + Converter.fromJsonOptionUnsafe[ScalaBuildTarget](utilTarget.data) + val javaTarget = scalaBuildTarget.jvmBuildTarget + (javaTarget.flatMap(_.javaVersion), javaTarget.flatMap(_.javaHome)) match { + case (Some(javaVersion), Some(javaHome)) => + assert(javaVersion.equals(sys.props("java.version"))) + assert(javaHome.equals(Paths.get(sys.props("java.home")).toUri)) + case _ => fail("JVM build target should contain javaVersion and javaHome") + } } test("buildTarget/sources") { _ => val buildTarget = buildTargetUri("util", "Compile") val badBuildTarget = buildTargetUri("badBuildTarget", "Compile") - svr.sendJsonRpc(buildTargetSources(Seq(buildTarget, badBuildTarget))) - assert(processing("buildTarget/sources")) + buildTargetSources(Seq(buildTarget, badBuildTarget)) val s = svr.waitFor[SourcesResult](10.seconds) val sources = s.items.head.sources.map(_.uri) assert(sources.contains(new File(svr.baseDirectory, "util/src/main/scala").toURI)) } test("buildTarget/sources: base sources") { _ => val buildTarget = buildTargetUri("buildserver", "Compile") - svr.sendJsonRpc(buildTargetSources(Seq(buildTarget))) - assert(processing("buildTarget/sources")) + buildTargetSources(Seq(buildTarget)) val s = svr.waitFor[SourcesResult](10.seconds) val sources = s.items.head.sources val expectedSource = SourceItem( @@ -80,8 +88,7 @@ object BuildServerTest extends AbstractServerTest { test("buildTarget/sources: sbt") { _ => val x = new URI(s"${svr.baseDirectory.getAbsoluteFile.toURI}#buildserver-build") - svr.sendJsonRpc(buildTargetSources(Seq(x))) - assert(processing("buildTarget/sources")) + buildTargetSources(Seq(x)) val s = svr.waitFor[SourcesResult](10.seconds) val sources = s.items.head.sources.map(_.uri).sorted val expectedSources = Vector( @@ -99,44 +106,27 @@ object BuildServerTest extends AbstractServerTest { test("buildTarget/compile") { _ => val buildTarget = buildTargetUri("util", "Compile") - - compile(buildTarget, id = nextId()) - - assert(processing("buildTarget/compile")) + compile(buildTarget) val res = svr.waitFor[BspCompileResult](10.seconds) assert(res.statusCode == StatusCode.Success) } test("buildTarget/compile - reports compilation progress") { _ => val buildTarget = buildTargetUri("runAndTest", "Compile") - compile(buildTarget) // This doesn't always come back in 10s on CI. - assert(svr.waitForString(60.seconds) { s => - s.contains("build/taskStart") && - s.contains(""""message":"Compiling runAndTest"""") - }) - - assert(svr.waitForString(60.seconds) { s => - s.contains("build/taskProgress") && - s.contains(""""message":"Compiling runAndTest (15%)"""") - }) - - assert(svr.waitForString(60.seconds) { s => - s.contains("build/taskProgress") && - 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"""") - }) + assertMessage("build/taskStart", """"message":"Compiling runAndTest"""")(duration = 60.seconds) + assertMessage( + "build/taskProgress", + """"message":"Compiling runAndTest (15%)"""" + )(duration = 60.seconds) + assertMessage( + "build/taskProgress", + """"message":"Compiling runAndTest (100%)"""" + )(duration = 60.seconds) + assertMessage("build/publishDiagnostics", """"diagnostics":[]""")(duration = 60.seconds) + assertMessage("build/taskFinish", """"message":"Compiled runAndTest"""")(duration = 60.seconds) } test( @@ -147,10 +137,7 @@ object BuildServerTest extends AbstractServerTest { compile(buildTarget) - assert(svr.waitForString(30.seconds) { s => - s.contains("build/taskFinish") && - s.contains(""""message":"Compiled diagnostics"""") - }) + assertMessage("build/taskFinish", """"message":"Compiled diagnostics"""")(30.seconds) // introduce compile error IO.write( @@ -160,16 +147,15 @@ object BuildServerTest extends AbstractServerTest { |}""".stripMargin ) - reloadWorkspace() compile(buildTarget) - 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" + assertMessage( + "build/publishDiagnostics", + "Diagnostics.scala", + "\"message\":\"type mismatch" + )( + duration = 30.seconds, + message = "should send publishDiagnostics with type error for Main.scala" ) // fix compilation error @@ -183,22 +169,20 @@ object BuildServerTest extends AbstractServerTest { reloadWorkspace() compile(buildTarget) - assert( - svr.waitForString(30.seconds) { s => - s.contains("build/publishDiagnostics") && - s.contains("Diagnostics.scala") && - s.contains("\"diagnostics\":[]") - }, - "should send publishDiagnostics with empty diagnostics" + assertMessage("build/publishDiagnostics", "Diagnostics.scala", "\"diagnostics\":[]")( + duration = 30.seconds, + message = "should send publishDiagnostics with empty diagnostics" ) + assertMessage("build/taskFinish", "\"noOp\":true")(debug = true) // trigger no-op compilation compile(buildTarget) assert( - !svr.waitForString(20.seconds) { s => - s.contains("build/publishDiagnostics") && - s.contains("Diagnostics.scala") + svr.waitForString(20.seconds) { s => + if (s.contains("build/publishDiagnostics") && s.contains("Diagnostics.scala")) + throw new Exception("shouldn't send publishDiagnostics if noOp compilation") + else s.contains("build/taskFinish") && s.contains("\"noOp\":true") }, "shouldn't send publishDiagnostics if there's no change in diagnostics (were empty, are empty)" ) @@ -210,13 +194,13 @@ object BuildServerTest extends AbstractServerTest { compile(buildTarget) - 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" + assertMessage( + "build/publishDiagnostics", + "PatternMatch.scala", + """"message":"match may not be exhaustive""" + )( + duration = 30.seconds, + message = "should send publishDiagnostics with type error for PatternMatch.scala" ) IO.write( @@ -234,57 +218,123 @@ object BuildServerTest extends AbstractServerTest { reloadWorkspace() compile(buildTarget) - assert( - svr.waitForString(30.seconds) { s => - s.contains("build/publishDiagnostics") && - s.contains("PatternMatch.scala") && - s.contains("\"diagnostics\":[]") - }, - "should send publishDiagnostics with empty diagnostics" + assertMessage("build/publishDiagnostics", "PatternMatch.scala", "\"diagnostics\":[]")( + duration = 30.seconds, + message = "should send publishDiagnostics with empty diagnostics" ) - } test("buildTarget/compile: Java diagnostics") { _ => val buildTarget = buildTargetUri("javaProj", "Compile") compile(buildTarget) + assertMessage( + "build/publishDiagnostics", + "Hello.java", + """"severity":2""", + """missing type arguments for generic class java.util.List""" + )(message = "should send publishDiagnostics with severity 2 for Hello.java") - assert( - svr.waitForString(10.seconds) { s => - s.contains("build/publishDiagnostics") && - s.contains("Hello.java") && - s.contains(""""severity":2""") && - s.contains("""missing type arguments for generic class java.util.List""") - }, - "should send publishDiagnostics with severity 2 for Hello.java" + assertMessage( + "build/publishDiagnostics", + "Hello.java", + """"severity":1""", + """incompatible types: int cannot be converted to java.lang.String""" + )( + message = "should send publishDiagnostics with severity 1 for Hello.java" ) + } - assert( - svr.waitForString(1.seconds) { s => - s.contains("build/publishDiagnostics") && - s.contains("Hello.java") && - s.contains(""""severity":1""") && - s.contains("""incompatible types: int cannot be converted to java.lang.String""") - }, - "should send publishDiagnostics with severity 1 for Hello.java" + test("buildTarget/compile [Java diagnostics] clear stale warnings") { _ => + val buildTarget = buildTargetUri("javaProj", "Compile") + val testFile = new File(svr.baseDirectory, s"java-proj/src/main/java/example/Hello.java") + + val otherBuildFile = new File(svr.baseDirectory, "force-java-out-of-process-compiler.sbt") + // Setting `javaHome` will force sbt to shell out to an external Java compiler instead + // of using the local compilation service offered by the JVM running this SBT instance. + IO.write( + otherBuildFile, + """ + |def jdk: File = sbt.internal.util.Util.javaHome.toFile() + |lazy val javaProj = project + | .in(file("java-proj")) + | .settings( + | javacOptions += "-Xlint:all", + | javaHome := Some(jdk) + | ) + |""".stripMargin + ) + reloadWorkspace() + + compile(buildTarget) + + assertMessage( + "build/publishDiagnostics", + "Hello.java", + """"severity":2""", + """found raw type""" + )(message = "should send publishDiagnostics with severity 2 for Hello.java", debug = false) + + assertMessage( + "build/publishDiagnostics", + "Hello.java", + """"severity":1""", + """incompatible types: int cannot be converted""" + )( + message = "should send publishDiagnostics with severity 1 for Hello.java", + debug = true + ) + // Note the messages changed slightly in both cases. That's interesting… + + IO.write( + testFile, + """|package example; + | + |import java.util.List; + |import java.util.ArrayList; + | + |class Hello { + | public static void main(String[] args) { + | List list = new ArrayList<>(); + | String msg = "42"; + | System.out.println(msg); + | } + |} + |""".stripMargin + ) + + compile(buildTarget) + + /* + assertMessage( + "build/publishDiagnostics", + "Hello.java", + "\"diagnostics\":[]", + "\"reset\":true" + )( + message = "should send publishDiagnostics with empty diagnostics" ) + */ + + IO.delete(otherBuildFile) + reloadWorkspace() + () } - test("buildTarget/scalacOptions") { _ => - val buildTarget = buildTargetUri("util", "Compile") - val badBuildTarget = buildTargetUri("badBuildTarget", "Compile") - val id = nextId() - svr.sendJsonRpc( - s"""{ "jsonrpc": "2.0", "id": "$id", "method": "buildTarget/scalacOptions", "params": { - | "targets": [{ "uri": "$buildTarget" }, { "uri": "$badBuildTarget" }] - |} }""".stripMargin + test("buildTarget/scalacOptions, buildTarget/javacOptions") { _ => + val buildTargets = Seq( + buildTargetUri("util", "Compile"), + buildTargetUri("badBuildTarget", "Compile"), ) - assert(processing("buildTarget/scalacOptions")) - assert(svr.waitForString(10.seconds) { s => - (s contains s""""id":"$id"""") && - (s contains "scala-library-2.13.11.jar") - }) + + val id1 = scalacOptions(buildTargets) + assertMessage(s""""id":"$id1"""", "scala-library-2.13.11.jar")() + + val id2 = javacOptions(buildTargets) + assertMessage(s""""id":"$id2"""", "scala-library-2.13.11.jar")() + + val id3 = scalacOptions(Seq(buildTargetUri("runAndTest", "Compile"))) + assertMessage(s""""id":"$id3"""", "util/target/scala-2.13/classes")() } test("buildTarget/cleanCache") { _ => @@ -306,7 +356,7 @@ object BuildServerTest extends AbstractServerTest { | "targets": [{ "uri": "$buildTarget" }] |} }""".stripMargin ) - assert(processing("buildTarget/cleanCache")) + assertProcessing("buildTarget/cleanCache") val res = svr.waitFor[CleanCacheResult](10.seconds) assert(res.cleaned) assert(targetDir.list().isEmpty) @@ -316,7 +366,7 @@ object BuildServerTest extends AbstractServerTest { svr.sendJsonRpc( s"""{ "jsonrpc": "2.0", "id": "${nextId()}", "method": "workspace/buildTargets", "params": {} }""" ) - assert(processing("workspace/buildTargets")) + assertProcessing("workspace/buildTargets") val result = svr.waitFor[WorkspaceBuildTargetsResult](10.seconds) val allTargets = result.targets.map(_.id.uri) @@ -327,7 +377,7 @@ object BuildServerTest extends AbstractServerTest { | ] |} }""".stripMargin ) - assert(processing("buildTarget/cleanCache")) + assertProcessing("buildTarget/cleanCache") val res = svr.waitFor[CleanCacheResult](10.seconds) assert(res.cleaned) } @@ -337,11 +387,8 @@ object BuildServerTest extends AbstractServerTest { svr.sendJsonRpc( s"""{ "jsonrpc": "2.0", "id": "$id", "method": "workspace/reload"}""" ) - assert(processing("workspace/reload")) - assert(svr.waitForString(10.seconds) { s => - (s contains s""""id":"$id"""") && - (s contains """"result":null""") - }) + assertProcessing("workspace/reload") + assertMessage(s""""id":"$id"""", """"result":null""")() } test("workspace/reload: send diagnostic and respond with error") { _ => @@ -355,25 +402,21 @@ object BuildServerTest extends AbstractServerTest { |) |""".stripMargin ) - val id = nextId() + val id = reloadWorkspace() // reload - reloadWorkspace(id) - assert( - svr.waitForString(10.seconds) { s => - s.contains(s""""buildTarget":{"uri":"$metaBuildTarget"}""") && - s.contains(s""""textDocument":{"uri":"${otherBuildFile.toPath.toUri}"}""") && - s.contains(""""severity":1""") && - s.contains(""""reset":true""") - } - ) - assert( - svr.waitForString(10.seconds) { s => - s.contains(s""""id":"$id"""") && - s.contains(""""error"""") && - s.contains(s""""code":${ErrorCodes.InternalError}""") && - s.contains("Type error in expression") - } - ) + assertMessage( + s""""buildTarget":{"uri":"$metaBuildTarget"}""", + s""""textDocument":{"uri":"${otherBuildFile.toPath.toUri}"}""", + """"severity":1""", + """"reset":true""" + )() + + assertMessage( + s""""id":"$id"""", + """"error"""", + s""""code":${ErrorCodes.InternalError}""", + "Type error in expression" + )() // fix the other-build.sbt file and reload again IO.write( otherBuildFile, @@ -385,14 +428,12 @@ object BuildServerTest extends AbstractServerTest { ) reloadWorkspace() // assert received an empty diagnostic - assert( - svr.waitForString(10.seconds) { s => - s.contains(s""""buildTarget":{"uri":"$metaBuildTarget"}""") && - s.contains(s""""textDocument":{"uri":"${otherBuildFile.toPath.toUri}"}""") && - s.contains(""""diagnostics":[]""") && - s.contains(""""reset":true""") - } - ) + assertMessage( + s""""buildTarget":{"uri":"$metaBuildTarget"}""", + s""""textDocument":{"uri":"${otherBuildFile.toPath.toUri}"}""", + """"diagnostics":[]""", + """"reset":true""" + )() IO.delete(otherBuildFile) } @@ -405,11 +446,8 @@ object BuildServerTest extends AbstractServerTest { | "targets": [{ "uri": "$buildTarget" }, { "uri": "$badBuildTarget" }] |} }""".stripMargin ) - assert(processing("buildTarget/scalaMainClasses")) - assert(svr.waitForString(30.seconds) { s => - (s contains s""""id":"$id"""") && - (s contains """"class":"main.Main"""") - }) + assertProcessing("buildTarget/scalaMainClasses") + assertMessage(s""""id":"$id"""", """"class":"main.Main"""")(duration = 30.seconds) } test("buildTarget/run") { _ => @@ -422,15 +460,9 @@ object BuildServerTest extends AbstractServerTest { | "data": { "class": "main.Main" } |} }""".stripMargin ) - assert(processing("buildTarget/run")) - assert(svr.waitForString(10.seconds) { s => - (s contains "build/logMessage") && - (s contains """"message":"Hello World!"""") - }) - assert(svr.waitForString(10.seconds) { s => - (s contains s""""id":"$id"""") && - (s contains """"statusCode":1""") - }) + assertProcessing("buildTarget/run") + assertMessage("build/logMessage", """"message":"Hello World!"""")() + assertMessage(s""""id":"$id"""", """"statusCode":1""")() } test("buildTarget/jvmRunEnvironment") { _ => @@ -443,16 +475,14 @@ object BuildServerTest extends AbstractServerTest { | "params": { "targets": [{ "uri": "$buildTarget" }] } |}""".stripMargin ) - assert(processing("buildTarget/jvmRunEnvironment")) - assert { - svr.waitForString(10.seconds) { s => - (s contains s""""id":"$id"""") && - (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 - } - } + assertProcessing("buildTarget/jvmRunEnvironment") + assertMessage( + s""""id":"$id"""", + "jsoniter-scala-core_2.13-2.13.11.jar", // compile dependency + "\"jvmOptions\":[\"Xmx256M\"]", + "\"environmentVariables\":{\"KEY\":\"VALUE\"}", + "/buildserver/run-and-test/" // working directory + )() } test("buildTarget/jvmTestEnvironment") { _ => @@ -465,17 +495,14 @@ object BuildServerTest extends AbstractServerTest { | "params": { "targets": [{ "uri": "$buildTarget" }] } |}""".stripMargin ) - assert(processing("buildTarget/jvmTestEnvironment")) - assert { - svr.waitForString(10.seconds) { s => - (s contains s""""id":"$id"""") && - // 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\"}") - } - } + assertProcessing("buildTarget/jvmTestEnvironment") + assertMessage( + s""""id":"$id"""", + "jsoniter-scala-core_2.13-2.13.11.jar", // compile dependency + "scalatest_2.13-3.0.8.jar", // test dependency + "\"jvmOptions\":[\"Xmx512M\"]", + "\"environmentVariables\":{\"KEY_TEST\":\"VALUE_TEST\"}" + )() } test("buildTarget/scalaTestClasses") { _ => @@ -487,13 +514,13 @@ object BuildServerTest extends AbstractServerTest { | "targets": [{ "uri": "$buildTarget" }, { "uri": "$badBuildTarget" }] |} }""".stripMargin ) - assert(processing("buildTarget/scalaTestClasses")) - assert(svr.waitForString(10.seconds) { s => - (s contains s""""id":"$id"""") && - (s contains """"tests.FailingTest"""") && - (s contains """"tests.PassingTest"""") && - (s contains """"framework":"ScalaTest"""") - }) + assertProcessing("buildTarget/scalaTestClasses") + assertMessage( + s""""id":"$id"""", + """"tests.FailingTest"""", + """"tests.PassingTest"""", + """"framework":"ScalaTest"""" + )() } test("buildTarget/test: run all tests") { _ => @@ -504,11 +531,8 @@ object BuildServerTest extends AbstractServerTest { | "targets": [{ "uri": "$buildTarget" }] |} }""".stripMargin ) - assert(processing("buildTarget/test")) - assert(svr.waitForString(10.seconds) { s => - (s contains s""""id":"$id"""") && - (s contains """"statusCode":2""") - }) + assertProcessing("buildTarget/test") + assertMessage(s""""id":"$id"""", """"statusCode":2""")() } test("buildTarget/test: run one test class") { _ => @@ -528,43 +552,39 @@ object BuildServerTest extends AbstractServerTest { | } |} }""".stripMargin ) - assert(processing("buildTarget/test")) - assert(svr.waitForString(10.seconds) { s => - (s contains s""""id":"$id"""") && - (s contains """"statusCode":1""") - }) + assertProcessing("buildTarget/test") + assertMessage(s""""id":"$id"""", """"statusCode":1""")() } test("buildTarget/compile: report error") { _ => val buildTarget = buildTargetUri("reportError", "Compile") compile(buildTarget) - assert(svr.waitForString(10.seconds) { s => - (s contains s""""buildTarget":{"uri":"$buildTarget"}""") && - (s contains """"severity":1""") && - (s contains """"reset":true""") - }) + assertMessage( + s""""buildTarget":{"uri":"$buildTarget"}""", + """"severity":1""", + """"reset":true""" + )() } test("buildTarget/compile: report warning") { _ => val buildTarget = buildTargetUri("reportWarning", "Compile") compile(buildTarget) - assert(svr.waitForString(10.seconds) { s => - (s contains s""""buildTarget":{"uri":"$buildTarget"}""") && - (s contains """"severity":2""") && - (s contains """"reset":true""") - }) + assertMessage( + s""""buildTarget":{"uri":"$buildTarget"}""", + """"severity":2""", + """"reset":true""" + )() } test("buildTarget/compile: respond error") { _ => val buildTarget = buildTargetUri("respondError", "Compile") - val id = nextId() - compile(buildTarget, id) - assert(svr.waitForString(10.seconds) { s => - s.contains(s""""id":"$id"""") && - s.contains(""""error"""") && - s.contains(s""""code":${ErrorCodes.InternalError}""") && - s.contains("custom message") - }) + val id = compile(buildTarget) + assertMessage( + s""""id":"$id"""", + """"error"""", + s""""code":${ErrorCodes.InternalError}""", + "custom message" + )() } test("buildTarget/resources") { _ => @@ -576,10 +596,8 @@ object BuildServerTest extends AbstractServerTest { | "targets": [{ "uri": "$buildTarget" }, { "uri": "$badBuildTarget" }] |} }""".stripMargin ) - assert(processing("buildTarget/resources")) - assert(svr.waitForString(10.seconds) { s => - (s contains s""""id":"$id"""") && (s contains "util/src/main/resources/") - }) + assertProcessing("buildTarget/resources") + assertMessage(s""""id":"$id"""", "util/src/main/resources/")() } test("buildTarget/outputPaths") { _ => @@ -590,7 +608,7 @@ object BuildServerTest extends AbstractServerTest { | "targets": [{ "uri": "$buildTarget" }, { "uri": "$badBuildTarget" }] |} }""".stripMargin ) - assert(processing("buildTarget/outputPaths")) + assertProcessing("buildTarget/outputPaths") val actualResult = svr.waitFor[OutputPathsResult](10.seconds) val expectedResult = OutputPathsResult( items = Vector( @@ -608,7 +626,48 @@ object BuildServerTest extends AbstractServerTest { assert(actualResult == expectedResult) } - private def initializeRequest(id: Int): Unit = { + test("buildTarget/compile: twirl diagnostics (sourcePositionMappers)") { _ => + val buildTarget = buildTargetUri("twirlProj", "Compile") + val testFile = new File(svr.baseDirectory, s"twirlProj/src/main/twirl/main.scala.html") + + compile(buildTarget) + assertMessage( + "build/publishDiagnostics", + "main.scala.html", + """"severity":1""", + "not found: value tilte" + )(message = "should report diagnostic in Twirl file") + IO.write( + testFile, + """|@(title: String, paragraphs: Seq[String]) + | + | + | + | + | @title + | + | + |

@title

+ | @for(paragraph <- paragraphs) { + |

@paragraph

+ | } + | + | + |""".stripMargin + ) + compile(buildTarget) + assertMessage( + "build/publishDiagnostics", + "main.scala.html", + """"diagnostics":[]""", + """"reset":true""" + )( + duration = 30.seconds, + message = "should reset diagnostic in Twirl file" + ) + } + + private def initializeRequest(): Int = { val params = InitializeBuildParams( "test client", "1.0.0", @@ -617,35 +676,67 @@ object BuildServerTest extends AbstractServerTest { BuildClientCapabilities(Vector("scala")), None ) - svr.sendJsonRpc(request(id, "build/initialize", params)) + sendRequest("build/initialize", params) } - private def processing(method: String, debug: Boolean = false): Boolean = { - svr.waitForString(10.seconds) { msg => - if (debug) println(msg) - msg.contains("build/logMessage") && - msg.contains(s""""message":"Processing $method"""") - } + private def assertProcessing(method: String, debug: Boolean = false): Unit = + assertMessage("build/logMessage", s""""message":"Processing $method"""")(debug = debug) + + def assertMessage( + parts: String* + )(duration: FiniteDuration = 10.seconds, debug: Boolean = false, message: String = ""): Unit = { + def assertion = + svr.waitForString(duration) { msg => + if (debug) println(msg) + if (debug) + parts.foreach { p => + if (msg.contains(p)) println(s"> $msg contains $p") + else () + } + parts.forall(msg.contains) + } + if (message.nonEmpty) assert.apply(assertion, message) else assert(assertion) } - private def reloadWorkspace(id: Int = nextId()): Unit = - svr.sendJsonRpc(s"""{ "jsonrpc": "2.0", "id": "$id", "method": "workspace/reload"}""") + private def reloadWorkspace(): Int = + sendRequest("workspace/reload") - private def compile(buildTarget: URI, id: Int = nextId()): Unit = { + private def compile(buildTarget: URI): Int = { val params = CompileParams(targets = Vector(BuildTargetIdentifier(buildTarget)), None, Vector.empty) - svr.sendJsonRpc(request(id, "buildTarget/compile", params)) + sendRequest("buildTarget/compile", params) + } + + private def scalacOptions(buildTargets: Seq[URI]): Int = { + val targets = buildTargets.map(BuildTargetIdentifier.apply).toVector + sendRequest("buildTarget/scalacOptions", ScalacOptionsParams(targets)) + } + + private def javacOptions(buildTargets: Seq[URI]): Int = { + val targets = buildTargets.map(BuildTargetIdentifier.apply).toVector + sendRequest("buildTarget/scalacOptions", ScalacOptionsParams(targets)) } - private def buildTargetSources(buildTargets: Seq[URI], id: Int = nextId()): String = { + private def buildTargetSources(buildTargets: Seq[URI]): Int = { val targets = buildTargets.map(BuildTargetIdentifier.apply).toVector - request(id, "buildTarget/sources", SourcesParams(targets)) + sendRequest("buildTarget/sources", SourcesParams(targets)) + } + + private def sendRequest(method: String): Int = { + val id = nextId() + val msg = JsonRpcRequestMessage("2.0", id.toString, method, None) + val json = Converter.toJson(msg).get + svr.sendJsonRpc(CompactPrinter(json)) + id } - private def 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 sendRequest[T: JsonWriter](method: String, params: T): Int = { + val id = nextId() + val msg = JsonRpcRequestMessage("2.0", id.toString, method, Converter.toJson(params).get) + val json = Converter.toJson(msg).get + svr.sendJsonRpc(CompactPrinter(json)) + if (method != "build/initialize") assertProcessing(method) + id } private def buildTargetUri(project: String, config: String): URI = diff --git a/server-test/src/test/scala/testpkg/ClientTest.scala b/server-test/src/test/scala/testpkg/ClientTest.scala index b4352062a0..628cbbda3b 100644 --- a/server-test/src/test/scala/testpkg/ClientTest.scala +++ b/server-test/src/test/scala/testpkg/ClientTest.scala @@ -57,7 +57,7 @@ object ClientTest extends AbstractServerTest { case r => r } } - private def client(args: String*): Int = { + private def client(args: String*): Int = background( NetworkClient.client( testPath.toFile, @@ -68,6 +68,19 @@ object ClientTest extends AbstractServerTest { false ) ) + def clientWithStdoutLines(args: String*): (Int, Seq[String]) = { + val out = new CachingPrintStream + val exitCode = background( + NetworkClient.client( + testPath.toFile, + args.toArray, + NullInputStream, + out, + NullPrintStream, + false + ) + ) + (exitCode, out.lines) } // This ensures that the completion command will send a tab that triggers // sbt to call definedTestNames or discoveredMainClasses if there hasn't diff --git a/server-test/src/test/scala/testpkg/TestServer.scala b/server-test/src/test/scala/testpkg/TestServer.scala index dac89ff79e..c3a78bc6ad 100644 --- a/server-test/src/test/scala/testpkg/TestServer.scala +++ b/server-test/src/test/scala/testpkg/TestServer.scala @@ -220,7 +220,7 @@ case class TestServer( // initiate handshake sendJsonRpc( - s"""{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "initializationOptions": { "skipAnalysis": true } } }""" + s"""{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "initializationOptions": { "skipAnalysis": true, "canWork": true } } }""" ) def test(f: TestServer => Future[Assertion]): Future[Assertion] = { diff --git a/testing/src/main/contraband-scala/sbt/protocol/testing/codec/TestItemEventFormats.scala b/testing/src/main/contraband-scala/sbt/protocol/testing/codec/TestItemEventFormats.scala index d6a8af553f..352ed5d8e9 100644 --- a/testing/src/main/contraband-scala/sbt/protocol/testing/codec/TestItemEventFormats.scala +++ b/testing/src/main/contraband-scala/sbt/protocol/testing/codec/TestItemEventFormats.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.protocol.testing.codec import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait TestItemEventFormats { self: sbt.protocol.testing.codec.TestResultFormats with sbt.protocol.testing.codec.TestItemDetailFormats with sjsonnew.BasicJsonProtocol => +trait TestItemEventFormats { self: sbt.protocol.testing.codec.TestResultFormats with sjsonnew.BasicJsonProtocol with sbt.protocol.testing.codec.TestItemDetailFormats with sbt.internal.testing.StatusFormats => implicit lazy val TestItemEventFormat: JsonFormat[sbt.protocol.testing.TestItemEvent] = new JsonFormat[sbt.protocol.testing.TestItemEvent] { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.testing.TestItemEvent = { __jsOpt match { diff --git a/testing/src/main/contraband-scala/sbt/protocol/testing/codec/TestMessageFormats.scala b/testing/src/main/contraband-scala/sbt/protocol/testing/codec/TestMessageFormats.scala index 13534855d6..ffeba09907 100644 --- a/testing/src/main/contraband-scala/sbt/protocol/testing/codec/TestMessageFormats.scala +++ b/testing/src/main/contraband-scala/sbt/protocol/testing/codec/TestMessageFormats.scala @@ -6,6 +6,6 @@ package sbt.protocol.testing.codec import _root_.sjsonnew.JsonFormat -trait TestMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.testing.codec.TestStringEventFormats with sbt.protocol.testing.codec.TestInitEventFormats with sbt.protocol.testing.codec.TestResultFormats with sbt.protocol.testing.codec.TestCompleteEventFormats with sbt.protocol.testing.codec.StartTestGroupEventFormats with sbt.protocol.testing.codec.EndTestGroupEventFormats with sbt.protocol.testing.codec.EndTestGroupErrorEventFormats with sbt.protocol.testing.codec.TestItemDetailFormats with sbt.protocol.testing.codec.TestItemEventFormats => +trait TestMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.testing.codec.TestStringEventFormats with sbt.protocol.testing.codec.TestInitEventFormats with sbt.protocol.testing.codec.TestResultFormats with sbt.protocol.testing.codec.TestCompleteEventFormats with sbt.protocol.testing.codec.StartTestGroupEventFormats with sbt.protocol.testing.codec.EndTestGroupEventFormats with sbt.protocol.testing.codec.EndTestGroupErrorEventFormats with sbt.protocol.testing.codec.TestItemDetailFormats with sbt.internal.testing.StatusFormats with sbt.protocol.testing.codec.TestItemEventFormats => implicit lazy val TestMessageFormat: JsonFormat[sbt.protocol.testing.TestMessage] = flatUnionFormat7[sbt.protocol.testing.TestMessage, sbt.protocol.testing.TestStringEvent, sbt.protocol.testing.TestInitEvent, sbt.protocol.testing.TestCompleteEvent, sbt.protocol.testing.StartTestGroupEvent, sbt.protocol.testing.EndTestGroupEvent, sbt.protocol.testing.EndTestGroupErrorEvent, sbt.protocol.testing.TestItemEvent]("type") } diff --git a/zinc-lm-integration/src/main/scala/sbt/internal/inc/ZincComponentCompiler.scala b/zinc-lm-integration/src/main/scala/sbt/internal/inc/ZincComponentCompiler.scala index 20a6492755..05a9ea6f12 100644 --- a/zinc-lm-integration/src/main/scala/sbt/internal/inc/ZincComponentCompiler.scala +++ b/zinc-lm-integration/src/main/scala/sbt/internal/inc/ZincComponentCompiler.scala @@ -57,7 +57,11 @@ private[sbt] object ZincComponentCompiler { scalaInstance: XScalaInstance, logger: Logger, ): File = lock.synchronized { - val raw = new RawCompiler(scalaInstance, ClasspathOptionsUtil.auto, logger) + val raw = new RawCompiler( + scalaInstance, + ClasspathOptionsUtil.autoNoboot(scalaInstance.version), + logger + ) val zinc = new ZincComponentCompiler(raw, manager, dependencyResolution, bridgeSources, logger) logger.debug(f0(s"Getting $bridgeSources for Scala ${scalaInstance.version}")) diff --git a/zinc-lm-integration/src/test/scala/sbt/internal/inc/ZincComponentCompilerSpec.scala b/zinc-lm-integration/src/test/scala/sbt/internal/inc/ZincComponentCompilerSpec.scala index bad241e515..7994581679 100644 --- a/zinc-lm-integration/src/test/scala/sbt/internal/inc/ZincComponentCompilerSpec.scala +++ b/zinc-lm-integration/src/test/scala/sbt/internal/inc/ZincComponentCompilerSpec.scala @@ -16,11 +16,8 @@ class ZincComponentCompilerSpec extends IvyBridgeProviderSpecification { val scala2106 = "2.10.6" val scala2118 = "2.11.8" val scala21111 = "2.11.11" - val scala2121 = "2.12.1" - val scala2122 = "2.12.2" - val scala2123 = "2.12.3" - val scala2130 = "2.13.0" - val scala2131 = "2.13.1" + val scala21220 = "2.12.20" + val scala21311 = "2.13.11" def isJava8: Boolean = sys.props("java.specification.version") == "1.8" @@ -40,14 +37,11 @@ class ZincComponentCompilerSpec extends IvyBridgeProviderSpecification { } else () } - it should "compile the bridge for Scala 2.12.2" in { implicit td => - IO.withTemporaryDirectory(t => getCompilerBridge(t, logger, scala2121) should exist) - IO.withTemporaryDirectory(t => getCompilerBridge(t, logger, scala2122) should exist) - IO.withTemporaryDirectory(t => getCompilerBridge(t, logger, scala2123) should exist) + it should "compile the bridge for Scala 2.12.20" in { implicit td => + IO.withTemporaryDirectory(t => getCompilerBridge(t, logger, scala21220) should exist) } - it should "compile the bridge for Scala 2.13" in { implicit td => - IO.withTemporaryDirectory(t => getCompilerBridge(t, logger, scala2130) should exist) - IO.withTemporaryDirectory(t => getCompilerBridge(t, logger, scala2131) should exist) + it should "compile the bridge for Scala 2.13.11" in { implicit td => + IO.withTemporaryDirectory(t => getCompilerBridge(t, logger, scala21311) should exist) } }